Creating Viewholders for Listviews with Different Item Layouts

Creating ViewHolders for ListViews with different item layouts

ListView has a built in type management system. In your adapter, you have several types of items, each with their own view and layout. By overriding getItemViewType to return the data type of a given position, ListView is garunteed to pass in the correct convertview for that type of data. Then, in your getView method simply check the datatype and use a switch statement to handle each type differently.

Each Layout type should have its own viewholder for naming clarity and ease of maintainence. Name the ViewHolders something related to each data type to keep everything straight.

Trying to overlap everything into one ViewHolder is just not worth the effort.

Edit
Example

@Override 
public View getView(int position, View convertView, ViewGroup parent) {
int viewType = this.getItemViewType(position);

switch(viewType)
{
case TYPE1:

Type1Holder holder1;

View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getContext().getSystemService (Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.layout_mylistlist_item_type_1, parent, false);

holder1 = new Type1Holder ();
holder1.text = (TextView) v.findViewById(R.id.mylist_itemname);
v.setTag(holder1);
}
else {
holder1 = (Type1Holder)v.getTag();
}

MyListItem myItem = m_items.get(position);

// set up the list item
if (myItem != null) {
// set item text
if (holder1.text != null) {
holder1.text.setText(myItem.getItemName());
}
}

// return the created view
return v;

case TYPE2:
Type2Holder holder2;

View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.layout_mylistlist_item_type_2, parent, false);

holder2 = new Type2Holder ();
holder2.text = (TextView) v.findViewById(R.id.mylist_itemname);
holder2.icon = (ImageView) v.findViewById(R.id.mylist_itemicon);
v.setTag(holder1);
}
else {
holder2 = (Type2Holder)v.getTag();
}

MyListItem myItem = m_items.get(position);

// set up the list item
if (myItem != null) {
// set item text
if (holder2.text != null) {
holder2.text.setText(myItem.getItemName());
}

if(holder2.icon != null)
holder2.icon.setDrawable(R.drawable.icon1);
}

// return the created view
return v;

default:
//Throw exception, unknown data type
}
}

How to inflate different layout in Listview row using Viewholder pattern

I solved this using a RecyclerView instead.

<RelativeLayout>
<android.support.v7.widget.RecyclerView>
</RelativeLayout>

Every entry is a cardview:

<android.support.v7.widget.CardView>

in EntriesAdapter :

@Override
public void onBindViewHolder(EntryViewHolder vh, int position) {
final EntryVO entryVO = entries.get(position);
vh.cv.removeAllViewsInLayout();
vh.cv.addView(EntriesAdapter.createEntryCardViewTable(context, entryVO, new ReadListener(context)));
}

Finally in createEntryCardViewTable method I construct a TableLayout programmatically like an HTML table with rows for the entry and its definitions.

Performance is great, despite the 1000+ entries (every entry may have more than one definition).

How to create RecyclerView with multiple view types

Yes, it's possible. Just implement getItemViewType(), and take care of the viewType parameter in onCreateViewHolder().

So you do something like:

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
class ViewHolder0 extends RecyclerView.ViewHolder {
...
public ViewHolder0(View itemView){
...
}
}

class ViewHolder2 extends RecyclerView.ViewHolder {
...
public ViewHolder2(View itemView){
...
}

@Override
public int getItemViewType(int position) {
// Just as an example, return 0 or 2 depending on position
// Note that unlike in ListView adapters, types don't have to be contiguous
return position % 2 * 2;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case 0: return new ViewHolder0(...);
case 2: return new ViewHolder2(...);
...
}
}

@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
switch (holder.getItemViewType()) {
case 0:
ViewHolder0 viewHolder0 = (ViewHolder0)holder;
...
break;

case 2:
ViewHolder2 viewHolder2 = (ViewHolder2)holder;
...
break;
}
}
}

Android ListView with different layouts for each row

Since you know how many types of layout you would have - it's possible to use those methods.

getViewTypeCount() - this methods returns information how many types of rows do you have in your list

getItemViewType(int position) - returns information which layout type you should use based on position

Then you inflate layout only if it's null and determine type using getItemViewType.

Look at this tutorial for further information.

To achieve some optimizations in structure that you've described in comment I would suggest:

  • Storing views in object called ViewHolder. It would increase speed because you won't have to call findViewById() every time in getView method. See List14 in API demos.
  • Create one generic layout that will conform all combinations of properties and hide some elements if current position doesn't have it.

