Coding Lines

RSS

Android: Multiple layouts in Dynamically Loading Adapter

This tutorial describes how to implement multiple layouts items for adapter based on DynamicViewAdapter.

GitHub repository:  https://github.com/yfranz/DynamicList.

example

Please see previous tutorial before start reading this.

Simple list view with the same type of items is most common use case. However, sometimes it requires some sort of header or separator between items. DynamicViewAdapter can efficiently manage several types of item layouts.

For this tutorial: I’ve created new adapter inherited from DynamicViewAdapter. I’ve copied all the code from TestDynamicViewAdapter to this new class with name TestDynamicHeaderViewAdapter.

The new adapter is going to operate with two types of layouts: test_item.xml and header_item.xml. It will display every 10th item as header_item.

Now, there are several additional steps need to be performed to modify TestDynamicViewAdapter:

  • 1. Create new view holder class;
  • 2. Overide onDataLoad() with logic when every 10th item is a header item;
  • 3. Overide onDataView with handling of multiple layouts;

Note: This example is not very practical since both layouts contains one TextView control and can be implemented using same layout and view holder. But I’m trying to keep the code as simple as possible so reader can focus on important aspects of implementation. In real life it could be two drastically different layouts with bunch of child views.

Create new view holder class

        
    public class HeaderViewHolder extends ViewHolder
    {
        public final static int ResourceId = R.layout.header_item;
        private TextView headerText;
    
        public HeaderViewHolder(View convertView)
        {
            super(convertView, ResourceId);
    
            headerText = (TextView)view.findViewById(R.id.headerText);
        }
    
        public void render(AdapterItem item)
        {
            headerText.setText(String.format("Header: %s", item.value));
        }
    }

        
    

TestDynamicHeaderViewAdapter: Overide onDataLoad with handling of multiple layouts

Despite the logic of 10th item we need to remember type of the created item somehow. The seconds parameter of addItem method was added for such purpose. So, we are going to store resource id there.

        
    @Override
    protected void onDataLoad()
    {
        for (int i = 0; i < _model.getCount(); i++)
        {
            if (i % 10 == 0)
            {
                addItem(_model.getItem(i), HeaderViewHolder.ResourceId);
            }
            else
            {
                addItem(_model.getItem(i), ItemViewHolder.ResourceId);
            }
        }
    }
        
    

TestDynamicHeaderViewAdapter: Overide onDataView with handling of multiple layouts

Since we know that resource id is now storing in tag field we need to create appropriate viewholder (if needed) based on that. So, I moved code that decide which view holder create into separate method: createViewHolderByResourceId. Rest of the code remain the same as in TestDynamicViewAdapter:

        
    @Override
    public View onDataView(int position, AdapterItem item, View convertView,
            ViewGroup parent)
    {
        int resourceId = (Integer) item.tag;
    
        convertView = tryInflateView(convertView, resourceId, parent);
        ViewHolder viewHolder = (ViewHolder) convertView.getTag();
        if (viewHolder == null)
        {
            viewHolder = createViewHolderByResourceId(convertView, resourceId);
        }
        viewHolder.render(item);
        return convertView;
    }
    
    private ViewHolder createViewHolderByResourceId(View convertView,
            int resourceId)
    {
        ViewHolder result = null;
        switch (resourceId)
        {
        case ItemViewHolder.ResourceId:
            result = new ItemViewHolder(convertView);
            break;
        case HeaderViewHolder.ResourceId:
            result = new HeaderViewHolder(convertView);
            break;
        }
        return result;
    }
        
    

tryInflateView should take care of whether or not physically creating or re-creating view. This approach optimizes android resources usage and improves performance.