ListView optimizes new gameplay and creates a new ListView and listview gameplay that is easy to maintain, high-performance, and fast to develop

Source: Internet
Author: User

ListView optimizes new gameplay and creates a new ListView and listview gameplay that is easy to maintain, high-performance, and fast to develop

How do you design a complicated ListView as shown in?

(The red line is the ListView area, and the blue line is the four types contained in Listview)

Maybe you will inherit a BaseAdapter to implement the getView () method,

Use getViewTypeCount (), getItemViewType (int position), and other methods to add a lot of if... else... statement blocks in getView () to generate a View of the corresponding Type.

Of course, you may also think of the ValueHolder mode, which customizes A ValueHolder object for each Type, greatly improving the performance and greatly increasing the code.

At last, this custom BaseAdapter can have thousands of lines of code and is filled with the if... else... statement, which seems very difficult to maintain.

It's complicated. Now I want to host it for you.

1. Enhance BaseAdapter. To simplify the code, we need a custom BaseAdapter to better support multi-ItemType design.
Because BaseAdapter supports various itemtypes, various itemtypes correspond to their respective UI layout and background data, and are filled in getView. Obviously, we will think of encapsulating various itemtypes into an object, which has its own UI layout, background data, and filling functions. The BaseAdapter only needs to manage these different itemtypes, instead of worrying about how each ItemType generates a UI layout and binds data.
With this idea, we start to implement it. First, we define Item. javaTo control the UI layout and background data corresponding to each ItemType:
<pre name="code" class="java">public abstract class Item<T> {    protected T data;    protected int mLayoutResId = -1;    public Item(T t) {        this(t, -1);    }    public Item(T t, int layoutResId) {        data = t;        mLayoutResId = layoutResId;    }    public T getData() {        return data;    }}

To facilitate expansion, we define Item as an abstract class. This abstract class contains two member variables. data is the data source corresponding to the Item. mLayoutResId is the layout file corresponding to the Item. 

NextYou need to customize your own BaseAdapter to manage these Item objects. I named it ItemAdapterI hope it can intelligently manage ItemType. When ItemType is increased and reduced, I don't need to write additional if... else.... To achieve this goal, I need to rewrite it.
GetItemViewType () and getViewTypeCount (). <Span style = "font-family: Arial, Helvetica, sans-serif;"> and make them smarter </span>

