How to make an UI component from several UI compontents - android

Friends,
I created an UI component "compTV" that extends Textview. It works very well.
Now i want to create an UI compoentent "3compTV" that just consists out of 3 "compTV" ´s next to each other.
The code, creating a LinearLayout and add 3 "compTV" ´s works very well if i just extend Activity.
But how to create a Component out of this?
What class do i have to extend for the "3compTV" component and what else would be necessary.
When i extend compTV only one object will be drawn. So i guess i have to extend a different class or take some other approach to this problem.
Thanks for your support
public class 3compTV extends compTV{
Context ctx;
int layoutMaringLeft = 100;
int layoutMaringRight = 0;
int layoutMaringTop = 0;
int layoutMaringBottom = 0;
int amountOfComponents = 5;
public components(Context context) {
super(context);
ctx = context;
Log.d(ctx.getString(R.string.app_name), "components, Constructor1");
compTV comp1 = new compTV(ctx);
compTV comp2 = new compTV(ctx);
compTV comp3 = new compTV(ctx);
comp2.setLetter("A");
comp2.setState("grey");
comp3.setLetter("A");
comp3.setState("grey");
LinearLayout LL2 = new LinearLayout(ctx);
LL2.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(layoutMaringLeft, layoutMaringTop,
layoutMaringRight, layoutMaringBottom);
LL2.addView(comp1, layoutParams);
comp1.setLetter("H");
comp1.setState("green");
LL2.addView(comp2, layoutParams);
LL2.addView(comp3, layoutParams);
}
public components(Context context, AttributeSet attrs) {
super(context, attrs);
ctx = context;
Log.d(ctx.getString(R.string.app_name), "components, Constructor2");
}
public components(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
ctx = context;
Log.d(ctx.getString(R.string.app_name), "components, Constructor3");
}
}

Make a view that extends LinearLayout and contains 3 instances of compTV.
public class 3CompTV extends LinearLayout {
public 3CompTV(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(LinearLayout.VERTICAL);
for (int i = 0; i < 3; i++) {
addView(new CompTV(context));
}
}
}
My personal preference would be to put the 3 CompTV views in an XML layout, with their parent element being <merge>. This allows you to specify their attributes like wrap_content in XML, which I find much cleaner. You add them to your custom view like this:
public class 3CompTV extends LinearLayout {
public 3CompTV(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(LinearLayout.VERTICAL);
View.inflate(context, R.id.three_comp_tvs, this);
}
}

Related

Custom View - Move Views into nested Layout