I hope that will help you. If you could provide some XML stub with your data structure and information how exactly you want to map it into row, I would be able to give you more precise advise. By pixel.

Listview with multiple layout item changes on item moves off the screen

I solved the problem since there was two different layouts to inflate i had to use two different viewholders.

public class MessageAdapter extends ResourceCursorAdapter {
private static final ContactsWrapper WRAPPER = ContactsWrapper
.getInstance();
/** Cursor's sort. */
// static ViewHolder holder;
int rowType;
public static String SORT = Calls.DATE + " DESC";
private Activity context;
/** {@link BackgroundQueryHandler}. */
// private BackgroundQueryHandler queryHandler;
/** Token for {@link BackgroundQueryHandler}: message list query. */
/** Thread id. */
private int threadId = -1;
/** Address. */
private String address = null;
/** Name. */
private String name = null;
/** Display Name (name if !=null, else address). */
private String displayName = null;
/** Used background drawable for messages. */
private int backgroundDrawableIn, backgroundDrawableOut;
private Cursor origCursor;
private static String TAG = "MessageAdapter";
/** General WHERE clause. */
/** Used text size/color. */
Contact contact;
private int textSize, textColor;
/** Convert NCR. */
private Drawable defaultContactAvatar, ownerAvatar;
private boolean convertNCR;
private Bitmap globalBitmap;
private String contactid26;
private String[] mProjection;
private Cursor mProfileCursor;
String path;
ViewHolder holder,holder1;

private static final String WHERE = "("
+ Message.PROJECTION_JOIN[Message.INDEX_TYPE] + " != "
+ Message.SMS_DRAFT + " OR "
+ Message.PROJECTION_JOIN[Message.INDEX_TYPE] + " IS NULL)";

/** WHERE clause for drafts. */
private static final String WHERE_DRAFT = "("
+ Message.PROJECTION_SMS[Message.INDEX_THREADID] + " = ? AND "
+ Message.PROJECTION_SMS[Message.INDEX_TYPE] + " = "
+ Message.SMS_DRAFT + ")";

// + " OR " + type + " = " + Message.SMS_PENDING;
String sssssss;
Contact globalcontact;
private int mLayout;
Uri uri;
public static AnimationDrawable AniFrame;
public static boolean enableHangoutAnimation=false;

public MessageAdapter(Activity _context, Uri uri) {

super(_context, R.layout.testmessage_classic_received, getCursor(
_context.getContentResolver(), uri), true);

this.defaultContactAvatar = _context.getResources().getDrawable(
R.drawable.default_avatar);
this.ownerAvatar = _context.getResources().getDrawable(
R.drawable.bubble_orange_right);
this.backgroundDrawableIn = PreferencesActivity.getBubblesIn(_context);
this.backgroundDrawableOut = PreferencesActivity
.getBubblesOut(_context);
this.textSize = PreferencesActivity.getTextsize(_context);
this.textColor = PreferencesActivity.getTextcolor(_context);
this.convertNCR = PreferencesActivity.decodeDecimalNCR(_context);

context = _context;
this.uri = uri;
if (uri == null || uri.getLastPathSegment() == null) {
this.threadId = -1;
} else {
this.threadId = Integer.parseInt(uri.getLastPathSegment());
}
Conversation conv = Conversation.getConversation(context,
this.threadId, false);
if (conv == null) {
this.address = null;
this.name = null;
this.displayName = null;
} else {
contact = conv.getContact();
this.address = contact.getNumber();
this.name = contact.getName();
this.displayName = contact.getDisplayName();
}

}

@Override
public int getViewTypeCount() {
return 2;
}

@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {

LayoutInflater mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

rowType = getItemViewType(cursor.getPosition());

if (rowType == 0) {

return mInflater.inflate(R.layout.testmessage_classic_sent, parent,
false);

} else if (rowType == 1) {

return mInflater.inflate(R.layout.testmessage_classic_received,
parent, false);
} else {
return null;
}

}

@Override
public int getItemViewType(int position) {
Cursor c = (Cursor) getItem(position);
Message m = Message.getMessage(context, c);
switch (m.getType()) {
case Message.MMS_IN: // 128
return 1;
case Message.MMS_OUT: // 132
return 0;
case Message.SMS_IN: // 2
return 1;
case Message.SMS_OUT: // 1
return 0;
default:
return 0;
}
}

public void setImageView(ImageView contactPhoto) {

mProjection = new String[] { Profile._ID, Profile.DISPLAY_NAME_PRIMARY,
Profile.LOOKUP_KEY, Profile.PHOTO_URI };

mProfileCursor = context.getContentResolver().query(
Profile.CONTENT_URI, mProjection, null, null, null);

if (mProfileCursor.moveToFirst()) {
do {

sssssss = mProfileCursor.getString(mProfileCursor
.getColumnIndex(Profile.PHOTO_URI));
if (sssssss != null) {
Uri photoUri = Uri.parse(sssssss);
contactPhoto.setImageURI(photoUri);
} else {
contactPhoto.setImageResource(R.drawable.ic_launcher);
}

} while (mProfileCursor.moveToNext());
}

}

@Override
public void bindView(View view, final Context context, Cursor cursor) {

/**
* checking starts here
*
*/

int viewType = getItemViewType(cursor.getPosition());

switch(viewType)
{
case 0:
final Message m = Message.getMessage(context, cursor);
holder = (ViewHolder) view.getTag();
if (holder == null) {

holder = new ViewHolder();
holder.tvBody = (TextView) view.findViewById(R.id.textBody);
holder.tvDate = (TextView) view.findViewById(R.id.textDate);
// holder.vRead= (View) view.findViewById(R.id.read);
Utilities.setCustomFont(context, holder.tvDate);
Utilities.setCustomFont(context, holder.tvBody);
// holder.tvDate = ( TextView ) view.findViewById( R.id.date );
holder.ivPhoto = (QuickContactBadge) view
.findViewById(R.id.imageContactPicture);
holder.btnDownload = (Button) view
.findViewById(R.id.downloadButton);
holder.media = (ImageView) view.findViewById(R.id.media);
holder.ellipse = (ImageView) view.findViewById(R.id.ellipsis);
AniFrame = (AnimationDrawable) holder.ellipse.getBackground();

view.setTag(holder);

} else {

holder = (ViewHolder) view.getTag();

}

/**
* New check starting here
*/

if (this.textSize > 0) {
holder.tvBody.setTextSize(this.textSize);
Utilities.setCustomFont(context, holder.tvBody);
}
final int col = this.textColor;
if (col != 0) {
// holder.tvPerson.setTextColor(col);
holder.tvBody.setTextColor(col);
holder.tvDate.setTextColor(col);
Utilities.setCustomFont(context, holder.tvDate);

}

int tt = PreferencesActivity
.getTextColorHackForMessageAdapter(context);
if (tt != 0) {
// holder.tvPerson.setTextColor(tt);
holder.tvBody.setTextColor(tt);
holder.tvDate.setTextColor(tt);
}
Conversation conv1 = Conversation.getConversation(context,
this.threadId, false);
if (conv1 == null) {
this.address = null;
this.name = null;
this.displayName = null;
} else {
contact = conv1.getContact();
this.address = contact.getNumber();
this.name = contact.getName();
this.displayName = contact.getDisplayName();
}
CharSequence text = m.getBody();
holder.tvBody.setText(text);

// unread / read
/*
* if ( m.getRead() == 0 ) { holder.vRead.setVisibility(
* View.VISIBLE ); } else { holder.vRead.setVisibility(
* View.INVISIBLE ); }
*/

int t = m.getType();
String subject = m.getSubject();
if (subject == null) {
subject = "";
} else {
subject = ": " + subject;
}

final long time = m.getDate();
holder.tvDate.setText(Utilities.getDate(context, time));

/**
* Adding codes for mms
*
*/

final Bitmap pic = m.getPicture();
if (pic != null) {
if (pic == Message.BITMAP_PLAY) {
holder.media.setImageResource(R.drawable.mms_play_btn);
} else {
holder.media.setImageBitmap(pic);
}
holder.media.setVisibility(View.VISIBLE);
final Intent i = m.getContentIntent();
holder.media.setOnClickListener(SMSdroid
.getOnClickStartActivity(context, i));
holder.media.setOnLongClickListener(m
.getSaveAttachmentListener(context));
} else {
holder.media.setVisibility(View.GONE);
holder.media.setOnClickListener(null);
}

// CharSequence text = m.getBody();
if (text == null && pic == null) {
final Button btn = holder.btnDownload;
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
try {
Intent i = new Intent();
i.setClassName("com.android.mms",
"com.android.mms.transaction.TransactionService");
i.putExtra("uri", m.getUri().toString());
i.putExtra("type", 1);
ComponentName cn = context.startService(i);
if (cn != null) {
btn.setEnabled(false);
btn.setText(R.string.downloading_);
} else {
i = new Intent(Intent.ACTION_VIEW, Uri
.parse(MainActivity.URI
+ m.getThreadId()));
context.startActivity(Intent.createChooser(i,
context.getString(R.string.view_mms)));
}
} catch (SecurityException e) {
Log.e(TAG, "unable to start mms download", e);
Toast.makeText(context,
R.string.error_start_mms_download,
Toast.LENGTH_LONG).show();
}
}
});
holder.btnDownload.setVisibility(View.VISIBLE);
holder.btnDownload.setEnabled(true);
} else {
// testing hiding this code
// holder.btnDownload.setVisibility(View.GONE);
}

