【问题标题】:ConvertView in GetView()GetView() 中的转换视图
【发布时间】:2014-05-09 18:22:32
【问题描述】:

我正试图围绕 getView() 进行思考,我认为 Android 中没有任何话题会在 StackOverflow 和其他地方造成更多的混乱和问题。每个人都想知道为什么它被调用了这么多次或者以什么顺序或根本不被调用,但正如 Romain Guy 所说 here ,“绝对不能保证调用 getView() 的顺序,也不多少次。"

所以我有一个不同的问题:我不明白 convertView 参数。

我有一个包含 15 个项目的列表,其中 11 个可以显示在屏幕上。我的应用程序第一次启动时 getView() 被调用了 48 次。

convertView 在第一次调用时位置 0 为空,位置 1-11 为非空,然后位置 0 非空,位置 1-11 为空,然后位置 0 为空,位置非空1-11,最后是位置 0-11 的非空值。

有人能解释一下为什么/什么时候 convertView 是 null 还是非 null,它是如何/为什么在大多数位置开始为非 null 的,以及为什么相同的位置似乎在这两种状态之间来回反弹?

如果能提供用清晰的英语编写的、详细解释 convertView 的优秀教程的参考资料,我们也将不胜感激。

PS - 我的测试是在运行 Android 2.3.5 的设备上完成的,如果这很重要的话。我知道 Google 从那时起已经多次更改 ListActivity/adapter/getView 的内容。

根据请求,我包含了适配器代码(我隐藏了一些专有名称)。不幸的是,我无法回答任何“你为什么这样做?”问题,因为不是我写的

protected class PLxxxAdapter extends BaseAdapter {

    public PLxxxAdapter(Context c) {
    }

    @Override
    public int getCount() {
        return listItems.size();
    }

    @Override
    public Object getItem(int position) {
        return listItems.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        boolean select;

        if (convertView == null) {              
            LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = vi.inflate(R.layout.PLxxxitem, null);
            //This is still needed even though we point to an XML description
            convertView.setLayoutParams(new ListView.LayoutParams(
                    ListView.LayoutParams.MATCH_PARENT,
                    ListView.LayoutParams.MATCH_PARENT));
            holder = new ViewHolder();

            //Get the views of the row
            holder.itemView = (TextView)convertView.findViewById(R.id.post);
            holder.cV1 = (CheckedTextView)convertView.findViewById(R.id.check1);

            //Init the 'confirm' box listener
            holder.cV1.setCompoundDrawablesWithIntrinsicBounds(0, R.layout.smallcb, 0, 0);
            holder.cV1.setOnClickListener(new ConfBoxListener());

            convertView.setTag(holder);
            holder.cV1.setTag(holder);  //These views need tags for onClick()
        }
        else {
            holder = (ViewHolder)convertView.getTag();  // convertview NOT null
        }

       try  {
          int liSize = listItems.size();
          if (position < liSize)  {
             holder.itemView.setText(listItems.get(position));
          }
       }
       catch (Exception e)  {
              Log.e ("PLxxxActivity.getView Crash", "details " + e);
       }
        holder.cV1.setChecked(confirmed.get(position));
        select = selected.get(position);

        if (select == true)   {
            convertView.setBackgroundResource(R.color.colBlue);
        }               

        else
            convertView.setBackgroundResource(R.color.colGrey);
        holder.position = position;
        if (RemoteControlActivity.confCBs == true)
            holder.cV1.setVisibility(View.VISIBLE);
        else
            holder.cV1.setVisibility(View.INVISIBLE);

        return convertView;
    }  // end getView
}  //end class PLxxxAdapter

【问题讨论】:

  • 正如我的回答中所述,您的 getView 方法被调用 48 次这一事实可能是您的代码中的一个问题。如果您可以展示您如何设置适配器以及如何处理它,也许我们也可以为您提供帮助。
  • @user1199931 之后它不会总是为非空值。至少可以在屏幕上显示的视图数量为空(但是,当您获得大小可能不同的视图或多个项目视图类型时,这会变得更加复杂)

标签: android android-listview


【解决方案1】:

第一个x 次,其中x 是一个接近屏幕上可见项目数的数字,convertView 是null。您需要实例化一个新的View 才能返回。

当您向下滚动时,现有的View 会被向上推到看不见的地方。现在可以重复使用,而不是销毁它。您会注意到,就在从下方推入新的View 之前,您的getView 方法被调用,并带有一个有效的convertView。这正是之前被推到看不见的View(或者可能是另一个,有一些额外的逻辑)!

