152

所以我之前遇到过这个问题,自然而然就在这里求助了。Luksprog 的回答很棒,因为我不知道 ListView 和 GridView 如何通过回收视图优化自身。因此,根据他的建议,我能够更改将视图添加到 GridView 的方式。问题是现在我有一些没有意义的东西。这是我getViewBaseAdapter


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 的回收 View,或者本文所说的方式,ScrapView 机制的其他人。

在此处输入图像描述

稍后编辑

添加指向谷歌 IO 谈话的链接,基本上你只需要了解 ListView 是如何工作的。链接死在评论中。所以 user3427079 很好地更新了那个链接。是为了方便访问。

4

3 回答 3

337

最初,我也不知道listview回收和convertview使用机制,但经过一整天的研究,通过参考android.amberfog中的图像,我非常了解listview的机制 在此处输入图像描述

每当您的列表视图被适配器填充时,它基本上会显示列表视图可以在屏幕上显示的数,并且即使您滚动浏览列表,行数也不会增加。这是 android 使用的技巧,以便 listview 更有效和更快地工作。现在 listview 引用图像的内幕,如您所见,最初 listview 有 7 个可见项目,然后,如果您向上滚动直到项目 1 不再可见,getView() 将此视图(即 item1)传递给回收站,你可以使用

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 中注意到,最初,convertview 对于所有可见行都是空的,因为最初在回收器中没有视图(即项目),所以您的 getView() 为每个可见项目创建一个新视图,但是当您向上滚动并且项目 1 移出屏幕时,它将以其当前状态发送到回收器(例如 TextView 'text' 或在我的情况下,如果选中复选框,它将与视图相关联并且存储在回收站中)。

现在,当您向上/向下滚动时,您的列表视图不会创建新视图,它将使用回收站中的视图。在您的Logcat中,您会注意到“convertView”不为空,这是因为您的新项目 8 将使用 convertview 绘制,即基本上它从回收站获取项目 1 视图并在其位置膨胀项目 8,您可以观察在我的代码中。如果您有一个复选框,并且您在位置 0 选中它(假设 item1 有一个复选框并且您选中了它),那么当您向下滚动时,您会看到第 8 项复选框已经选中,这就是为什么 listview 重新使用相同的视图,由于性能优化,不会为您创建新的。

重要的事

1 . 永远不要将您的列表视图的layout_heightand设置为,因为这将迫使您的适配器获取一些子项来测量要在列表视图中绘制的视图的高度,并且可能导致一些意外行为,例如即使列表未滚动也返回转换视图。始终使用或修复宽度/高度。layout_widthwrap_contentgetView()match_parent

2 . 如果您想在列表视图之后使用一些布局或视图,并且如果我将列表视图设置layout_heightfill_parent视图后列表视图将不会显示在屏幕下方,那么您可能会想到问题,因此最好将您的列表视图放在布局。例如线性布局,并根据您的要求设置该布局的高度和宽度,并将列表视图的高度宽度属性设置为您的布局(例如,如果您的布局宽度为320,高度为280)然后您的列表视图应该有相同的高度宽度. 这将告诉 getView() 要呈现的视图的确切高度和宽度,并且 getView() 不会一次又一次地调用一些随机行,并且不会发生其他问题,例如在滚动之前返回转换视图,我有测试这是我自己,除非我的列表视图在 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

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

于 2013-01-01T07:24:30.057 回答
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();
}

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

getHolder(position, view, parent);

做所有的设置操作

ImageViews, TextViews, etc..
于 2014-04-14T10:56:11.737 回答
3

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

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

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

于 2015-12-19T13:24:35.440 回答