I know that many similar questions have been posted on stackoverflow, so please don't think I haven't searched high and low. I think my problems simply comes from now completely understanding listViews and the lifecycles of list items. I have a list view that can contain two types of messages, outbound or inbound. Originally, my listView would use a different background color depending on the type of message (outbound vs inbound), and it worked flawlessly. Now my application doesn't require a different background for list items, but it actually requires different layouts for different list items.
This is a clip of my Adapter.
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
SoapBoxMessage thisMessage = messages.get(position);
if (v == null) {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (thisMessage.isOutbound()) {
v = inflater.inflate(R.layout.outbound_row, null);
} else {
v = inflater.inflate(R.layout.inbound_row, null);
}
}
Adapters can support different ViewItemTypes that will solve your recycling problems.
static public enum LAYOUT_TYPE {
INBOUND,
OUTBOUND
}
@Override
public int getViewTypeCount () {
return LAYOUT_TYPE.values().length;
}
@Override
public int getItemViewType (int position) {
if ( messages.get(position).isOutbound())
return LAYOUT_TYPE.OUTBOUND.ordinal();
else
return LAYOUT_TYPE.INBOUND.ordinal();
}
@Override
public View getView (int position, View convertView, ViewGroup parent) {
LAYOUT_TYPE itemType = LAYOUT_TYPE.values()[getItemViewType(position)];
... (code until inflater )
switch (itemType){
case INBOUND:
convertview = /inflate & configure inbound layout
break;
case OUTBOUND:
convertview = /inflate & configure outbound layout
break;
}
you don't need to worry about recycling views because the listview will respect the ViewItemTypes for each position and it will only provide a convertview of the correct viewtype for that position
The problem is that listview is recycling the view so when you check if the view is null it wont pass that because the view is not null when it is recycled
you would need to inflate the view each time getView is called, basically removing if(v == null)
Try to use a ViewHolder like this:
ViewHolder holder;
public View getView(int position, View convertView, ViewGroup parent) {
convertView = null;
SoapBoxMessage thisMessage = messages.get(position);
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (thisMessage.isOutbound()) {
convertView = inflater.inflate(R.layout.outbound, null, false);
//specific to your outbound layout
holder = new ViewHolder();
holder.text= (TextView)convertView.findViewById(R.id.textview);
holder.group = (RadioGroup)convertView.findViewById(R.id.toggleGroup);
holder.toggle = (ToggleButton)convertView.findViewById(R.id.toggleButton);
} else {
convertView = inflater.inflate(R.layout.inbound, null, false);
//specific to your inbound layout
holder = new ViewHolder();
holder.text= (TextView)convertView.findViewById(R.id.textview);
holder.group = (RadioGroup)convertView.findViewById(R.id.toggleGroup);
holder.toggle = (ToggleButton)convertView.findViewById(R.id.toggleButton);
}
convertView.setTag(holder);
}
else{
holder = (ViewHolder) convertView.getTag();
}
//Here you can set the text or other code you want to implement
holder.text.setText("Whatever!");
return convertView;
}
static class ViewHolder {
//TODO put components you use like:
TextView text;
RadioGroup group;
ToggleButton toggle;
}
It's because of the recycling that is happening. You would need something along these lines:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
SoapBoxMessage thisMessage = messages.get(position);
if (convertView == null) {
LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.listview_feedlog_item, null);
holder = new ViewHolder();
holder.txtTime = (TextView) convertView.findViewById(R.id.textTime);
holder.txtDate = (TextView) convertView.findViewById(R.id.textDate);
convertView.setTag(holder);
} else
holder = (ViewHolder) convertView.getTag();
// I don't know how your SoapBoxMessage is made up so here are two sample methods
holder.txtTime.setText(thisMessage.getTime());
holder.txtDate.setText(thisMessage.getDate());
return convertView;
}
/* private view holder class */
private class ViewHolder {
TextView txtTime;
TextView txtDate;
}
Also, remember to always reset or initiate a value in the getView
method. Since a View
can be recycled it might carry with it properties of its former life.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With