当前位置: 首页 > 知识库问答 >
问题:

ListView的回收机制如何工作

楮庆
2023-03-14

所以我以前遇到过这个问题,很自然地我在这里寻求帮助。Luksprog的回答很棒,因为我不知道ListView和GridView是如何通过回收视图来优化自己的。所以在他的建议下,我能够改变我向GridView添加视图的方式。问题是现在我有了一些没有意义的东西。这是我的getView来自我的BaseAdapter

public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.day_view_item, parent, false);
        }
        Log.d("DayViewActivity", "Position is: "+position);
        ((TextView)convertView.findViewById(R.id.day_hour_side)).setText(array[position]);
        LinearLayout layout = (LinearLayout)convertView.findViewById(R.id.day_event_layout);

        //layout.addView(new EventFrame(parent.getContext()));

        TextView create = new TextView(DayViewActivity.this);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 62, getResources().getDisplayMetrics()), 1.0f);
        params.topMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        params.bottomMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        create.setLayoutParams(params);
        create.setBackgroundColor(Color.BLUE);
        create.setText("Test"); 
        //the following is my original LinearLayout.LayoutParams for correctly setting the TextView Height
        //new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60, getResources().getDisplayMetrics()), 1.0f)   
        if(position == 0) {
            Log.d("DayViewActivity", "This should only be running when position is 0. The position is: "+position);
            layout.addView(create);
        }

        return convertView;
    }

}

问题是当我滚动时,这会发生,而不是在位置0上。。。看起来像是第6位和第8位,加上第8位有两个。现在我仍在尝试使用ListView和GridView,所以我不明白为什么会发生这种情况。我提出这个问题的主要原因之一是为了帮助其他人,他们可能不知道ListView和GridView的回收视图,或者本文所说的ScrapView机制。

稍后编辑

添加链接到谷歌输入输出谈话中,这基本上是你理解列表视图如何工作所需要的。链接就在评论中。所以用户3427079很好地更新了这个链接。这里是为了方便访问。

共有3个答案

孟浩然
2023-03-14

使用Holder模式,您可以实现您想要的:

您可以在此处找到此模式的描述:

当您向下滚动屏幕并且上面的列表视图项被隐藏时,列表视图的回收就会发生。它们被重用来显示新的列表视图项。

司徒钱青
2023-03-14

注意,在Holder模式中,如果您在Holder对象中设置了位置,您应该每次都设置它,例如:

@Override
public final View getView(int position, View view, ViewGroup parent) {
    Holder holder = null;
    if (view == null) {
        LayoutInflater inflater = (LayoutInflater) App.getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(getContainerView(), parent, false);
        holder = getHolder(position, view, parent);
        holder.setTag(tag);
        view.setTag(holder);
    } else {
        holder = (Holder) view.getTag();
    }
    holder.position = position;
    draw(holder);
    return holder.getView();
}

这是一个抽象类的示例,其中

getHolder(position, view, parent);

执行所有的设置操作

ImageViews, TextViews, etc..
上官树
2023-03-14

每当listview被适配器填充时,它基本上会显示listview可以在屏幕上显示的行数,即使滚动列表,行数也不会增加。这就是Android使用的技巧,让listview更高效、更快地工作。现在,listview的内部故事引用了图像,正如您所看到的,listview最初有7个可见项,然后,如果您向上滚动直到项1不再可见,getView()将此视图(即项1)传递给回收商,您可以使用它

System.out.println("getview:"+position+" "+convertView);

在你的

public View getView(final int position, View convertView, ViewGroup parent)
{
    System.out.println("getview:"+position+" "+convertView);
    ViewHolder holder;
    View row=convertView;
    if(row==null)
    {
        LayoutInflater inflater=((Activity)context).getLayoutInflater();
        row=inflater.inflate(layoutResourceId, parent,false);
        
        holder=new PakistaniDrama();
        holder.tvDramaName=(TextView)row.findViewById(R.id.dramaName);
        holder.cbCheck=(CheckBox)row.findViewById(R.id.checkBox);
        
        row.setTag(holder);
        
    }
    else
    {
        holder=(PakistaniDrama)row.getTag();
    }
            holder.tvDramaName.setText(dramaList.get(position).getDramaName());
    holder.cbCheck.setChecked(checks.get(position));
            return row;
    }

你会注意到在你的logcat中,最初,所有可见的行都是空的,因为最初在回收器中没有视图(即项目),所以你的getView()为每个可见的项目创建一个新视图,但是当你向上滚动并且项目1移出屏幕时,它将被发送到回收器以其当前状态(例如TextView的“文本”,或者在我的情况下,如果选中复选框,它将与视图相关联并存储在回收器中)。

现在,当你向上/向下滚动时,你的listview不会创建一个新的视图,它将使用回收器中的视图。在您的Logcat中,您会注意到“convertView”不是空的,因为您的新项目8将使用convertView绘制,也就是说,基本上它从回收商处获取项目1视图,并在其位置对项目8进行充气,您可以在我的代码中观察到这一点。如果您有一个复选框,并且您在位置0处选中它(假设item1有一个复选框,您选中了它),那么当您向下滚动时,您将看到项目8复选框已经选中,这就是为什么listview重新使用同一视图,而不是因为性能优化而为您创建新视图的原因。

重要的事情

