【问题标题】:ListView for messaging app shows wrong listItem layout after scrolling消息应用程序的 ListView 在滚动后显示错误的 listItem 布局
【发布时间】:2013-12-09 17:34:01
【问题描述】:

我知道stackoverflow上已经贴了很多类似的问题,所以请不要以为我没有搜索过高低。我认为我的问题只是来自现在完全理解 listViews 和列表项的生命周期。我有一个列表视图,可以包含两种类型的消息,出站或入站。最初,我的 listView 会根据消息的类型(出站与入站)使用不同的背景颜色,并且它完美地工作。现在我的应用程序不需要为列表项设置不同的背景,但它实际上需要为不同的列表项设置不同的布局。

这是我的适配器的剪辑。

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

            }
        }

【问题讨论】:

    标签: java android listview android-listview


    【解决方案1】:

    适配器可以支持不同的 ViewItemType,从而解决您的回收问题。

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

    您无需担心回收视图,因为列表视图将尊重每个位置的 ViewItemTypes,并且它只会为该位置提供正确视图类型的转换视图

    【讨论】:

    • 跟着这个,但仍然没有运气。我动态添加的视图(稍后在 getView() 中添加的视图仍然混乱。它们出现了,但在我开始滚动后消失了。
    • 动态添加的视图与使用两种不同的布局是不同的问题。您基本上有两条路径.. 1. 您可以忽略传入的 convertview 并始终创建一个适合项目位置的视图。 2.您可以使用回收视图,但您必须删除或更正之前添加的任何内容。回收视图就像教科书一样,您必须擦除以前的所有者(项目)笔记才能得到一本干净的书。
    • 我想我想走你提到的第一条路线。所以我需要: 1. 为我的列表项设置正确的布局(入站 v 出站) 2. 在适用的情况下添加我的所有动态视图 3. 完成了吗?
    • getView 有两个阶段。首先是决定要膨胀的布局,该部分在 if(v==null){} 范围内;第二个是用特定于项目的数据填充视图。如果您在第二阶段根据项目状态添加视图,那么您将必须检查它们并根据项目状态将它们拉出。使用 ItemViewTypes 允许您使用不同的布局,因此您不必动态添加视图。只需更改其中的图像和文本。
    【解决方案2】:

    问题在于 listview 正在回收视图,因此当您检查视图是否为空时,它不会通过,因为视图在回收时不为空

    每次调用 getView 时都需要对视图进行膨胀,基本上是删除 if(v == null)

    【讨论】:

    • 呵呵……我犯了这么简单的错误。非常感谢您注意到这一点。您是否建议我采取其他措施来确保性能达到最佳?
    • 实际上,简单地删除 if 语句就可以解决布局问题,但会给我稍后在适配器中动态添加的其他视图带来更多问题。想法?
    【解决方案3】:

    尝试像这样使用 ViewHolder:

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

    【讨论】:

    • 不会 convertView 总是为空...使 else{ holder = (ViewHolder) convertView.getTag(); } 无用?
    • @GuyLeStack 如果你正确地覆盖了getViewTypeCountgetItemViewType 方法,它就不会为空。
    • 但是,答案的第二行是convertView = null;
    • 那么你试过了吗@GuyLeStack?
    • 还没有。我认为这种方法可以正常工作。投票最多的答案也成功了,但我必须等待才能选择正确的答案。
    【解决方案4】:

    这是因为正在发生的回收。你需要一些类似的东西:

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

    另外,请记住始终在 getView 方法中重置或初始化一个值。由于 View 可以回收利用,因此它可能会携带其前世的特性。

    【讨论】:

    • 我遵循@tyczj 解决方案,它修复了布局问题,但给我稍后在适配器中动态添加的其他视图带来了更多问题。想法?
    • 我不认为删除 if (v == null) 是一个好的解决方案。至少,这不是首选解决方案。现在阅读您的问题,我知道您要做什么,并且要使其正常工作,您需要实施更多方法。请看这个答案:stackoverflow.com/questions/4777272/…
    【解决方案5】:

    无论这是否是一个好习惯,删除if (v == null) 将解决问题。 无论如何,您将不得不重新膨胀视图。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-11
      • 2018-06-14
      • 1970-01-01
      • 1970-01-01
      • 2012-12-25
      • 1970-01-01
      相关资源
      最近更新 更多