A better custom ViewGroup
Jerome P. commented on my earlier post denouncing the ViewHolder pattern. He suggested an improvement to the ViewGroup approach that inverts the direction of dependency between the ContactView and its layout XML file. Instead of referencing the custom class from XML, the Java code now references the layout file using a standard resource reference. This adds further type safety and it means your view class can be completely ProGuarded.
The list adapter implementation becomes more convenient. It no longer needs to use a layout inflater, it can simply new up an instance of the view class:
[code language=”java”]
public View getView(int position, View convertView, ViewGroup parent) {
ContactView view;
if (convertView == null) {
view = new ContactView(context);
// Was: view = (ContactView) inflater.inflate(R.layout.list_item, null);
}
// continued…
}
[/code]
To do this, change the root element of the XML layout to <merge/> and modify the constructors of the custom view group:
[code language=”java”]
public class ContactView extends LinearLayout {
// private field declarations
/** Inherited constructor. */
public ContactView(Context context) {
super(context);
init(context);
}
/** All three constructors invoke this method. */
private void init(Context context) {
setOrientation(VERTICAL);
LayoutInflater.from(context).inflate(R.layout.contact_view, this, true);
nameView = (TextView) findViewById(R.id.contact_name);
emailView = (TextView) findViewById(R.id.contact_email);
addressView = (TextView) findViewById(R.id.contact_address);
}
// continued
[/code]
The complete code for this example has been added to the 4-_better_custom_ViewGroup branch of the android-cciaa repository on GitHub.
Look at our consultancy services, training offers and careers below or contact us at info@xebia.com
Comments
using the <merge/> tag is all good if it was a simple LinearLayout. what about views that uses RelativeLayout? i could only think of 2 approach.
1. use <merge><RelativeLayout>. this would introduce a useless parent container after merging/inflate
2. pretend <merge> is a RelativeLayout, i.e. continue using Layout specific attributes on the child view and in the View java class, extends RelativeLayout.
what would your approach be?
I can see the problem you’re getting at. The approach works perfectly fine, but the layout editor doesn’t know how to interpret it and you lose all editor support. For the short term, I guess I would temporarily substitute <RelativeLayout/> as XML root element for the time I’m editing the layout file and change it back to <merge/> once the design work is done; basically option 2 with a bit of manual work.
The only lasting solution I can think of is an improvement to the Android layout editor, using the “tools:” xmlns that they introduced a little while ago. Something like this:
[code language=”xml” autolinks=”false”]
<merge xmlns:tools="ht p://schemas.android.com/tools"
xmlns:android="ht p://schemas.android.com/apk/res/android"
tools:container="RelativeLayout">
<!– content –>
</merge>
[/code]
This would clearly need buy-in from the adt-dev folks.
This sounds good, but I guess there’s a downside to instantiating the custom view yourself, in that it won’t inherit your theme properties.
Hi, I came across your post accidentally looking for animation alternatives to ViewAnimator.
Although completely unrelated, I was guilty of using the classic adapter approach and have been suffering performance issues when handling large lists.
I tried implementing your ‘better custom viewgroup’ but stumbled upon a roadblock.
How do you implement this with listeners for lists such as a to-do-list for example where we have a linear layout with an edit text and checkboxes?
The main thing is to respect the Law of Demeter, that is, maintain high cohesion and low coupling. The most straight forward approach is that your adapter is the listener for your item view types’ events and your activity or fragment is the listener for your Adapter’s events, but that’s not a one-size-fits-all solution. In case of the check boxes, you may be able to do something clever with Checkable and duplicateParentState to avoid implementing any custom listeners; I didn’t try that out.
Last sentence in your first paragraph:
“This adds further type safety and it means your view class can be completely ProGuarded.”
May I ask what it means? I don’t get it.
What I meant was that if you create a custom ViewGroup and use it like you would any other layout manager, you need to put the fully qualified name in XML, like so:
…which means you need to leave the class name unobfuscated or the XML won’t inflate properly*. I suppose “completely” is not the best choice of word, because all the View Lifecycle callbacks can not be obfuscated either way.
*) Or use DexGuard, which can rewrite the XML to solve this problem.
Ok, I got it. Thanks~