/**
* New check Ends here
*/

if (MainActivity.showContactPhoto) {

holder.ivPhoto.setImageDrawable(contact.getAvatar(this.context,
this.defaultContactAvatar));
holder.ivPhoto.setVisibility(View.VISIBLE);
holder.ivPhoto.setOnClickListener(WRAPPER.getQuickContact(
context, holder.ivPhoto,
contact.getLookUpUri(context.getContentResolver()), 2,
null));

setImageView(holder.ivPhoto);

} else {
holder.ivPhoto.setVisibility(View.GONE);
}

break;
case 1:

final Message m1 = Message.getMessage(context, cursor);
holder1 = (ViewHolder) view.getTag();
if (holder1 == null) {

holder1 = new ViewHolder();
holder1.tvBody = (TextView) view.findViewById(R.id.textBody);
holder1.tvDate = (TextView) view.findViewById(R.id.textDate);
// holder.vRead= (View) view.findViewById(R.id.read);
Utilities.setCustomFont(context, holder1.tvDate);
Utilities.setCustomFont(context, holder1.tvBody);
// holder.tvDate = ( TextView ) view.findViewById( R.id.date );
holder1.ivPhoto = (QuickContactBadge) view
.findViewById(R.id.imageContactPicture);
holder1.btnDownload = (Button) view
.findViewById(R.id.downloadButton);
holder1.media = (ImageView) view.findViewById(R.id.media);
holder1.ellipse = (ImageView) view.findViewById(R.id.ellipsis);

view.setTag(holder1);

} else {

holder1 = (ViewHolder) view.getTag();

}

