GetView中的ConvertView()

时间:2014-05-09 18:22:33

标签: android android-listview

我正在尝试围绕 getView(),我不认为Android中的任何主题会在StackOverflow和其他地方造成更多混淆和问题。每个人都想知道为什么它被调用了这么多次或者按什么顺序被调用,但是正如Romain Guy所说here,“对于getView()的顺序绝对不能保证叫了也没多少次。

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

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

对于第一次调用的位置0,convertView为空,对于位置1-11为非空,然后对于位置0为非空,对于位置1-11为空,对于位置0为null,对于位置为非null 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

3 个答案:

答案 0 :(得分:6)

第一次xx是一个数字,接近屏幕上显示的项目数,convertViewnull。您需要实例化一个新的View才能返回。

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

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

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

// 'bind' view

return view;

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

答案 1 :(得分:0)

convertView参数是您之前从getView()返回的某个View的循环实例。第一次调用getView()时,convertView 保证为空,因为您尚未创建要回收的视图。当列表中的某个视图已在屏幕上滚动且用户不再可见时,它们将从ViewGroup中删除并添加到内部&#34;回收站&#34;。然后,您将在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);

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

答案 2 :(得分:0)

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