I'm trying to programmatically (not using XML files) create custom subviews in Android (that's what I call it in iOS) that is a basically a number of basic views (labels, buttons, text fields etc) put together into a reusable subview class so I can use it inside my UIViewControllers
or Activity
in Android.
I don't know what is the correct terminology in Android. There seems to be a million different terminologies.
Custom View, ViewGroups, Layouts, Widgets, Components, whatever you want to call it.
In iOS this is simply done like this:
CustomView.h
@interface CustomView : UIView
@property (nonatomic, strong) UILabel *message;
@property (nonatomic, strong) UIButton *button;
@end
CustomView.m
@implementation CustomView
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self)
{
[self initViews];
[self initConstraints];
}
return self;
}
-(void)initViews
{
self.message = [[UILabel alloc] init];
self.button = [[UIButton alloc] init];
[self addSubview:self.message];
[self addSubview:self.button];
}
-(void)initConstraints
{
id views = @{
@"message": self.message,
@"button": self.button
};
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[message]|" options:0 metrics:nil views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[button]|" options:0 metrics:nil views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[message][button]|" options:0 metrics:nil views:views]];
}
@end
Now I can reuse this custom view in any ViewController (Android Activity
) I chose.
How does one achieve something like that in Android?
I've been looking around and from what I gather in Android, to add subviews, I add them to Layouts
:
RelativeLayout relativeLayout = new RelativeLayout(...);
TextView textView = new TextView(...);
relativeLayout.addSubview(textView);
Does that mean I need extend RelativeLayout
or ViewGroup
?
Looking at this page: http://developer.android.com/reference/android/view/ViewGroup.html
It seems like we need to write some really complicated logic to layout the custom view such as:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
// These keep track of the space we are using on the left and right for
// views positioned there; we need member variables so we can also use
// these for layout later.
mLeftWidth = 0;
mRightWidth = 0;
// Measurement will ultimately be computing these values.
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
// Iterate through all children, measuring them and computing our dimensions
// from their size.
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
// Measure the child.
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
// Update our size information based on the layout params. Children
// that asked to be positioned on the left or right go in those gutters.
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.position == LayoutParams.POSITION_LEFT) {
mLeftWidth += Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
} else if (lp.position == LayoutParams.POSITION_RIGHT) {
mRightWidth += Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
} else {
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
}
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
}
}
// Total width is the maximum width of all inner children plus the gutters.
maxWidth += mLeftWidth + mRightWidth;
// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// Report our final dimensions.
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
}
All I'm trying to do is use multiple basic android labels, views, buttons in a custom view like the iOS example above, why is it so hard in Android ?
I was hoping for something simple like this:
public class CustomView extends View
{
public RelativeLayout mainLayout;
public TextView message;
public Button button;
// default constructor
public CustomView()
{
...
initViews();
initLayouts();
addViews();
}
public initViews()
{
mainLayout = new RelativeLayout(this);
message = new TextView(this);
button = new Button(this);
...
}
public initLayouts()
{
// --------------------------------------------------
// use Android layout params to position subviews
// within this custom view class
// --------------------------------------------------
}
public addViews()
{
mainLayout.addView(message);
mainLayout.addView(button);
setContentView(mainLayout);
}
}
I'm sorry I am sincerely trying to learn and build a basic Android application and not trying to bash Android's way of doing things.
I know how to add and layout subviews inside an Activity and have been doing so for the past two days but not inside a custom View/View Group/Layout. I don't want to end up constructing the exact same subview for each of my Activity in the Android app, that just goes against good coding practice right ? :D
Just need a bit of guidance here from others who have done both iOS and Android development.
Edit
It seems like what I'm looking for is called a Compound Control: http://developer.android.com/guide/topics/ui/custom-components.html
I'll keep digging and hopefully achieve the result I'm after :D
Just need to work out this Inflater business.