if (MainActivity.showContactPhoto) {

holder1.ivPhoto.setImageDrawable(contact.getAvatar(this.context,
this.defaultContactAvatar));
holder1.ivPhoto.setVisibility(View.VISIBLE);
holder1.ivPhoto.setOnClickListener(WRAPPER.getQuickContact(
context, holder1.ivPhoto,
contact.getLookUpUri(context.getContentResolver()), 2,
null));

holder1.ivPhoto.setImageDrawable(contact.getAvatar(
this.context, this.defaultContactAvatar));

} else {
holder1.ivPhoto.setVisibility(View.GONE);
}

/**
* New check starting here
*/

if (this.textSize > 0) {
holder1.tvBody.setTextSize(this.textSize);
Utilities.setCustomFont(context, holder1.tvBody);
}
final int col1 = this.textColor;
if (col1 != 0) {
// holder.tvPerson.setTextColor(col);
holder1.tvBody.setTextColor(col1);
holder1.tvDate.setTextColor(col1);
Utilities.setCustomFont(context, holder1.tvDate);

}

int tt1 = PreferencesActivity
.getTextColorHackForMessageAdapter(context);
if (tt1 != 0) {
// holder.tvPerson.setTextColor(tt);
holder1.tvBody.setTextColor(tt1);
holder1.tvDate.setTextColor(tt1);
}
Conversation conv11 = Conversation.getConversation(context,
this.threadId, false);
if (conv11 == null) {
this.address = null;
this.name = null;
this.displayName = null;
} else {
contact = conv11.getContact();
this.address = contact.getNumber();
this.name = contact.getName();
this.displayName = contact.getDisplayName();
}
CharSequence text1 = m1.getBody();
holder1.tvBody.setText(text1);

// unread / read
/*
* if ( m.getRead() == 0 ) { holder.vRead.setVisibility(
* View.VISIBLE ); } else { holder.vRead.setVisibility(
* View.INVISIBLE ); }
*/

