Android 为具有不同项目布局的 ListView 创建 ViewHolder

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3514548/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 10:33:58  来源:igfitidea点击:

Creating ViewHolders for ListViews with different item layouts

androidlistviewlistactivity

提问by Andrew

I have a ListView with different layouts for different items. Some items are separators. Some items are different because they hold different kinds of data, etc.

我有一个针对不同项目具有不同布局的 ListView。有些项目是分隔符。有些项目是不同的,因为它们包含不同类型的数据等。

I want to implement ViewHolders to speed up the getView process, but I'm not quite sure how to go about it. Different layouts have different pieces of data (which makes naming difficult) and different numbers of Views I want to use.

我想实现 ViewHolders 来加速 getView 过程,但我不太确定如何去做。不同的布局有不同的数据(这使得命名变得困难)和我想要使用的不同数量的视图。

How should I go about doing this?

我该怎么做呢?

The best idea I can come up with is to create a generic ViewHolder with X items where X is the number of Views in an item layout with the highest number of them. For the other views with a small number of Views, I'll just use a subsection of those variables in the ViewHolder. So say I have 2 layouts I use for 2 different items. One has 3 TextViews and the other has 1. I would create a ViewHolder with 3 TextView variables and only use 1 of them for my other item. My problem is that this can get really ugly looking and feels really hacky; especially when an item layout may have many Views of many different types.

我能想到的最好的主意是创建一个带有 X 个项目的通用 ViewHolder,其中 X 是项目布局中视图数量最多的项目。对于具有少量视图的其他视图,我将只使用 ViewHolder 中这些变量的一部分。所以说我有 2 个布局用于 2 个不同的项目。一个有 3 个 TextView,另一个有 1 个。我会用 3 个 TextView 变量创建一个 ViewHolder,并且只将其中的 1 个用于我的其他项目。我的问题是这可能会变得非常难看,而且感觉非常糟糕;特别是当一个项目布局可能有许多不同类型的视图时。

Here is a very basic getView:

这是一个非常基本的 getView:

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    MyHolder holder;

    View v = convertView;
    if (v == null) {
        LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        v = vi.inflate(R.layout.layout_mylistlist_item, parent, false);

        holder = new MyHolder();
        holder.text = (TextView) v.findViewById(R.id.mylist_itemname);
        v.setTag(holder);
    }
    else {
        holder = (MyHolder)v.getTag();
    }

    MyListItem myItem = m_items.get(position);

    // set up the list item
    if (myItem != null) {
        // set item text
        if (holder.text != null) {
            holder.text.setText(myItem.getItemName());
        }
    }

    // return the created view
    return v;
}

Suppose I had different types of row layouts, I could have a ViewHolder for each type of row. But what type would I declare "holder" to be at the top? Or would I declare a holder for each type and then use the one for the type of row I'm on.

假设我有不同类型的行布局,我可以为每种类型的行设置一个 ViewHolder。但是我会声明什么类型的“持有人”在顶部?或者我会为每种类型声明一个持有人,然后将一个用于我所在行的类型。

回答by CodeFusionMobile

ListView has a built in type management system. In your adapter, you have several types of items, each with their own view and layout. By overriding getItemViewTypeto return the data type of a given position, ListView is garunteed to pass in the correct convertview for that type of data. Then, in your getView method simply check the datatype and use a switch statement to handle each type differently.

ListView 有一个内置的类型管理系统。在您的适配器中,您有多种类型的项目,每个项目都有自己的视图和布局。通过覆盖getItemViewType以返回给定位置的数据类型,ListView 需要为该类型的数据传入正确的转换视图。然后,在您的 getView 方法中,只需检查数据类型并使用 switch 语句以不同方式处理每种类型。

Each Layout type should have its own viewholder for naming clarity and ease of maintainence. Name the ViewHolders something related to each data type to keep everything straight.

每个布局类型都应该有自己的viewholder,以便命名清晰和易于维护。将 ViewHolders 命名为与每种数据类型相关的名称,以保持一切正常。

Trying to overlap everything into one ViewHolder is just not worth the effort.

试图将所有内容重叠到一个 ViewHolder 中是不值得的。

EditExample

编辑示例

