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>();
}