【问题标题】:android listview displays false data after scrolling (custom adapter)android listview 滚动后显示虚假数据(自定义适配器)
【发布时间】:2012-08-15 15:53:51
【问题描述】:

我遇到了一个让我发疯的奇怪问题。在我的 android 应用程序中,我定制了我自己的从 ArrayAdapter 扩展的适配器。我添加适配器的 ListView 的项目可以是标记文本(不可编辑)、可编辑文本或微调器。 疯狂的是:当我滚动 ListView 时,有两个问题:

(1) 微调器项目中显示的(选定的) 更改 有时虽然我只是滚动!当我单击微调器时,仍然显示旧的选定值(应该由微调器显示的那个) (2) 当我滚动时,ListViewItems 顺序会发生变化

=> 但是适配器中的数据没有改变(数据本身和顺序都没有) - 所以它一定是视图本身的问题?!也许android在后台缓存并且没有尽快刷新ListViewItems或类似的东西?!

有人可以帮帮我吗?

非常感谢!

好的,我找到了一个不是很好的解决方案,但它确实有效。我只是不再使用 convertView 了,尽管这在内存和性能方面不是最理想的。在我的情况下应该没问题,因为我的 ListView 的最大项目数量是 15。 这是我的适配器类:

 public class FinAdapter extends ArrayAdapter<Param>{
    public Param[] params;
    private boolean merkzettel;
    protected EditText pv;

    public FinAdapter(Context context, int textViewResourceId, Param[] items, boolean merkzettel) {
        super(context, textViewResourceId, items);
        this.params = items;
        this.merkzettel = merkzettel;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final Param p = params[position];
        if(p != null){
            if(merkzettel){
                if (convertView == null) {
                    LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    convertView = vi.inflate(R.layout.merk_det_item, null);
                }
                TextView tvl = (TextView) convertView.findViewById(R.id.paramM);
                TextView edl = (TextView) convertView.findViewById(R.id.param_valueM);
                TextView pal = (TextView) convertView.findViewById(R.id.param_unitM);
                if (tvl != null) {
                    tvl.setText(p.getName());                            
                }
                if(pal != null){
                    pal.setText(p.getUnit());
                }
                if(edl != null){
                    edl.setText(p.getDefData());
                }
            }
            else{
                if(p.isSelect()){

                if (convertView == null) {
                    LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    convertView = vi.inflate(R.layout.fin_prod_list_item_select, null);
                }            
                TextView tvs = (TextView) convertView.findViewById(R.id.paramS);
                Spinner sp = (Spinner) convertView.findViewById(R.id.spinner_kalk);
                TextView paU = (TextView) convertView.findViewById(R.id.param_unitS);


                if (tvs != null) {
                    tvs.setText(p.getName());                            
                }
                if(paU != null){
                    paU.setText(p.getUnit());
                }
                if(sp != null){
                    String[] values = new String[p.getData().size()];
                    for(int i=0; i<values.length; i++){
                        values[i] = p.getData().get(i);
                    }
                    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this.getContext(), android.R.layout.simple_spinner_item, values);
                    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
                    sp.setAdapter(adapter);
                    sp.setSelection(p.getData().indexOf(p.getDefData()));
                    sp.setOnItemSelectedListener(new OnItemSelectedListener(){
                        public void onItemSelected(AdapterView<?> parent,
                                View convertView, int pos, long id) {
                            p.setDefData(p.getData().get(pos));
                            p.setChanged(true);
                        }
                        public void onNothingSelected(AdapterView<?> arg0) {
                            // TODO Auto-generated method stub

                        }

                    });
                }
            }       
            else if(p.isEdit()){
                if (convertView == null) {
                    LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    convertView = vi.inflate(R.layout.fin_prod_list_item_edit, null);
                }
                TextView pa = (TextView) convertView.findViewById(R.id.param);
                pv = (EditText) convertView.findViewById(R.id.param_value);
                TextView paE = (TextView) convertView.findViewById(R.id.param_unit);
                if (pa != null) {
                    pa.setText(p.getName());                            
                }
                if(paE != null){
                    paE.setText(p.getUnit());
                }
                if(pv != null){
                    pv.setText(p.getDefData());
                    pv.setOnEditorActionListener(new OnEditorActionListener(){
                        public boolean onEditorAction(TextView convertView, int actionId,
                                KeyEvent event) {
                            // TODO Auto-generated method stub
                            p.setDefData(pv.getText().toString());
                            p.setChanged(true);
                            return false;
                        }                   
                    }); 
                }

            }
            else if(p.isLabel()){
                if (convertView == null) {
                    LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    convertView = vi.inflate(R.layout.fin_prod_list_item_label, null);
                }
                TextView tvl = (TextView) convertView.findViewById(R.id.paramL);
                TextView edl = (TextView) convertView.findViewById(R.id.param_valueL);
                TextView pal = (TextView) convertView.findViewById(R.id.param_unitL);
                if (tvl != null) {
                    tvl.setText(p.getName());                            
                }
                if(pal != null){
                    pal.setText(p.getUnit());
                }
                if(edl != null){
                    edl.setText(p.getDefData());
                }   
            }}
        }
        return convertView;
    }
}

【问题讨论】:

标签: android listview scroll adapter


【解决方案1】:

我对列表中的多个项目类型有类似的问题。在我的例子中,列表项要么是一个部分(标签)项,要么是一个常见的列表项。

要使用此类列表,您应该重写 getViewTypeCountgetItemViewType 方法。 像这样的:

