I usually create a custom view, for say,
public class MyView extends LinearLayout{
public MyView(Context context){
super(context);
//Inflating my custom layout
LayoutInflater inflater = getLayoutInflater();
inflater.inflate(R.layout.view_layout, this);
}
}
The problem with the above layout is, it will create a new LinearLayout and inflate R.layout.view_layout inside it, adding a new view hierarchy which is not preferred.
R.layout.view_layout contains RelativeLayout with many child elements, which I cannot add programatically by extending RelativeLayout (since positioning is difficult).
Having unnecessary hierarchies slow down my app.
What is the best way to create custom views/view group without an extra hierarchy level.
Solved as per the soln by CommonsWare:
XML Layout:
<merge>
<TextView ...
.../>
More items
</merge>
Since the XML layout had RelativeLayout as the parent view, I will extend RelativeLayout in the code instead of LinearLayout
public class MyView extends RelativeLayout{
public MyView(Context context){
super(context);
//Inflating my custom layout
LayoutInflater inflater = getLayoutInflater();
inflater.inflate(R.layout.view_layout, this);
}
}
Have res/layout/view_layout.xml start with a <merge> element instead of a LinearLayout.
<merge> is the solution but your layout is displayed incorrectly in Design mode in your IDE and this could be a pain. To fix this you need:
<merge
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout"
tools:layout_width="match_parent"
tools:layout_height="match_parent">
Related
I have layout.xml which is FrameLayout and it has another FrameLayout inside.
I have class Field subclassing FrameLayout that contains logic, background color handling etc.
I want to do something like this:
Field f = new Field(width, height, color); //args can be changed if needed.
linearLayout.addView(f);
Inside Field class in a constructor I do this:
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.game_board_field, null);
Something is added to linearLayout beccause its children count is not 0, but I can't see the red square that Field is.
Is there something I do wrong(clearly)?
Yes this was it.
View view = inflate(getContext(), R.layout.fragment_game_board_field, this);
Apart from that I had to correct other properties in xml, because I dropped Frament approach and decided to inflate layouts by hand.
Problem:
I've developed a custom compound view and I'm unsure of how to display it in my listview.
What I've done:
-> My custom compound view
public class HZScrollView extends LinearLayout {
public HZScrollView(Context context) {
super(context);
initView(context);
}
private void initView(Context context) {
mContext = context;
setOrientation(LinearLayout.HORIZONTAL);
setGravity(Gravity.CENTER_VERTICAL);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
this.setLayoutParams(lp);
//inflate XML resource and attach
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mInflater.inflate(R.layout.hz_scroll_view, this, true);
}
}
public void addContent(String name, String age, String sex) {
//content is added to the individual widgets within this compound view
}
-> My Adapter
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
convertView = new HZScrollView(context); //<--- PROBLEM !
}
}
The major problem I'm experiencing is that the line marked with "PROBLEM" causes the exception java.lang.RuntimeException: Unable to start activity ComponentInfo{com.my.app/.MainActivity}: android.view.InflateException: <merge /> can be used only with a valid ViewGroup root and attachToRoot=true
In sample code around the 'net, the getView() usually inflates an XML layout, but in my case the compound view is completely self-contained.
Question:
How is it possible to insert/attach my custom compound view into the listview item ?
Solution:
1) in the adapter, assigning the HZScrollView to convertView is fine
2) to fix another problem with LayoutParams, the initView() needed to be updated to use AbsListView.LayoutParams instead of ViewGroup.LayoutParams (since the parent container is a listView)
3) to fix the InflateException, child views used <merge> in the XML, I refactored that to wrap the child views in LinearLayout's. Note: using <merge> in the XML for "hz_scroll_view" file is just fine.
The really interesting part, for me, was point #1 because I was unsure whether assigning a custom compound view to a listview item would work.
I am currently populating an Adapter on startup with views inflated from XML using
private void addView(Context context) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.deal_tile, this, null);
mViews.add(view);
}
However, I've found that storing the views in a list inside the AdapterView creates problems with controls within those views, so I want to change over to use the recycling functions in Adapter#getView(int position, View recycle, ViewGroup container).
For this reason I want to use a custom view class so I can do a sanity check (if(recycle!=null && recycle instanceof CustomView)) before I repopulate it in the adapter. However, I can't find out how you inflate a custom view class from XML. I can find out how you add an inflated view to a custom view, I can find out how you insert a custom view into an XML layout, etc, and obviously I am quite happily inflating these things directly using LayoutInflater, but I can't find an equivalent for generating the custom view itself. I want to reuse the XML I already have; consequently I don't want to program in the elements (and how they look) directly.
I used this to create my own slide gallery, i think it would help.
LinearLayout internalWrapper = new LinearLayout(getContext());
internalWrapper.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
internalWrapper.setOrientation(LinearLayout.HORIZONTAL);
addView(internalWrapper);
this.mItems = items;
LinearLayout generalLayout = new LinearLayout(this.getContext());
generalLayout = (LinearLayout) View.inflate(this.getContext(), R.layout.galleryrow, null);
// inside linear layout
LinearLayout generalLinear = (LinearLayout) generalLayout.findViewById(R.id.rowgenerallin);
// set height & width to the LINEAR
generalLinear.setLayoutParams(new LayoutParams(reference_width, reference_height));
ImageView ivl = (ImageView) generalLayout.findViewById(R.id.arrow_left);
ImageView ivr = (ImageView) generalLayout.findViewById(R.id.arrow_right);
internalWrapper.addView(generalLayout);
In my case, R.layout.gallery_row contains the two images I want to manage, nested by a LinearLayous (rowgenerllin), the internal wrapper is an empty LinearLayout declared in the main layout of your activity.
Double check the LayoutParams code or you will get a big NULL :)
Cheers!
While researching how to create custom compound views in Android, I have come across this pattern a lot (example comes from the Orange11 blog) :
public class FirstTab extends LinearLayout {
private ImageView imageView;
private TextView textView;
private TextView anotherTextView;
public FirstTab(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.firstTab, this);
}
}
I mostly understand how this is working, except for the part where inflate() is called. The documentation says that this method returns a View object, but in this example the author does not store the result anywhere. After inflation, how is the new View created fromt eh XML associated with this class? I thought about assigning it to "this", but that seems very wrong.
thanks for any clarification.
The reference to this would be the viewgroup root. See here:
http://developer.android.com/reference/android/view/LayoutInflater.html#inflate(int, android.view.ViewGroup)
What this means is it is inflating the designated view from xml with this as the parent view. The xml ends up inside the Linear layout defined by the class.
edit: put in the full link as I can't seem to get URLs with brackets to escape properly
I defined a layout like this:
<LinearLayout>
<ListView />
</LinearLayout>
and I want to use a wrapper class for it:
public class MyView extends LinearLayout {
ListView mListView;
public build() {
mListView = (ListView)findViewById(R.id.mylistview);
}
}
not sure how to inflate this from my layout file:
MyView v = LayoutInflater.from(this).inflate(R.layout.myview, null);
the inflater of course does not know what 'MyView' type is, and returns only View. What's a good way to reconcile this?
Why do you inflate LinearLayout in linear layout ?
What you probably need, define a layout for myView. Inflate it in some sort of initialize procedure ( for that view ).
And than you'll use your new view in xml layout.
More on that here
http://developer.android.com/guide/topics/ui/custom-components.html
You have to define the type of the Layout in xml.
Instead of your xml you need to declare it like that:
<your.package.MyView>
<ListView />
</MyView>