I want to create a custom View, which shows a Card with follwing Contents:
TextView (Caption)
TextView (Description)
LinearLayout (innerLayout)
So i just extended a LinearLayout and inflated my Layout file with it:
public class FrageContainerView extends LinearLayout {
private TextView objTextViewCaption;
private TextView objTextViewDescription;
private String caption;
private String description;
private LinearLayout objLayoutInner;
public FrageContainerView(Context context) {
this(context, null);
}
public FrageContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context, attrs);
}
public FrageContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context, attrs);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public FrageContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize(context, attrs);
}
private void initialize(Context context, AttributeSet attrs) {
TypedArray a =
context.obtainStyledAttributes(attrs, R.styleable.options_frageContainerView, 0, 0);
caption = a.getString(R.styleable.options_frageContainerView_caption);
description = a.getString(R.styleable.options_frageContainerView_description);
a.recycle();
setOrientation(LinearLayout.HORIZONTAL);
setGravity(Gravity.CENTER_VERTICAL);
LayoutInflater inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.view_fragecontainer, this, true);
objLayoutInner = (LinearLayout) findViewById(R.id.linearlayout_inner);
objTextViewCaption = (TextView) findViewById(R.id.textview_caption);
objTextViewDescription = (TextView) findViewById(R.id.textview_description);
objTextViewCaption.setText(caption);
objTextViewDescription.setText(description);
}
A user which uses my custom View should be able to add his own Components preferably inside the XML like this:
<FrageContainerView
android:layout_width="match_parent"
android:layout_height="match_parent"
custom:caption="Hallo"
custom:description="LOLOLOL"
android:background="#FF00FF00">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="sdsdfsdf"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="sdsdfsdf"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="sdsdfsdf"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="sdsdfsdf"/>
</FrageContainerView>
The current State is, that the defined EditText's are inflated inside my custom View. I want the EditTexts in my Example to be added to the InnerLayout instead to append them to Custom View.
What's the best approach to do this?
The essence of this problem is how to add child Views to a GroupView that is itself a child of the custom layout.
This is relatively straightforward programmatically, but more of an issue in XML.
Androids LayoutInflater logically interprets the nested levels in an XML file and builds the same structure in the hierarchy of Views it creates.
Your example XML defines 4 EditText Views as first tier children of FrageContainerView, but you want them to be created as second tier children of FrageContainerView sitting inside your LinearLayout. This would mean changing Androids LayoutInflater which is a core component of the whole Android system.
To do this programmatically you could do something like the following:
public class FrageContainerView extends LinearLayout {
private TextView objTextViewCaption;
private TextView objTextViewDescription;
private String caption;
private String description;
private LinearLayout objLayoutInner;
public FrageContainerView(Context context) {
this(context, null);
}
public FrageContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context, attrs);
}
public FrageContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context, attrs);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public FrageContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize(context, attrs);
}
private void initialize(Context context, AttributeSet attrs) {
// Create your 3 predefined first tier children
// Create the Caption View
objTextViewCaption = new TextView(context);
// You can add your new Views to this LinearLayout
this.addView(objTextViewCaption)
// Create the Description View
objTextViewDescription = new TextView(context);
// You can also provide LayoutParams when you add any of your new Views if you want to
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
this.addView(objTextViewDescription, params);
// Create your inner LinearLayout
objLayoutInner = new LinearLayout(context);
objLayoutInner.setOrientation(VERTICAL);
// Add it
this.addView(objLayoutInner);
TypedArray a =
context.obtainStyledAttributes(attrs, R.styleable.options_frageContainerView, 0, 0);
caption = a.getString(R.styleable.options_frageContainerView_caption);
description = a.getString(R.styleable.options_frageContainerView_description);
a.recycle();
setOrientation(LinearLayout.HORIZONTAL);
setGravity(Gravity.CENTER_VERTICAL);
/**
* Oops! Only just spotted you're inflating your three predefined views
* here. It's fine to do this instead of programmatically adding them as I
* have above. Obviously they should only be added once, so I've commented out
* your version for the moment.
**/
// LayoutInflater inflater =
// (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// inflater.inflate(R.layout.view_fragecontainer, this, true);
//
// objLayoutInner = (LinearLayout) findViewById(R.id.linearlayout_inner);
// objTextViewCaption = (TextView) findViewById(R.id.textview_caption);
// objTextViewDescription = (TextView) findViewById(R.id.textview_description);
objTextViewCaption.setText(caption);
objTextViewDescription.setText(description);
}
}
/** Public method for adding new views to the inner LinearLayout **/
public void addInnerView(View view) {
objLayoutInner.addView(view);
}
/** Public method for adding new views to the inner LinearLayout with LayoutParams **/
public void addInnerView(View view, LayoutParams params) {
objLayoutInner.addView(view, params);
}
You would use this in your code with something like the following:
FrageContainerView fragContainerView = (FrageContainerView) findViewById(R.id.my_frag_container_view);
TextView newView = new TextView(context);
newView.setText("whatever");
fragContainerView.addInnerView(newView);
The other thing you could do is
1) Cache all current children and remove them all
ArrayList<View> nestedViews = ViewUtil.getAllChildren(this); removeAllViews();
2) Inflate the layout you have which already contains things
View myLayout = LayoutInflater.from(getContext()).inflate(R.layout.my_layout_with_stuff, null);
3) Add the cached views to the newly inflated layout and add it to the root back
for (View view : nestedViews) {
myLayout.<ViewGroup>findViewById(R.id.contentLayout).addView(view);
}
addView(myLayout);

findViewById returns null in custom View after inflate