1.切勿将listview的layout_heightlayout_width设置为wrap_content,因为getView()将迫使适配器获取一些子对象,用于测量列表视图中要绘制的视图的高度,并且可能会导致一些意外行为,例如返回convertview,即使列表未滚动。始终使用match_parent或固定宽度/高度。

2.如果你想在列表视图之后使用一些布局或视图,如果我将Layout\u height设置为fill\u parent则列表视图之后的视图将不会在屏幕下方显示,因此最好将列表视图放在布局中。例如,线性布局,根据您的要求设置该布局的高度和宽度,并将listview的高度和宽度属性设置为布局的高度和宽度(例如,如果布局宽度为320,高度为280),则listview的高度和宽度应相同。这将告诉getView()要渲染的视图的确切高度和宽度,getView()不会一次又一次地调用一些随机行,并且不会出现其他问题,比如在滚动之前返回convert view,我已经自己进行了测试,除非我的listview在LinearLayout中,否则它也会遇到一些问题,比如重复视图调用和将视图转换为,将listview放在LinearLayout中对我来说就像魔术一样。(不知道为什么)

01-01 14:49:36.606: I/System.out(13871): getview 0 null
01-01 14:49:36.636: I/System.out(13871): getview 0 android.widget.RelativeLayout@406082c0
01-01 14:49:36.636: I/System.out(13871): getview 1 android.widget.RelativeLayout@406082c0
01-01 14:49:36.646: I/System.out(13871): getview 2 android.widget.RelativeLayout@406082c0
01-01 14:49:36.646: I/System.out(13871): getview 3 android.widget.RelativeLayout@406082c0
01-01 14:49:36.656: I/System.out(13871): getview 4 android.widget.RelativeLayout@406082c0
01-01 14:49:36.666: I/System.out(13871): getview 5 android.widget.RelativeLayout@406082c0
01-01 14:49:36.666: I/System.out(13871): getview 0 android.widget.RelativeLayout@406082c0
01-01 14:49:36.696: I/System.out(13871): getview 0 android.widget.RelativeLayout@406082c0
01-01 14:49:36.706: I/System.out(13871): getview 1 null
01-01 14:49:36.736: I/System.out(13871): getview 2 null
01-01 14:49:36.756: I/System.out(13871): getview 3 null
01-01 14:49:36.776: I/System.out(13871): getview 4 null

但是现在它解决了,我知道,我不擅长解释,但是当我花了一整天去理解的时候,我想像我一样的其他初学者可以从我的经验中得到帮助,我希望现在你们会对ListView框架的工作原理有一点了解,因为它真的很混乱和棘手,所以初学者发现理解它有太多的问题

 类似资料:
  • 本文向大家介绍JS的垃圾回收机制?相关面试题,主要包含被问及JS的垃圾回收机制?时的应答技巧和注意事项,需要的朋友参考一下 参考回答: GC(garbage collection),GC执行时,中断代码,停止其他操作,遍历所有对象,对于不可访问的对象进行回收,在V8引擎中使用两种优化方法, 分代回收,2、增量GC,目的是通过对象的使用频率,存在时长来区分新生代和老生代对象,多回收新生代区,少回收老

  • 本文向大家介绍如何快速理解python的垃圾回收机制,包括了如何快速理解python的垃圾回收机制的使用技巧和注意事项,需要的朋友参考一下 一、先来说说为什么要有垃圾回收 解释器在执行到定义变量得语法时,会申请内存空间来存放变量得值,但是由于内存空间是有限得,所以这就涉及到了内存回收问题了,当一个变量值没有用了(简称垃圾),这种时候就应该回收掉这个变量值得内存空间。 二、那么什么是垃圾回收机制 垃

  • 本文向大家介绍Python垃圾回收机制?相关面试题,主要包含被问及Python垃圾回收机制?时的应答技巧和注意事项,需要的朋友参考一下 引用计数 标记清除 分代回收  

  • 问题内容: 描述为: “共享秘密”的存储库,这是一种在不使用反射的情况下在另一个程序包中调用实现私有方法的机制。package- private类实现了一个公共接口,并提供了在该包内调用package- private方法的能力。实现该接口的对象是通过限制访问的第三包提供的。该框架避免了为此目的使用反射的主要缺点,即损失了编译时检查。 有人可以提供一个示例来说明此机制如何使一个包中的类访问另一个包

  • 问题内容: JavaScript中的垃圾回收如何工作?它类似于.NET垃圾回收吗?难道是因为人们在VBScript中实现垃圾回收很不好,所以人们避免了垃圾回收并建立了对JavaScript作为其标准客户端语言的偏好? 问题答案: 垃圾收集如何工作? 简短的答案是:当某个内存块(例如某个对象)不再可访问时,有资格回收它。何时,如何回收或是否回收它完全取决于实现,并且不同的实现方式也不同。但是在语言级

  • 主要内容:总结通过前几节对可利用空间表进行动态存储管理的介绍,运行机制可以概括为: 当用户发出申请空间的请求后,系统向用户分配内存;用户运行结束释放存储空间后,系统回收内存。这两部操作都是在用户给出明确的指令后,系统对存储空间进行有效地分配和回收。 但是在实际使用过程中,有时会因为用户申请了空间,但是在使用完成后没有向系统发出释放的指令,导致存储空间既没有被使用也没有被回收,变为了 无用单元或者会产生 悬挂访问