private static final int ITEM_VIEW_TYPE_ITEM = 0;
private static final int ITEM_VIEW_TYPE_SEPARATOR = 1;

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

@Override
public int getItemViewType(int position) {
    return this.getItem(position).isSection() ? ITEM_VIEW_TYPE_SEPARATOR : ITEM_VIEW_TYPE_ITEM;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    final Item item = this.getItem(position);

    if (convertView == null) {
        convertView = mInflater.inflate(item.isSection() ? R.view1 : R.view2, null);
    }

    if(item.isSection()){
        //...
    }
    else{
        //...
    }

    return convertView;
}

那么 convertView 参数将始终正确并包含您需要的类型。

还有一件事:当您已经在基类ArrayAdapter&lt;Param&gt; 中拥有public Param[] params 字段时,您显式添加了它。

我建议从 BaseAdapter 类继承。

编辑: 以下是您可以尝试使用的代码,以使您的 Spinner 工作:

sp.setTag(p);
sp.setOnItemSelectedListener(new OnItemSelectedListener(){
public void onItemSelected(AdapterView<?> parent, View convertView, int pos, long id) {
    Param currentItem = (Param)parent.getTag();
    currentItem.setDefData(currentItem.getData().get(pos));
    currentItem.setChanged(true);
}
//...

尝试结合使用getTagsetTag 方法。我记得我在事件处理程序和最终变量方面也有类似的问题,但我完全忘记了它们的原因,所以我无法准确解释为什么会发生这种情况。

【讨论】:

  • 听起来很棒 - 我会在星期一试一试,并提供反馈,无论它是否适合我。到目前为止非常感谢你=)
  • 好的,我试过了。它适用于我的列表项的排序 - 它们不再混合。但我仍然有问题,我的微调器作为列表项在滚动后显示其项目的第一个值尽管我选择了另一个项目(不是第一个)。例如,如果我有一个 ListViewItem,它是一个以 {1,2,3} 作为项目的 Spinner。假设选择了“1”。现在我选择“2”并向下滚动我的 ListView。再次向上滚动后,微调器显示“1”而不是“2”。但是如果我点击 Spinner 来选择另一个值,它会显示选择了“2”......为什么?
  • @user1384200 您说的是第二个“if”块中的 Spinner?我认为原因在于代码p.setDefData,您在事件处理程序中使用了外部变量p。虽然它被标记为final,但这些变量并没有按预期工作,我遇到了很多问题。您应该使用调试器检查它,将断点放在事件处理程序中并查看p 的值。我现在将编辑我的回复,并展示你可以用这个变量做什么。
  • 我按照你说的调试了。 defData 参数设置正确 - 所以我不认为这会导致我的问题。这对于 Spinner 在单击它时显示正确的项目是有意义的(这意味着在带有 Radiobuttons 的列表中,其中显示了要选择的可能值,尽管当您只查看时它没有显示为选中状态微调器(未展开)。
  • humph... currentItem 为空,尽管设置正确
【解决方案2】:

正如 Sam 所说,Android 系统总是尽可能地尝试回收以节省资源。 这意味着,开发人员需要自己跟踪ListView 上的View。 对于实体,或者所谓的表示数据结构,你可以有一些东西来跟踪选择/取消选择的状态,例如:

class PresentationData {
    // other stuffs..
    boolean isSelected = false;
}

将这些数据结构映射到Adapter,如果有任何项目被点击,则将状态设置为true:listPresentationData.get(selected_position).isSelected = true

好的,所以在适配器的getView() 中,为您的数据正确跟踪演示。

if(listPresentationData.get(position).isSelected) 
{ 
    // set background color of the row layout..or something else 
}

另外,值得一提的是,最好为每个视图都有一个内存缓存,通常称为ViewHolder 以提高性能。

【讨论】:

  • 感谢您的回答!当一个项目被点击以及如何处理它时,我没有问题。这已实施并且工作正常。我的问题是,当我滚动时 ListView 显示的数据在我的适配器中没有像这样表示。例如,当我滚动时,Item1 突然显示在 ListView 的末尾,尽管它在我滚动之前位于顶部。
  • 哦,如果是你说的那样,物品的顺序显示在错误的位置;则表示Param[] items(在构造函数中),items 的原始数组已在某处更改;因为ListView默认会按照items数组中存储的顺序显示项目。
  • 不,数组没有改变。在阅读了不同的论坛后,我想问题一定是适配器类中的 getView() 方法。我遇到了与此处所述类似的问题:link
【解决方案3】:

正如大家所说,Android 回收视图。那是什么意思?想象一下,您的列表中有 10 个项目,但屏幕上只显示了 3 个项目。向下滚动时,第一个将消失,而第四个将出现。第四个项目已经在 Android 系统中,但第五个是从第一个项目创建的,使用相同的视图。 就我而言,我有一个带有默认背景的 TextView。但是,如果满足某些条件,我会更改背景。

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ...
    if (someCondition) {
        holder.textView.setBackground(R.drawable.different_background)
    }
    ...
}

问题是如果不满足条件,我不会设置回默认背景(因为我认为视图是为每个项目创建的)。当第一项条件为真时出现问题,因此回收视图与 different_background 一起使用,而不是默认背景。为了避免这个问题,我必须始终设置条件的两个分支并设置默认值,如下所示:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ...
    if (someCondition) {
        holder.textView.setBackground(R.drawable.different_background)
    } else {
        holder.textView.setBackground(R.drawable.default_background)
    }
    ...
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多