Clickable ListView Items

7 votes · 4 comments

The ClickableListAdapter bases on the API-Demo-Example "Efficient List Adapter". It was refactored to provide a better reusability. Additionally, you can connect OnClickListener and/or OnLongClickListener to each View of your list item. This allows you to create complex interactive lists, or simply using a customized checkbox version.

The ClickableListAdapter is an abstract class. An example implementation is added also.

raw ·
copy
· download
package com.o1.android.view; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; /** * This list adapter is derived from the "Efficient List Adapter"-Example of * API-Demos. It uses holder object to access the list items efficiently. * Additionally, click listeners are provided, which can be connected to the * arbitrary view items, e.g. customized checkboxes, or other clickable * Image/TextViews. Implement subclasses of them and add your listeners to your * "clickable" views. * * @author poss3x */ public abstract class ClickableListAdapter extends BaseAdapter { private LayoutInflater mInflater; private List mDataObjects; // our generic object list private int mViewId; /** * This is the holder will provide fast access to arbitrary objects and * views. Use a subclass to adapt it for your personal needs. */ public static class ViewHolder { // back reference to our list object public Object data; } /** * The click listener base class. */ public static abstract class OnClickListener implements View.OnClickListener { private ViewHolder mViewHolder; /** * @param holder The holder of the clickable item */ public OnClickListener(ViewHolder holder) { mViewHolder = holder; } // delegates the click event public void onClick(View v) { onClick(v, mViewHolder); } /** * Implement your click behavior here * @param v The clicked view. */ public abstract void onClick(View v, ViewHolder viewHolder); }; /** * The long click listener base class. */ public static abstract class OnLongClickListener implements View.OnLongClickListener { private ViewHolder mViewHolder; /** * @param holder The holder of the clickable item */ public OnLongClickListener(ViewHolder holder) { mViewHolder = holder; } // delegates the click event public boolean onLongClick(View v) { onLongClick(v, mViewHolder); return true; } /** * Implement your click behavior here * @param v The clicked view. */ public abstract void onLongClick(View v, ViewHolder viewHolder); }; /** * @param context The current context * @param viewid The resource id of your list view item * @param objects The object list, or null, if you like to indicate an empty * list */ public ClickableListAdapter(Context context, int viewid, List objects) { // Cache the LayoutInflate to avoid asking for a new one each time. mInflater = LayoutInflater.from(context); mDataObjects = objects; mViewId = viewid; if (objects == null) { mDataObjects = new ArrayList<Object>(); } } /** * The number of objects in the list. */ public int getCount() { return mDataObjects.size(); } /** * @return We simply return the object at position of our object list Note, * the holder object uses a back reference to its related data * object. So, the user usually should use {@link ViewHolder#data} * for faster access. */ public Object getItem(int position) { return mDataObjects.get(position); } /** * We use the array index as a unique id. That is, position equals id. * * @return The id of the object */ public long getItemId(int position) { return position; } /** * Make a view to hold each row. This method is instantiated for each list * object. Using the Holder Pattern, avoids the unnecessary * findViewById()-calls. */ public View getView(int position, View view, ViewGroup parent) { // A ViewHolder keeps references to children views to avoid uneccessary // calls // to findViewById() on each row. ViewHolder holder; // When view is not null, we can reuse it directly, there is no need // to reinflate it. We only inflate a new View when the view supplied // by ListView is null. if (view == null) { view = mInflater.inflate(mViewId, null); // call the user's implementation holder = createHolder(view); // we set the holder as tag view.setTag(holder); } else { // get holder back...much faster than inflate holder = (ViewHolder) view.getTag(); } // we must update the object's reference holder.data = getItem(position); // call the user's implementation bindHolder(holder); return view; } /** * Creates your custom holder, that carries reference for e.g. ImageView * and/or TextView. If necessary connect your clickable View object with the * PrivateOnClickListener, or PrivateOnLongClickListener * * @param vThe view for the new holder object */ protected abstract ViewHolder createHolder(View v); /** * Binds the data from user's object to the holder * @param h The holder that shall represent the data object. */ protected abstract void bindHolder(ViewHolder h); } // ------------------------------------------------------------- // E X A M P L E // ------------------------------------------------------------- // LAYOUT FILE <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" > <TextView android:text="Text" android:id="@+id/listitem_text" android:layout_weight="1" android:layout_width="fill_parent" android:layout_height="wrap_content" ></TextView> <ImageView android:id="@+id/listitem_icon" android:src="@drawable/globe2_32x32" android:layout_width="wrap_content" android:layout_height="wrap_content" android:maxWidth="32px" android:maxHeight="32px" > </ImageView> </LinearLayout> //-------------------------------------------------------------- // ACTIVITY //-------------------------------------------------------------- package com.o1.android.view; import java.util.ArrayList; import java.util.List; import android.app.ListActivity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import com.o1.android.view.ClickableListAdapter; import com.o1.android.view.ClickableListAdapter.ViewHolder; /** * An example how to implement the ClickableListAdapter for a list view layout containing * a TextView and an ImageView. * @author poss3x */ public class ClickableListItemActivity extends ListActivity { /** * Our data class. This data will be bound to * MyViewHolder, which in turn is used for the * ListView. */ static class MyData { public MyData(String t, boolean e) { text = t; enable = e; } String text; boolean enable; } /** * Our very own holder referencing the view elements * of our ListView layout */ static class MyViewHolder extends ViewHolder { public MyViewHolder(TextView t, ImageView i) { text = t; icon = i; } TextView text; ImageView icon; } /** * The implementation of ClickableListAdapter */ private class MyClickableListAdapter extends ClickableListAdapter { public MyClickableListAdapter(Context context, int viewid, List<MyData> objects) { super(context, viewid, objects); // nothing to do } protected void bindHolder(ViewHolder h) { // Binding the holder keeps our data up to date. // In contrast to createHolder this method is called for all items // So, be aware when doing a lot of heavy stuff here. // we simply transfer our object's data to the list item representatives MyViewHolder mvh = (MyViewHolder) h; MyData mo = (MyData)mvh.data; mvh.icon.setImageBitmap( mo.enable ? ClickableListItemActivity.this.mIconEnabled : ClickableListItemActivity.this.mIconDisabled); mvh.text.setText(mo.text); } @Override protected ViewHolder createHolder(View v) { // createHolder will be called only as long, as the ListView is not filled // entirely. That is, where we gain our performance: // We use the relatively costly findViewById() methods and // bind the view's reference to the holder objects. TextView text = (TextView) v.findViewById(R.id.listitem_text); ImageView icon = (ImageView) v.findViewById(R.id.listitem_icon); ViewHolder mvh = new MyViewHolder(text, icon); // Additionally, we make some icons clickable // Mind, that item becomes clickable, when adding a click listener (see API) // so, it is not necessary to use the android:clickable attribute in XML icon.setOnClickListener(new ClickableListAdapter.OnClickListener(mvh) { public void onClick(View v, ViewHolder viewHolder) { // we toggle the enabled state and also switch the icon MyViewHolder mvh = (MyViewHolder) viewHolder; MyData mo = (MyData) mvh.data; mo.enable = !mo.enable; // toggle ImageView icon = (ImageView) v; icon.setImageBitmap( mo.enable ? ClickableListItemActivity.this.mIconEnabled : ClickableListItemActivity.this.mIconDisabled); } }); // for text we implement a long click listener text.setOnLongClickListener(new ClickableListAdapter.OnLongClickListener(mvh) { @Override public void onLongClick(View v, ViewHolder viewHolder) { MyViewHolder mvh = (MyViewHolder) viewHolder; MyData mo = (MyData)mvh.data; // we toggle an '*' in our text element String s = mo.text; if (s.charAt(0) == '*') { mo.text = s.substring(1); } else { mo.text = '*' + s; } mvh.text.setText(mo.text); } }); return mvh; // finally, we return our new holder } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // preloading our icons mIconEnabled = BitmapFactory.decodeResource(this.getResources(), R.drawable.globe2_32x32); mIconDisabled = BitmapFactory.decodeResource(this.getResources(), R.drawable.globe2_32x32_trans); // fill list with some items... // to demonstrate the performance we create a bunch of data objects for (int i = 0; i < 250; ++i) { mObjectList.add(new MyData("Some Text " + i, true)); } //here we set our adapter setListAdapter(new MyClickableListAdapter(this, R.layout.clickablelistitemview, mObjectList)); } // --------------- field section ----------------- private Bitmap mIconEnabled; private Bitmap mIconDisabled; private List<MyData> mObjectList = new ArrayList<MyData>(); }
Add a comment

4 Comments

Thanks for sharing these very helpful programming code.

Accounting Essays |Dissertation help

Reply · April 4, 2011, 12:02 p.m.

The template is good but there are too much code for just override createHolder and bindHolder. Also is a bad practice saving a data object inside ViewHolder. As the class name says is for Views.

Reply · April 6, 2011, 12:37 p.m.

This is a positive space...if you have a better version ... do it and link it here and do not criticize people who are trying to help! Thanks

Reply · Feb. 28, 2013, 11:53 p.m.

Thank you!!!! You are awesome!!!!!!!!

Reply · Feb. 28, 2013, 11:51 p.m.