int t1 = m1.getType();
String subject1 = m1.getSubject();
if (subject1 == null) {
subject1 = "";
} else {
subject1 = ": " + subject1;
}

final long time1= m1.getDate();
holder1.tvDate.setText(Utilities.getDate(context, time1));

/**
* Adding codes for mms
*
*/

final Bitmap pic1 = m1.getPicture();
if (pic1 != null) {
if (pic1 == Message.BITMAP_PLAY) {
holder1.media.setImageResource(R.drawable.mms_play_btn);
} else {
holder1.media.setImageBitmap(pic1);
}
holder1.media.setVisibility(View.VISIBLE);
final Intent i = m1.getContentIntent();
holder1.media.setOnClickListener(SMSdroid
.getOnClickStartActivity(context, i));
holder1.media.setOnLongClickListener(m1
.getSaveAttachmentListener(context));
} else {
holder1.media.setVisibility(View.GONE);
holder1.media.setOnClickListener(null);
}

// CharSequence text = m.getBody();
if (text1 == null && pic1 == null) {
final Button btn = holder1.btnDownload;
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
try {
Intent i = new Intent();
i.setClassName("com.android.mms",
"com.android.mms.transaction.TransactionService");
i.putExtra("uri", m1.getUri().toString());
i.putExtra("type", 1);
ComponentName cn = context.startService(i);
if (cn != null) {
btn.setEnabled(false);
btn.setText(R.string.downloading_);
} else {
i = new Intent(Intent.ACTION_VIEW, Uri
.parse(MainActivity.URI
+ m1.getThreadId()));
context.startActivity(Intent.createChooser(i,
context.getString(R.string.view_mms)));
}
} catch (SecurityException e) {
Log.e(TAG, "unable to start mms download", e);
Toast.makeText(context,
R.string.error_start_mms_download,
Toast.LENGTH_LONG).show();
}
}
});
holder1.btnDownload.setVisibility(View.VISIBLE);
holder1.btnDownload.setEnabled(true);
} else {
// testing hiding this code
// holder.btnDownload.setVisibility(View.GONE);
}

/**
* New check Ends here
*/

break;

}

}

/** View holder. */
public static class ViewHolder {
public static ImageView ellipse;
TextView tvBody;
TextView tvPerson;
TextView tvDate;
ImageView media;
// View vRead;
public View vPending;
public View vLayout;
public ImageView ivInOut;
public Button btnDownload;
public Button btnImport;

QuickContactBadge ivPhoto;

}
/** View holder. */
public static class ViewHolder1 {
public static ImageView ellipse;
TextView tvBody;
TextView tvPerson;
TextView tvDate;
ImageView media;
// View vRead;
public View vPending;
public View vLayout;
public ImageView ivInOut;
public Button btnDownload;
public Button btnImport;

QuickContactBadge ivPhoto;

}

/**
* Get the {@link Cursor}.
*
* @param cr
* {@link ContentResolver}
* @param u
* {@link Uri}
* @return {@link Cursor}
*/
private static Cursor getCursor(final ContentResolver cr, final Uri u) {
Log.d(TAG, "getCursor(" + u + ")");
final Cursor[] c = new Cursor[] { null, null };

int tid = -1;
try {
tid = Integer.parseInt(u.getLastPathSegment());
} catch (Exception e) {
Log.e(TAG, "error parsing uri: " + u, e);
}

try {
Log.d(TAG, "where: " + WHERE);
c[0] = cr.query(u, Message.PROJECTION_JOIN, WHERE, null, null);
} catch (NullPointerException e) {
Log.e(TAG, "error query: " + u + " / " + WHERE, e);
c[0] = null;
} catch (SQLiteException e) {
Log.e(TAG, "error getting messages", e);
}

final String[] sel = new String[] { String.valueOf(tid) };
try {
Log.d(TAG, "where: " + WHERE_DRAFT + " / sel: " + sel);
c[1] = cr.query(Uri.parse("content://sms/"),
Message.PROJECTION_SMS, WHERE_DRAFT, sel, Message.SORT_USD);
} catch (NullPointerException e) {
Log.e(TAG, "error query: " + u + " / " + WHERE_DRAFT + " sel: "
+ sel, e);
c[1] = null;
} catch (SQLiteException e) {
Log.e(TAG, "error getting drafts", e);
}

if (c[1] == null || c[1].getCount() == 0) {
return c[0];
}


Related Topics



Leave a reply



Submit