Android ViewHolder-良好实践

Android ViewHolder-良好实践,android,android-viewholder,Android,Android Viewholder,一个新手问题。为什么要在getView()中初始化ViewHolder?为什么我们不能在构造函数中初始化它?当我们在每次行填充时滚动列表,并为需要初始化视图持有者的每一行创建新的行视图时。就像我在第二排有两个文本视图一样 static class ViewHolder { protected TextView title; protected TextView type; } public View getView(int positi

一个新手问题。为什么要在
getView()
中初始化
ViewHolder
?为什么我们不能在构造函数中初始化它?

当我们在每次行填充时滚动列表,并为需要初始化视图持有者的每一行创建新的行视图时。就像我在第二排有两个文本视图一样

  static class ViewHolder {
        protected TextView title;
        protected TextView type;

    }


     public View getView(int position, View convertView, ViewGroup parent) {
            View view = null;
            if (convertView == null) {
                LayoutInflater inflator = context.getLayoutInflater();
                view = inflator.inflate(R.layout.feeds_rowview, null);
                final ViewHolder viewHolder = new ViewHolder();
                view.setTag(viewHolder);
                viewHolder.title = (TextView) view.findViewById(R.id.Title);
                viewHolder.type = (TextView) view.findViewById(R.id.Type);

            } else {
                view = convertView;
            }

            ViewHolder holder = (ViewHolder) view.getTag();
            holder.title.setText(list.get(position).getTitle());
            holder.type.setText(list.get(position).getType());

            return view;
     }

您将有多个
ViewHolder
对象存在

ListView
就其性质而言,不会为其每一行创建新的
View
实例。这样,如果您有一个包含一百万个内容的
ListView
,您就不需要存储一百万个内容的布局信息。那么你需要储存什么呢?只是屏幕上的东西。然后可以反复使用这些视图。这样,你的
ListView
中的一百万个对象可能只有10个子视图

在自定义阵列适配器中,将有一个名为
getView()
的函数,该函数如下所示:

public View getView(int position, View convertView, ViewGroup parent) {
    //Here, position is the index in the list, the convertView is the view to be
    //recycled (or created), and parent is the ListView itself.

    //Grab the convertView as our row of the ListView
    View row = convertView;

    //If the row is null, it means that we aren't recycling anything - so we have
    //to inflate the layout ourselves.
    if(row == null) {
          LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
          row = inflater.inflate(R.layout.list_item, parent, false);
    }

    //Now either row is the recycled view, or one that we've inflated. All that's left
    //to do is set the data of the row. In this case, assume that the row is just a
    //simple TextView
    TextView textView = (TextView) row.findViewById(R.id.listItemTextView);

    //Grab the item to be rendered. In this case, I'm just using a string, but
    //you will use your underlying object type.
    final String item = getItem(position);

    textView.setText(item);

    //and return the row
    return row;
}
这会起作用,但请花一点时间,看看您是否能发现这里的效率低下。考虑一下上面哪一个代码将被冗余调用

问题是我们一次又一次地调用
row.findviewbyd
,即使在我们第一次查找它之后,它也不会改变。如果您的列表中只有一个简单的
TextView
,这可能没那么糟糕,如果您有一个复杂的布局,或者您有多个视图需要设置数据,那么您可能会一次又一次地浪费一些时间来查找视图

那么我们如何解决这个问题呢?嗯,在我们查找文本视图之后,将其存储在某个地方是有意义的。因此,我们引入了一个名为
ViewHolder
的类,它“保存”视图。在适配器内部,引入一个内部类,如下所示:

private static class ViewHolder {
    TextView textView;
}
这个类是私有的,因为它只是适配器的缓存机制,并且是静态的,所以我们不需要对适配器的引用就可以使用它

这将存储我们的视图,这样我们就不必多次调用
row.findViewById
。我们应该把它放在哪里?当我们第一次放大视图时。我们把它存放在哪里?视图有一个自定义的“标记”字段,可以用来存储关于视图的元信息——这正是我们想要的!然后,如果我们已经看到这个视图,我们只需要查找标记,而不是查找行中的每个视图

因此
getView()中的if语句变成:

//If the row is null, it means that we aren't recycling anything - so we have
//to inflate the layout ourselves.
ViewHolder holder = null;
if(row == null) {
    LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    row = inflater.inflate(R.layout.list_item, parent, false);
    //Now create the ViewHolder
    holder = new ViewHolder();
    //and set its textView field to the proper value
    holder.textView =  (TextView) row.findViewById(R.id.listItemTextView);
    //and store it as the 'tag' of our view
    row.setTag(holder);
} else {
    //We've already seen this one before!
    holder = (ViewHolder) row.getTag();
}
现在,我们只需要更新holder.textView的文本值,因为它已经是对回收视图的引用!因此,我们的最终适配器代码变成:

public View getView(int position, View convertView, ViewGroup parent) {
    //Here, position is the index in the list, the convertView is the view to be
    //recycled (or created), and parent is the ListView itself.

    //Grab the convertView as our row of the ListView
    View row = convertView;

    //If the row is null, it means that we aren't recycling anything - so we have
    //to inflate the layout ourselves.
    ViewHolder holder = null;
    if(row == null) {
        LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        row = inflater.inflate(R.layout.list_item, parent, false);
        //Now create the ViewHolder
        holder = new ViewHolder();
        //and set its textView field to the proper value
        holder.textView =  (TextView) row.findViewById(R.id.listItemTextView);
        //and store it as the 'tag' of our view
        row.setTag(holder);
    } else {
        //We've already seen this one before!
        holder = (ViewHolder) row.getTag();
    }

    //Grab the item to be rendered. In this case, I'm just using a string, but
    //you will use your underlying object type.
    final String item = getItem(position);

    //And update the ViewHolder for this View's text to the correct text.
    holder.textView.setText(item);

    //and return the row
    return row;
}
我们完了

需要考虑的一些事情:

  • 如果一行中有多个要更改的视图,则这将如何更改?作为一项挑战,创建一个列表视图,其中每行有两个
    TextView
    对象和一个
    ImageView
  • 调试ListView时,请检查一些事项,以便真正了解发生了什么:
  • 调用ViewHolder的构造函数的次数
  • getView()末尾更新之前,
    holder.textView.getText()的值是多少

  • . 看看这个视频。应该回答你的问题。不,每个视图持有者都有不同的值。因为我们在同一行中有不同的数据。没问题-让我知道一切是否合理,当我第一次了解它时,ViewHolder的事情确实让我困惑了一段时间。哇。回答得很好。作为对你的挑战;你能用一些参考资料来改进它吗?=)回答得很好,确实帮助我理解了关键概念。但现在我心里有一个疑问。如果我们在适配器的构造函数中创建ViewHolder对象,并将该对象保存在某个地方,每次获取保存的ViewHolder对象并使用它时都保存在getview方法中,会怎么样????????