findViewById VS中的ListView适配器查看持有人模式 [英] findViewById vs View Holder Pattern in ListView adapter

查看:113
本文介绍了findViewById VS中的ListView适配器查看持有人模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我总是使用LayoutInflater和findViewById在适配器的getView方法,创造新的项目。

但在许多文章人民写findViewById是非常非常慢,强烈建议使用View持有人模式。

任何人都可以解释为什么findViewById是如此之慢?为什么查看持有人模式是快?

和我应该怎么做,如果需要不同的项目添加到ListView的?我应该创建类为每种类型的?

 静态类ViewHolderItem1 {
    TextView的textViewItem;
}

静态类ViewHolderItem2 {
    按钮btnViewItem;
}
静态类ViewHolderItem3 {
    按钮btnViewItem;
    ImageView的imgViewItem;
}
 

解决方案
  

任何人都可以解释为什么findViewById是如此之慢?为什么查看持有人   模式比较快?

在您不使用支架, getView()方法调用 findViewById()多次你行(S)将退出视野。所以如果你有1000行的名单和990行会出观了那么990次会叫 findViewById()了。

座的设计模式,用于查看缓存 - 持有人(任意)对象包含每行的子部件,当排出来查看,然后findViewById()将不会调用视图将被回收和窗口小部件将从获得持有人。

 如果(convertView == NULL){
   convertView = inflater.inflate(布局,空,假);
   持有人=新的持有人(convertView);
   convertView.setTag(保持器); //设置支架作为行任意对象
}
其他{//观点回收
   //行已经包含Holder对象
   支架=(座)convertView.getTag();
}

//建立从持有人行数据
titleText.setText(。holder.getTitle()的getText()的toString());
 

在哪里Holder类可以是这样的:

 公共类持有人{

   私人视图行;
   私人TextView的称号;

   公共座(查看行){
      this.row =行;
   }

   公众的TextView的getTitle(){
      如果(标题== NULL){
         标题=(TextView中)row.findViewById(R.id.title);
      }
      返回称号;
   }
}
 

由于@meredrica指出你的,如果你想获得更好的性能,可以使用公共领域(但它破坏了封装)。

更新:

下面是第二种方式如何使用 ViewHolder 模式:

  ViewHolder持有人;
//视图创建
如果(convertView == NULL){
   convertView = LayoutInflater.from(mContext).inflate(R.layout.row,父母,假);
   持有人=新ViewHolder();
   holder.title =(TextView中)convertView.findViewById(R.id.title);
   holder.icon =(ImageView的)convertView.findViewById(R.id.icon);
   convertView.setTag(保持器);
}
//观点是回收
其他 {
   支架=(ViewHolder)convertView.getTag();
}

//建立行
最后MyItem项目= mItems.get(位置);
holder.title.setText(item.getTitle());
...

私有静态类ViewHolder {

   公共TextView的称号;
   公共ImageView的图标;
}
 

更新#2:

大家都知道,谷歌和AppCompat V7作为支持库,发布了新的ViewGroup名为<一个href="https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html">RecyclerView这是专为渲染任何适配器为主的观点。由于 @antonioleiva 说,在帖子这是supossed是ListView和GridView控件的继任者的。

要能够使用,你需要基本上是最重要的这个元素和它的特殊的适配器被包裹在提到的ViewGroup - RecyclerView.Adapter 其中ViewHolder是什么东西,我们都在谈论在这里:)简单地说,这个新的ViewGroup元素都有自己的ViewHolder模式实现。所有你需要做的就是创建自定义ViewHolder类必须从延长 RecyclerView.ViewHolder ,你不必在意检查适配器当前行是否为空或不是。

适配器会为你做,你可以肯定该行只在它必须充气(我会说)的情况被夸大。下面是简单的imlementation:

 公共静态类ViewHolder扩展RecyclerView.ViewHolder {

   私人TextView的称号;

   公共ViewHolder(查看根){
      超(根);
      标题= root.findViewById(R.id.title);
   }
}
 

这里有两个重要的事情:

  • 您必须调用超()构造中,你需要通过你 排根视图
  • 您可以直接从ViewHolder获得行特定位置 通过为getPosition()方式。当你想要做一些这是有用 窃听行动后 1 上排小工具。

和ViewHolder的适配器的使用。适配器有三种方法你必须实现:

  • onCreateViewHolder() - 在创建ViewHolder
  • onBindViewHolder() - 在这里要更新的行。我们可以说这是 一张code你在哪里回收行
  • getItemCount() - 我会说这是一样的典型getCount将()方法 在BaseAdapter

因此​​,一个小例子:

  @覆盖
公共ViewHolder onCreateViewHolder(ViewGroup中的父母,INT viewType){
   查看根= LayoutInflater.from(mContext).inflate(myLayout,父母,假);
   返回新ViewHolder(根);
}

@覆盖公共无效onBindViewHolder(ViewHolder持有人,INT位置){
   项目项目= mItems.get(位置);
   holder.title.setText(item.getTitle());
}

@覆盖公众诠释getItemCount(){
   返回mItems!= NULL? mItems.size():0;
}
 

1 这是很好的一提的是 RecyclerView 不提供直接接口,以便能够听产品Click事件。这可能是好奇的人,但<一个href="http://stackoverflow.com/questions/24885223/why-doesnt-recyclerview-have-onitemclicklistener-and-how-recyclerview-is-dif">here是很好的解释为什么它不是那么奇怪,因为它实际上看起来。