Let's talk about the Code:
Public class ItemAdapter extends BaseAdapter {protected List <Item> mData = new ArrayList <Item> (); protected Context mContext; private List <ItemLayout> mItemLayouts; private ItemLayout mTempItemLayout = new ItemLayout (); private boolean mHasReturnedViewTypeCount = false; // whether getViewTypeCount private int mMaxTypeCount = 10; private static final Boolean DEBUG = true; private static final String T AG = "ItemAdapter"; public ItemAdapter (Context context) {mContext = context; mItemLayouts = new ArrayList <ItemLayout> ();} private static class ItemLayout implements Comparable <ItemLayout> {private Class <?> Clz; public int compareTo (ItemLayout other) {if (clz. hashCode () = other. clz. hashCode () {return 0;} else if (clz. hashCode () <other. clz. hashCode () {return-1 ;}else {return 1 ;}} private ItemLayout createItemLayout (Item <?> Item, ItemLayout in) {ItemLayout il = in! = Null? In: new ItemLayout (); il. clz = item. getClass (); return il;} private void addToItemLayouts (Item <?> Item) {final ItemLayout il = createItemLayout (item, null); int insertPos = Collections. binarySearch (mItemLayouts, il); // if it does not exist, add a type if (insertPos <0) {insertPos = insertPos *-1-1; mItemLayouts. add (insertPos, il) ;}} public void setTypeCount (int num) {if (mHasReturnedViewTypeCount) {throw new IllegalArgumentException ("must call setTypeCount before setAdapter ");} mMaxTypeCount = num;} pu Blic int indexOfItem (Item <?> Item) {return mData. indexOf (item);} public void addItem (final Item <?> Item) {mData. add (item); addToItemLayouts (item); yydatasetchanged ();} public void addItem (int idx, final Item <?> Item) {mData. add (idx, item); addToItemLayouts (item); yydatasetchanged ();} public void addItems (final ArrayList <Item> items) {mData. addAll (items); for (Item <?> Item: items) {addToItemLayouts (item);} yydatasetchanged ();} public Item <?> RemoveItem (int idx) {if (idx <mData. size () {Item <?> T = mData. remove (idx); yydatasetchanged (); return t;} else {return null ;}} public void removeItem (Class <?> Clz) {Iterator <Item> iterator = mData. iterator (); boolean find = false; while (iterator. hasNext () {if (clz. isInstance (iterator. next () {find = true; iterator. remove () ;}} if (find) {this. notifyDataSetChanged () ;}} public void clearItems () {mData. clear (); // The value of mItemLayouts is to make getItemViewType always get the correct value this. notifyDataSetChanged () ;}@ Override public int getItemViewType (int position) {if (! MHasReturnedViewTypeCount) {mHasReturnedViewTypeCount = true;} final Item <?> Item = this. getItem (position); mTempItemLayout = createItemLayout (item, mTempItemLayout); int viewType = Collections. binarySearch (mItemLayouts, mTempItemLayout); if (DEBUG) {Log. I (TAG, "getItemView Type is:" + viewType + "position is:" + position + "item class is:" + item. getClass (). getName ();} if (viewType <0) {// not tested: viewType is not found normally. if <0, it is an exception. // View is not reused in case of exceptions. If (DEBUG) {Log. e (TAG, "ERROR: viewType <0 !!! ");} Return IGNORE_ITEM_VIEW_TYPE;} else {return viewType ;}}@ Override public int getViewTypeCount () {if (! MHasReturnedViewTypeCount) {mHasReturnedViewTypeCount = true;} // additional memory overhead will be incurred here. To avoid this, you must go to SimpleListFragment. before java executes setAdapter, add all items return mMaxTypeCount; // return Math. max (1, mItemLayouts. size ();} // number of items in a certain type, public int getCount (Class <?> Clz) {int num = 0; for (Item <?> Item: mData) {if (item. getClass (). equals (clz) {num ++ ;}} return num ;}@ Override public int getCount () {return mData. size () ;}@ Override public Item <?> GetItem (int position) {if (position <0 | position >= getCount () {return null;} Item <?> Item = mData. get (position); return item ;}@ Override public long getItemId (int I) {return I ;}@ Override public View getView (int position, View convertView, ViewGroup parent) {// TODO }}

In ItemAdapter, I use List <Item> mDataTo save all Item objects, List <ItemLayout> mItemLayoutsSave all types. For example, if 50 items are added to the ItemAdapter and these 50 items belong to different types in 5, the mData size is 50, and the mItemLayouts size is 5. as mentioned above, I have done a lot of processing on the getViewTypeCount () and getItemCount () methods to make them easier to use.
In ItemAdapter, I also implemented an internal ItemLayout class, which is mainly used for Binary Search and improves the ItemType search speed from mItemLayouts.
Next, we need to implement the getView method of ItemAdapter. As mentioned at the beginning, to facilitate development, we hope that the getView of the Adapter only performs management work and puts the real implementation into the Item. Therefore, we first add a getView method in Item. java:
<Pre name = "code" class = "java"> public View getView (View convertView, ViewGroup parent) {if (convertView = null) {convertView = LayoutInflater. from (mContext ). inflate (mResLayoutId, parent, false);} // TODO filled data return convertView ;}
Next, the getView method of ItemAdapter obtains convertView through getView of Item: 

    @Override    public View getView(int position, View convertView, ViewGroup parent) {        final Item<?> item = this.getItem(position);        if (item == null) {            Log.e(TAG, "ERROR item is NULL");        }        mTempItemLayout = createItemLayout(item, mTempItemLayout);        if (Collections.binarySearch(mItemLayouts, mTempItemLayout) < 0) {            if (DEBUG) {                Log.v(TAG, "ERROR getView binarySearch not found");            }            convertView = null;        }        return item.getView(convertView, parent);    }

So far, we have simplified the complicated Apdater through the ItemAdapter and Item classes. When initializing ItemAdapter, you only need the following statements:
MItemAdapter = new ItemAdapter (this );
MItemAdapter. addItem (new ItemA (itemDataA ));
MItemAdapter. addItem (new ItemB (itemDataB ));
MItemAdapter. addItem (new ItemC (itemDataC ));
MListView. setAdapter (mItemAdapter); then implement the getView method for each Item. The remaining items will be automatically processed by ItemAdapter.
2. With the previous efforts to host the getView of the Item, at least the code looks less messy. Now we need to make the code simpler. Currently, the most code, the most complex part is the getView () method of the Item. Let's start from here. In getView (), there are two main tasks: I. loading layout Ii. Data Filling
The implementation of each Item in the loading layout is the same. The system first checks whether convertView is Null. If it is Null, the layout file is inflate. Because it is too repetitive, we can create a new class and put these repeated statements into this class for implementation. I name this class ItemBuilder. java.
public class ItemBuilder {    private final Context mContext;    private View mConvertView;    private int mLayoutId = -1;    public ItemBuilder(Context context, ViewGroup parent, int layoutId) {        mContext = context;        mConvertView = LayoutInflater.from(mContext).inflate(layoutId, parent, false);
        mConvertView.setTag(this);        mLayoutId = layoutId;    }
    public View getView() {        return mConvertView;    }}
In the getView () method of Item. java, use ItemBuilder to return convertView:
    public View getView(View convertView, ViewGroup parent) {        ItemBuilder builder = getAdapterBuilder(convertView, parent);        return builder.getView();    }    protected ItemBuilder getAdapterBuilder(View convertView, ViewGroup parent) {        if (convertView == null) {            return new ItemBuilder(parent.getContext(), parent, mLayoutResId);        }        return (ItemBuilder) convertView.getTag();    }

If convertView is empty, a new ItemBuilder is generated. In the ItemBuilder constructor, The inflate layout file generates convertView and sets ItemBuilder as a Tag to convertView. Next time, you only need to getTag () on convertView to get the ItemBuilder object.
Next, we still need to fill in the data. We believe that ItemBuilder has been made into a prototype of ValueHolder. Now I want ItemBuilder to save all the child views like ValueHolder. At the same time, I also want to use ItemBuilder to directly populate these child views.
First, I define a SparseArray <View> mViews in ItemBuilder and use SparseArray to save all the child views. The reason for using SparseArray is that the View id can be used as the Key, and SparseArray adopts binary search, which saves better memory than HashMap.
    private final SparseArray<View> mViews;
Because you want to directly populate the data through ItemBuilder, you need to provide a large number of setXXX () methods in ItemBuilder to populate the child View:
    public ItemBuilder setImageResource(int viewId, int imageResId) {        ImageView view = retrieveView(viewId);        view.setImageResource(imageResId);        return this;    }    public ItemBuilder setImageBitmap(int viewId, Bitmap bm) {        ImageView view = retrieveView(viewId);        view.setImageBitmap(bm);        return this;    }    private <T extends View> T retrieveView(int viewId) {        if (mLastViewId == viewId && mLastView != null) {            return (T) mLastView;        }        View view = mViews.get(viewId);        if (view == null) {            view = mConvertView.findViewById(viewId);            mViews.put(viewId, view);        }        setLastView(view, viewId);        if (mLastView == null)            android.util.Log.i("xzy", "!!!!!mLastView is : null layout is: " + mLayoutId);        // return (T) view;        return (T) mLastView;    }

As shown above, when filling in a View control, you only need to input the Control id and the data to be filled. When the setXXX () method is executed, the view control is placed in the mViews of ItemBuilder for management. If we regard ItemBuilder as ValueHolder, ValueHolder is a variable value holder, where the View data can be dynamically increased and managed.

The framework has been set up. Finally, you need to add a convert () method to the Item to expose ItemBuilder to the developer so that the developer can fill in the data in the convert () method:
public abstract boolean convert(ItemBuilder builder, ViewGroup parent, T data);

To update the getView () of an Item, call the convert () method here:
    public View getView(View convertView, ViewGroup parent) {        ItemBuilder builder = getAdapterBuilder(convertView, parent);        convert(builder, parent, getData());        return builder.getView();    }

Currently, developers do not need to care about the getView method and the valueHolder mode when implementing the Item method. They only need to implement the convert method and use the ItemBuilder and data passed through the convert method to fill in the data, in addition, data filling is very simple. It is similar to AlertDialogBuilder:
builder.setImageResource(, data);
:-) You can create a ValueHolder with only one line of code and fill in the data.

Finally, our code looks like this:

Code in Activity:
mListView = (ListView) this.findViewById(;        mItemAdapter = new ItemAdapter(this);        mItemAdapter.addItem(new PicItem(mDrawableId[i], R.layout.pic_items));        mListView.setAdapter(mItemAdapter);
The PicItem. java code is as follows:
public class PicItem extends Item<Integer> {    public PicItem(Integer t, int layoutResId) {        super(t, layoutResId);    }    @Override    public boolean convert(ItemBuilder builder, ViewGroup parent, Integer data) {        builder.setImageResource(, data);        return true;    }}

This is simple. It supports multiple itemtypes and uses the ValueHolder mode for performance optimization. You don't have to worry about a weekend appointment.
The Chinese language capability is limited. If you do not understand it, please refer to the Code: Project address: Workshop


Author: xzy2046, which must be noted for reprinting. Blog homepage:

How to optimize ListView in Android?

You can specify ListVeiw on your own. Remember that my blog on Baidu is well written. You cannot find the specified ListView. Previously encountered projects. When I have time, I will check if there are any records in the svn document and send them to you.
Listview optimization methods (original)

Listview A view that shows items in a vertically scrolling list. A list view that displays a vertical scroll subitem. In android development, there are many places where listview is used to display data and form a vertical view. Using listview is a standard adapter mode. The data is displayed as needed by the data --, interface -- xml, and adapter -- adapter. xml describes how the data is displayed, activities. If a custom adapter is used, the getView method will be rewritten to generate the view and data for the item in the getView method. As shown in the figure: Here is an optimization, that is, reusing the view to reduce memory consumption and accelerate item loading. The Optimization in getView is very common. I have summarized the Three optimization methods below. please correct me. First, the convertView is reused, which greatly reduces the memory consumption. If you determine whether convertView is null, You need to generate a view. Then, you can return the View data to the bottom layer and present it to the user. Feature: if the current convertView is null, a view is generated through LayoutInflat. View Code 2: There is a drawback in the above writing, that is, each time you get a View, you need to re-find the findViewById, re-find the control, and then assign values to the control and set the event accordingly. In this case, we are actually doing the same thing, because the geiview actually contains these controls, and the IDs of these controls are the same, that is, as long as the findViewById is in the view, findViewById is not required each time. The second writing method is provided below. There is usually an internal class ViewHolder. This ViewHolder is used to identify some controls in the view to facilitate the setting of Event-related operations, such as onClick, so you don't have to use findViewById every time, reducing the performance consumption. At the same time, convertView is reused, which greatly reduces memory consumption. View Code 3: I personally think this writing method is the most comfortable. The most comfortable way is to look at the Code with a very nice and clear look. Feature. The internal class ViewHolder is used and convertView is reused. The second difference is that a temporary variable "View view = convertView" is used, the view is modified, and the viewView Code is returned. The preceding variable is a centralized method for beginners to learn and summarize. The source Code is as follows: according to the suggestions provided by friends downstairs, we found there are some optimizations. The latest update is as follows: View Code @ Override public View getView (int position, View convertView, ViewGroup parent) {View view = convertView; ViewHolder holder; if (view = null) {view = LayoutInflater. from (context ). inflate (R. layout. section_list_item1, null); holder = new ViewHolder (); holder. TV _name = (TextView) view. findViewById (R. id. contact_contactinfoitem_ TV _name); holder. TV _phone = (TextView) view. findView ...... remaining full text>

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.