I have a custom RelativeLayout and I inflate an xml res file in it.
This works fine if I use the custom layout in an xml file and set it as contentview, but if I try to add it in the code with new LocationItem(this) and addChild() the findViewById method always returns null in the constructor of the custom RelativeLayout.
Here is the code:
public class LocationItem extends RelativeLayout {
private String parcelType;
private int countIntoBox, countFromBox;
private RelativeLayout deliveryContainer, pickupContainer;
private TextView countPickup, countDelivery;
public LocationItem(Context context) {
this(context, null);
}
public LocationItem(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LocationItem(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
inflate(getContext(), R.layout.list_item_location, this);
deliveryContainer = (RelativeLayout) findViewById(R.id.rl_location_delivery_container);
pickupContainer = (RelativeLayout) findViewById(R.id.rl_location_pickup_container);
countPickup = (TextView) findViewById(R.id.tv_location_pickup_count);
countDelivery = (TextView) findViewById(R.id.tv_location_delivery_count);
countPickup.setOnClickListener(getShowNumberPickerListener());
countDelivery.setOnClickListener(getShowNumberPickerListener());
}
private OnClickListener getShowNumberPickerListener() {
return new OnClickListener() {
#Override
public void onClick(View view) {
showNumberPickerDialog(view);
}
};
} ...
}
Add custom view in activity
mRootLayoutLocations.addView(new LocationItem(this));
The view is inflated correctly, because I can see it, but when I try to access a view inside the custom view the app crashes with a NullPointerException.
Ok i inflated the view into a View(holder)
View v = inflate(getContext(), R.layout.list_item_location, this);
and then access the views via v.findViewById. Now it's working.
Code:
View v = inflate(getContext(), R.layout.list_item_location, this);
deliveryContainer = (RelativeLayout) v.findViewById(R.id.rl_location_delivery_container);
pickupContainer = (RelativeLayout) v.findViewById(R.id.rl_location_pickup_container);
countPickup = (TextView) v.findViewById(R.id.tv_location_pickup_count);
countDelivery = (TextView) v.findViewById(R.id.tv_location_delivery_count);
countPickup.setOnClickListener(getShowNumberPickerListener());
countDelivery.setOnClickListener(getShowNumberPickerListener());
You need to be using appropriate constructors, not overloading them.
public class LocationItem extends RelativeLayout {
private String parcelType;
private int countIntoBox, countFromBox;
private RelativeLayout deliveryContainer, pickupContainer;
private TextView countPickup, countDelivery;
public LocationItem(Context context) {
super(context);
init(context, null, 0);
}
public LocationItem(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public LocationItem(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
inflate(getContext(), R.layout.list_item_location, this);
deliveryContainer = (RelativeLayout) findViewById(R.id.rl_location_delivery_container);
pickupContainer = (RelativeLayout) findViewById(R.id.rl_location_pickup_container);
countPickup = (TextView) findViewById(R.id.tv_location_pickup_count);
countDelivery = (TextView) findViewById(R.id.tv_location_delivery_count);
countPickup.setOnClickListener(getShowNumberPickerListener());
countDelivery.setOnClickListener(getShowNumberPickerListener());
}
private OnClickListener getShowNumberPickerListener() {
return new OnClickListener() {
#Override
public void onClick(View view) {
showNumberPickerDialog(view);
}
};
}
...
}
I fear you must inflate the view instead of finding it from nowhere. The method
findindViewById(int Id)
must be called within the Actvity's onCreate or with a view within which the child view/widget you are trying to find is resides in.
If all of the child views resides in a single xml file(within single parent root)
View rootView=(View) LayoutInflater.from(context).inflate(R.layout.list_item_location);
pickupContainer = (RelativeLayout) rootview.findViewById(R.id.rl_location_pickup_container);
you can try this. init views in the method onFinishInflate().This method will be called called as the last phase of inflation, after all child views have been added.So you can avoid NPE.
this should work
public LocationItem(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this = inflate(getContext(), R.layout.list_item_location,null);
...

How to set Margins in class inherited from RelativeLayout

How can I set margins in class extends from RelativeLayout?
I have tried this but it doesn't work:
public class MyRelativeLayout extends RelativeLayout {
int margin = 50;
public MyRelativeLayout(Context context) {
super(context);
setLook();
}
public MyRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setLook();
}
public MyRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setLook();
}
private void setLook() {
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.setMargins(margin, margin, margin, margin);
setLayoutParams(params);
}
}
How should I do this?
Update:
Usage of this view:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<info.korzeniowski.widget.MyRelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center" >
//content
</info.korzeniowski.widget.MyRelativeLayout>
</ScrollView>
A custom View should NEVER define its own margins. Margins are purely used for layouting and you cannot reliably use them to design your custom View.
You can essentially replicate the effect margins have without any of the problems that come with margins by using paddings and a child View:
public class MyRelativeLayout extends RelativeLayout {
private final int padding;
public MyRelativeLayout(Context context) {
super(context);
// Use 50 dip instead of 50 pixels
this.padding = LayoutHelper.dpToPixel(context, 50);
setLook();
}
public MyRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// Use 50 dip instead of 50 pixels
this.padding = LayoutHelper.dpToPixel(context, 50);
setLook();
}
public MyRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Use 50 dip instead of 50 pixels
this.padding = LayoutHelper.dpToPixel(context, 50);
setLook();
}
private void setLook() {
setPadding(this.padding, this.padding, this.padding, this.padding);
final View innerView = ...;
final LayoutParams innerViewParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
addView(innerView, innerViewParams);
}
}
The View called innerView should contain all the content you want to display in your custom View.