我通过创建用于处理在行单击事件(和你想在行任何部件的)我自己的接口解决了这个:

 公共接口RecyclerViewCallback&LT; T&GT; {

   公共无效onItemClick(T项目,INT位置);
}
 

我通过构造结合成适配器,然后调用回调ViewHolder:

  root.setOnClickListener(新View.OnClickListener {
   @覆盖
   公共无效的onClick(视图v){
      INT位置=为getPosition();
      mCallback.onItemClick(mItems.get(位置),位置);
   }
});
 

这是基本的例子,所以不要把它作为唯一的一种可能的方式。可能性是无穷无尽的。

I always use LayoutInflater and findViewById for creating new item in getView method of Adapter.

But in many articles peoples write that findViewById is very very slow and strongly recommends to use View Holder Pattern.

Can anyone explain why findViewById is so slow? And why View Holder Pattern is faster?

And what should i do if needed to add different items to ListView? Should i create classes for each type?

static class ViewHolderItem1 {
    TextView textViewItem;
}

static class ViewHolderItem2 {
    Button btnViewItem;
}
static class ViewHolderItem3 {
    Button btnViewItem;
    ImageView imgViewItem;
}

解决方案

Can anyone explain why findViewById is so slow? And why View Holder Pattern is faster?

When you are not using Holder so getView() method will call findViewById() as many times as you row(s) will be out of View. So if you have 1000 rows in List and 990 rows will be out of View then 990 times will be called findViewById() again.

Holder design pattern is used for View caching - Holder (arbitrary) object holds child widgets of each row and when row is out of View then findViewById() won't be called but View will be recycled and widgets will be obtained from Holder.

if (convertView == null) {
   convertView = inflater.inflate(layout, null, false);
   holder = new Holder(convertView);
   convertView.setTag(holder); // setting Holder as arbitrary object for row
}
else { // view recycling
   // row already contains Holder object
   holder = (Holder) convertView.getTag();
}

// set up row data from holder
titleText.setText(holder.getTitle().getText().toString());

Where Holder class can looks like:

public class Holder {

   private View row;
   private TextView title;

   public Holder(View row) {
      this.row = row;
   }

   public TextView getTitle() {
      if (title == null) {
         title = (TextView) row.findViewById(R.id.title);
      }
      return title;
   }
}

As @meredrica pointed your if you want to get better performance, you can use public fields (but it destroys encapsulation).

Update:

Here is second approach how to use ViewHolder pattern:

ViewHolder holder;
// view is creating
if (convertView == null) {
   convertView = LayoutInflater.from(mContext).inflate(R.layout.row, parent, false);
   holder = new ViewHolder();   
   holder.title = (TextView) convertView.findViewById(R.id.title);
   holder.icon = (ImageView) convertView.findViewById(R.id.icon);
   convertView.setTag(holder);
}
// view is recycling
else {
   holder = (ViewHolder) convertView.getTag();
}

// set-up row
final MyItem item = mItems.get(position);
holder.title.setText(item.getTitle());
...

private static class ViewHolder {

   public TextView title;
   public ImageView icon;
}

Update #2:

As everybody know, Google and AppCompat v7 as support library released new ViewGroup called RecyclerView that is designed for rendering any adapter-based views. As @antonioleiva says in post: "It is supossed to be the successor of ListView and GridView".

To be able to use this element you need basically one the most important thing and it's special kind of Adapter that is wrapped in mentioned ViewGroup - RecyclerView.Adapter where ViewHolder is that thing we are talking about here :) Simply, this new ViewGroup element has its own ViewHolder pattern implemented. All what you need to do is to create custom ViewHolder class that has to extend from RecyclerView.ViewHolder and you don't need to care about checking whether current row in adapter is null or not.

Adapter will do it for you and you can be sure that row will be inflated only in the case it must be inflated (i would say). Here is simple imlementation:

public static class ViewHolder extends RecyclerView.ViewHolder {

   private TextView title;

   public ViewHolder(View root) {
      super(root);
      title = root.findViewById(R.id.title);
   }
}

Two important things here:

  • You have to call super() constructor in which you need to pass your root view of row
  • You are able to get specific position of row directly from ViewHolder via getPosition() method. This is useful when you want to do some action after tapping1 on row widget.

And an usage of ViewHolder in Adapter. Adapter has three methods you have to implement:

  • onCreateViewHolder() - where ViewHolder is created
  • onBindViewHolder() - where you are updating your row. We can say it's piece of code where you are recycling row
  • getItemCount() - i would say it's same as typical getCount() method in BaseAdapter

So a little example:

@Override 
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
   View root = LayoutInflater.from(mContext).inflate(myLayout, parent, false);
   return new ViewHolder(root);
}

@Override public void onBindViewHolder(ViewHolder holder, int position) {
   Item item = mItems.get(position);
   holder.title.setText(item.getTitle());
}

@Override public int getItemCount() {
   return mItems != null ? mItems.size() : 0;
}

1 It's good to mention that RecyclerView doesn't provide direct interface to be able to listen item click events. This can be curious for someone but here is nice explanation why it's not so curious as it actually looks.

I solved this by creating my own interface that is used to handle click events on rows (and any kind of widget you want in row):

public interface RecyclerViewCallback<T> {

   public void onItemClick(T item, int position); 
}

I'm binding it into Adapter through constructor and then call that callback in ViewHolder:

root.setOnClickListener(new View.OnClickListener {
   @Override
   public void onClick(View v) {
      int position = getPosition();
      mCallback.onItemClick(mItems.get(position), position);
   }
});

This is basic example so don't take it as only one possible way. Possibilities are endless.

这篇关于findViewById VS中的ListView适配器查看持有人模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