ListView for messaging app shows wrong listItem layout after scrolling

笑着哭i 提交于 2020-01-03 14:17:19

问题


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

            }
        }

回答1:


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




回答2:


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)




回答3:


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



回答4:


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.




回答5:


Whether this is a good practice or not, Removing if (v == null) will solve the problem. Anyway, you will have to re-inflate the view.



来源:https://stackoverflow.com/questions/20476903/listview-for-messaging-app-shows-wrong-listitem-layout-after-scrolling

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!