How can I inflate a layout with new instances of the another layout?

I would like to inflate a LinearLayout with multiple instances of another LinearLayout. How can I do that? My problem is that I seem to always use the same instance and hence add that instance over and over again.
In short: What I need is a way to add new instances of a LinearLayout child to another LinearLayout parent.
Here is what I have done so far:
private void setupContainers() {
LayoutInflater layoutInflater = (LayoutInflater)this.getSystemService(MainActivity.LAYOUT_INFLATER_SERVICE);
LinearLayout parentContainer = (LinearLayout)this.findViewById(R.id.parent_container);
for (int i = 0; i < someNumber; i++) {
LinearLayout childContainer = (LinearLayout) layoutInflater.inflate(R.layout.child_container, null);
parentContainer.addView(childContainer);
}
}
Try this:
for (int i = 0; i < someNumber; i++) {
LinearLayout.LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); // or any other layout params that suit your needs
LinearLayout childContainer = new LinearLayout(this);
parentLayout.addView(childContainer, params)
}
EDIT
Considering you need to use the content from XML, you'll need to create a custom class that extends LinearLayout and initialize in there all its properties. Something like:
public class MyLinearLayout extends LinearLayout {
public MyLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public MyLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MyLinearLayout(Context context) {
super(context);
init(context);
}
private void init(Context context) {
inflate(context, R.id.R.layout.child_container, this);
// setup all your Views from here with calls to getViewById(...);
}
}
Also, since your custom LieanrLayout extends from LinearLayout you can optimize the xml by replacing the root <LinearLayout> element with <merge>. Here is a short documentation and an SO link. So the for loop becomes:
for (int i = 0; i < someNumber; i++) {
LinearLayout.LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); // or any other layout params that suit your needs
LinearLayout childContainer = new MyLinearLayout(this);
parentLayout.addView(childContainer, params); // feel free to add or not the LayoutParams object
}

Android: custom view from inflated layout

I am creating my own layout based on RelativeLayout as a class in code
I have basics of the layout defined in XML R.layout.menu_layout (style, drawable for background, margin, height)
If I would not need a class then I would call inflater to do this:
RelativeLayout menuLayout = (RelativeLayout)inflater.inflate(R.layout.menu_layout, root);
But I would like to be calling my own class instead
MenuLayout menuLayout = new MenuLayout(myparams);
Since I need to create a class I need to somehow inherit the R.layout.menu_layout in constructor, how can I do that? I guess there is no this.setLayout(res); or this.setResource(res); in View. Maybe I can use the other two parameters in View constructor but I did not find any tutorial how to do that either.
public class MenuLayout extends RelativeLayout {
public MenuLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView(context);
}
public MenuLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public MenuLayout(Context context) {
super(context);
initView(context);
}
private void initView(Context context) {
View view = LayoutInflater.from(context).inflate(R.layout.menu_layout, null);
addView(view);
}
}
now you can use
MenuLayout menuLayout = new MenuLayout(myparams);
you can change constructors for params i think

Categories

Resources