因此,您可以重用View 并将其调整为它所代表的新item,而不是重新实例化您的View。你会经常看到类似的东西:

View view = convertView;
if(view == null){
    view = LayoutInflater.from(getContext()).inflate(...);
}

// 'bind' view

return view;

您的 getView 方法在启动时被调用 48 次这一事实实际上可能是您的代码存在问题。

【讨论】:

  • 您说“前 x 次,其中 x 是屏幕上可见项目数附近的数字,convertView 为空” - 我也是这么想的,但显然不是这样,正如我在问题中指出的那样。因此我很困惑。
  • @user316117 查看我对您问题的评论 :)
  • 这是一个很好的解释伙伴。我很高兴你把它写下来。谢谢。
  • 我有同样的问题,但在我的情况下,我的列表视图中有编辑文本。因此,在notifyDataSetChanged 生成位置 x 的新行时,两个编辑文本都获得焦点和相同的数据。请尽快回复
【解决方案2】:

convertView 参数是您之前从getView() 返回的某个视图的回收实例。第一次调用 getView() 时,convertView 保证为空,因为您尚未创建要回收的视图。当列表中的一个视图被滚动到屏幕外并且不再对用户可见时,它们将从 ViewGroup 中删除并添加到内部“回收站”中。然后,您将开始以convertView 参数的形式接收这些回收的视图,一旦它们可用。

您应始终检查 convertView 是否为非 null,如果是,则应尝试重新使用它。例如,如果您的 getView() 扩展了 TextView,则可以安全地将 convertView 转换为 TextView 实例如果它是非空的。然后您应该更新其文本以匹配当前位置 (getItem(position)) 中代表您的项目的任何内容。


编辑:

您需要这个的唯一原因:

    convertView = vi.inflate(R.layout.PLxxxitem, null);
    //This is still needed even though we point to an XML description
    convertView.setLayoutParams(new ListView.LayoutParams(
            ListView.LayoutParams.MATCH_PARENT,
            ListView.LayoutParams.MATCH_PARENT));

是因为您膨胀视图的方式。而不是传递null,而是传递带有attachToRoot = false的父项,即:

convertView = vi.inflater(R.layout.PLxxxitem, parent, false);

无论如何,我认为您的问题不在于您的适配器。

【讨论】:

  • 没有滚动。正如我所说,这是应用程序首次启动的时候 - 设备只是坐在桌子上,没有被触摸。位置 0 为空,但位置 1-11 非空。这怎么可能? FWIW - 当一切都结束并且 getView 已被调用 48 次时,屏幕已正确填充。
  • 您是否在某个地方的适配器上调用 notifyDatasetChanged()?这将触发列表中所有视图的重绘。此外,如果您的布局发生变化(您是否在某处执行 requestLayout() ?)。 TL;DR:显示一些代码。
  • 但是如果它触发了所有视图的重绘,那是否也会导致 getView 被调用?我不关心调用 getView() 的次数(正如 Romain 所说,没有任何保证) - 我想立即了解 convertView 如何在 12 个位置中的 11 个位置不为空。这是一个大程序——你想看什么代码?
  • 好的,我已经添加了源代码。在 [link(developer.android.com/reference/android/widget/Adapter.html) 的 Google 文档中,他们说 convertView 是 “如果可能的话,要重用的旧视图。注意:在使用之前,您应该检查此视图是否为非空且类型合适” 他们是否在任何地方确切地记录了他们所说的“旧观点”?
  • 它们表示您之前在 getView() 中创建的视图。
【解决方案3】:

正如其他人所暗示的那样,问题可能不在您的适配器中。我相当肯定这一点,因为我刚刚解决了同样的问题,然后来到这里寻找答案,结果却感到失望。就我而言,我将列表视图的宽度和高度设置为 wrap_content 而不是 match_parent。当我将其设置为 match_parent 时,我得到了所需的行为。很难发现,因为视图会增长以适应其内容的大小并占据其容器的整个大小,从而显示多个列表条目,但是系统“认为”它只能容纳 1 个项目。所以 getView 方法被同一个 convertView 重复调用。 (在您的情况下,它仅在第一次调用时为空。)一种可能的方法(我没有尝试过)是询问父列表视图并检查其大小。确保它有足够的空间来渲染多行并进行相应的调整。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多