@Override 
public View getView(int position, View convertView, ViewGroup parent) { 
    int viewType = this.getItemViewType(position);

    switch(viewType)
    {
       case TYPE1:

        Type1Holder holder1; 

         View v = convertView; 
         if (v == null) { 
             LayoutInflater vi = (LayoutInflater)getContext().getSystemService     (Context.LAYOUT_INFLATER_SERVICE); 
             v = vi.inflate(R.layout.layout_mylistlist_item_type_1, parent, false); 

             holder1 = new Type1Holder (); 
             holder1.text = (TextView) v.findViewById(R.id.mylist_itemname); 
             v.setTag(holder1); 
         } 
         else { 
             holder1 = (Type1Holder)v.getTag(); 
         } 

         MyListItem myItem = m_items.get(position); 

         // set up the list item 
         if (myItem != null) { 
             // set item text 
             if (holder1.text != null) { 
                 holder1.text.setText(myItem.getItemName()); 
             } 
         } 

         // return the created view 
         return v; 


     case TYPE2:
            Type2Holder holder2; 

         View v = convertView; 
         if (v == null) { 
             LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
             v = vi.inflate(R.layout.layout_mylistlist_item_type_2, parent, false); 

             holder2 = new Type2Holder (); 
             holder2.text = (TextView) v.findViewById(R.id.mylist_itemname); 
             holder2.icon = (ImageView) v.findViewById(R.id.mylist_itemicon); 
             v.setTag(holder1); 
         } 
         else { 
             holder2 = (Type2Holder)v.getTag(); 
         } 

         MyListItem myItem = m_items.get(position); 

         // set up the list item 
         if (myItem != null) { 
             // set item text 
             if (holder2.text != null) { 
                 holder2.text.setText(myItem.getItemName()); 
             } 

             if(holder2.icon != null)
                 holder2.icon.setDrawable(R.drawable.icon1);
         } 


         // return the created view 
         return v; 


       default:
           //Throw exception, unknown data type
    }
} 

回答by Santiago Battaglino

Another example.

另一个例子。

public class CardArrayAdapter extends ArrayAdapter {

公共类 CardArrayAdapter 扩展 ArrayAdapter {

public CardArrayAdapter(Context context) {
    super(context, R.layout.adapter_card);
}

@Override
public View getView(int position, View view, ViewGroup parent) {

    final Card card = getItem(position);
    ViewHolder holder;

    //if (view != null) {
        //holder = (ViewHolder) view.getTag();
    //} else {
    Log.d("card.important?", card.name + " = " + Boolean.toString(card.important));
    if(card.important) {
        view = LayoutInflater.from(getContext()).inflate(R.layout.adapter_card_important, parent, false);
    }else {
        view = LayoutInflater.from(getContext()).inflate(R.layout.adapter_card, parent, false);
    }
    holder = new ViewHolder(view);
    view.setTag(holder);
    //}

    // IMG
    Picasso.with(getContext())
            .load(card.logo)
            .placeholder(R.drawable.ic_phonebook)
            .error(R.drawable.ic_phonebook)
            .fit()
            .centerCrop()
            .transform(new CircleTransform())
            .into(holder.logo);

    holder.name.setText(card.name);

    if(card.important) {
        holder.category.setVisibility(View.VISIBLE);
        if (card.category.equals("airline")) {
            card.category = "airlines";
        }
        int id = getContext().getResources().getIdentifier(card.category, "string", getContext().getPackageName());
        holder.category.setText(getContext().getResources().getString(id));
    }else {
        holder.category.setVisibility(View.GONE);
    }

    holder.tagline.setText(card.tagline);
    holder.favorite.setChecked(card.favorite);

    holder.favorite.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            card.favorite = ((CheckBox) v).isChecked();
            card.save();
        }
    });

    return view;

}

static class ViewHolder {
    @InjectView(R.id.logo) ImageView logo;
    @InjectView(R.id.name) TextView name;
    @InjectView(R.id.category) TextView category;
    @InjectView(R.id.tagline) TextView tagline;
    @InjectView(R.id.favorite) CheckBox favorite;
    public ViewHolder(View view) {
        ButterKnife.inject(this, view);
    }
}

}

}