ListView的回收机制如何运作

And*_*ndy 142 android android-listview android-gridview

所以我之前遇到过这个问题,我自然就在这里寻求帮助.Luksprog的答案很棒,因为我不知道ListView和GridView如何通过回收视图优化自身.因此,根据他的建议,我能够改变将Grid添加到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;
    }

}
Run Code Online (Sandbox Code Playgroud)

问题是当我滚动时,发生这种情况,而不是在位置0 ...看起来像位置6和位置8,加上它将两个放在位置8.现在我仍然试图使用ListView和GridView,所以我做不明白为什么会这样.我提出这个问题的一个主要原因是帮助那些可能不了解ListView和GridView的回收View的人,或者本文的方式,ScrapView机制.

在此输入图像描述

稍后编辑

添加链接到谷歌IO谈话,基本上你需要了解ListView的工作原理.链接已经落在了评论上.所以user3427079非常适合更新该链接.是为了方便访问.

Muh*_*bar 319

最初我还没有意识到listview回收和convertview使用机制,但经过一整天的研究,我通过引用android.amberfog中的图像非常了解列表视图的机制. 在此输入图像描述

当过你的ListView充满适配器它基本上显示的数目的列表视图可以显示在屏幕上的行数犯规得到提高,即使您在列表中滚动,这是招Android的使用,使ListView的工作更effcient快速,现在listview的内幕故事引用图像,你可以看到最初列表视图有7个可见项目,如果向上滚动,当项目1不再可见时,getView()将此视图(item1)传递给回收站,你可以使用

System.out.println("getview:"+position+" "+convertView);
Run Code Online (Sandbox Code Playgroud)

你的内心

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;
    }
Run Code Online (Sandbox Code Playgroud)

你会注意到你的logcat最初convertview对于所有可见行都是null,因为最初在recycleler中没有视图(item),所以你的getView()为可见项创建了每个新视图,但是当你向上滚动你的项目时1将发送给具有它状态的Recycler(例如TextView文本和我的情况,如果选中复选框,它也将与视图相关联并存储在回收器中).现在,当您向上/向下滚动列表视图不会创建新视图时,它将使用已在您的回收器中的视图(转换视图),在您的Logcat中您将注意到convertView不为null,因为您的新项目8将使用convertview绘制,即,基本上它取得了来自回收器和inflater的item1视图而不是第8项,你可以在矿区代码中观察,如果你有一个复选框,如果你在位置0检查它(让比如item1还有一个复选框,你检查了它)所以当你向下滚动时你会看到第8项复选框已被选中,这就是为什么listview重新使用相同的视图,因为性能优化没有为你创建新的.

重要的事情

1.永远不要将listview 设置为layout_height,因为这将强制你的适配器获取一些孩子来测量要在列表视图中绘制的视图的高度,并且可能导致一些意外的行为,如返回convertview甚至列表不滚动.总是使用或修复宽度/高度.layout_widthwrap_contentgetView()match_parent

2.如果您希望在列表视图和问题后使用某些布局或视图,如果我在列表视图中设置layout_heightfill_parent视图后不会显示,因为它在屏幕上显示,所以最好将列表视图放在layout.For例如Linear Layout并根据您的要求设置该布局的高度宽度,并将listview 的heightwidth属性设置为布局(如果您的布局宽度为320,高度为280),那么您的listview应该有相同的高度宽度.这将告诉getView()要呈现的视图的精确高度和宽度,并且getView()不会一次又一次地调用一些随机行,并且在滚动之前返回转换视图等其他问题也不会发生,我已经测试过了这个我自己,除非我的listview在lineaLayout里面,它也有像重复视图调用和转换视图一样的问题,把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
Run Code Online (Sandbox Code Playgroud)

但现在它已经解决了,我知道,我不是很善于解释,但是因为我整天都在理解,所以我认为像我这样的其他初学者可以得到我的经验帮助,我希望你们现在会有一点点了解的ListView的框架,它是如何工作的,因为它是非常的混乱和麻烦,新手发现太多问题理解它

  • "我知道我不太擅长解释">>>这个答案的+37票表明你在解释方面做得很好.请继续分享您的知识!;) (35认同)
  • 谢谢你的形象.当我开始使用Android时遇到很多关于ListView的麻烦后,我已经非常了解回收原理.不过,它拍了这张照片和句子:`如果你有一个复选框,如果你在位置0检查它(假设item1也有一个复选框,你检查了它)所以当你向下滚动时你会看到第8项复选框已经检查,这就是为什么listview重新使用相同的视图,因为性能优化没有为你创建新的.为我实现我的错误.关于Android ListView回收的最佳帖子我遇到过! (2认同)

Ahm*_*ail 8

请注意,在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();
}
Run Code Online (Sandbox Code Playgroud)

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

getHolder(position, view, parent);
Run Code Online (Sandbox Code Playgroud)

完成所有的设置操作

ImageViews, TextViews, etc..
Run Code Online (Sandbox Code Playgroud)