使用 ViewHolder 的 Android 列表视图

Posted

技术标签:

【中文标题】使用 ViewHolder 的 Android 列表视图【英文标题】:Android listview using ViewHolder 【发布时间】:2014-09-11 23:06:10 【问题描述】:

我有问题。单击后,我试图更改列表视图中的图标。它可以正常工作,尽管不仅修改了单击的图标,还修改了未显示的图标。例如,如果我单击列表视图第一项中的图标,第五个图标也会更改。对于以下所有项目(列表视图的每五个项目)重复此行为。 这是我的 getView 方法:

   public class AlphabeticalAdapter extends ArrayAdapter<String>
   
       int layoutResourceId; 
       private final Context context;
       private List<String> data;
       private ProgressDialog mProgressDialog;
       private ImageView downloadImageButton;


       public AlphabeticalAdapter(Context context, int resource, List<String> data)
           super(context, resource, data);
           this.layoutResourceId = resource;
           this.context = context;
           this.data = data;    
       

       public View getView(int position, View convertView, ViewGroup parent) 

          // View rowView = convertView;
           final ViewHolder viewHolder;

           if (convertView == null) 

               LayoutInflater inflater = (LayoutInflater) context
                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);


               convertView  = inflater.inflate(R.layout.catalogslist_single_row, parent, false);

           viewHolder = new ViewHolder();

           viewHolder.catlogTitle=(TextView)convertView.findViewById(R.id.txtTitle);
           viewHolder.icon=(ImageView)convertView.findViewById(R.id.imageView2); 
           viewHolder.downloadImageButton=(ImageView)convertView.findViewById(R.id.downloadImageButton);

           //downloadImageButton = (ImageView)rowView.findViewById(R.id.downloadImageButton);

           viewHolder.position = position;


           viewHolder.downloadImageButton.setOnClickListener(new OnClickListener() 
               @Override  
               public void onClick(View v) 
                     System.out.println("DOWNLOAD PRESSED");

                     viewHolder.downloadImageButton = (ImageView)v.findViewById(R.id.downloadImageButton);
                     viewHolder.downloadImageButton.setImageResource(R.drawable.icon_ok);
                     viewHolder.downloadImageButton.setTag("downloaded");
                     //rowView.setTag("downloaded");


                 
             );



           convertView.setTag(viewHolder);

           

           else
               viewHolder= (ViewHolder)convertView.getTag(); 
           

           viewHolder.catlogTitle.setText(data.get(position));
           viewHolder.catlogTitle.setTypeface(regularDin);
           viewHolder.icon.setImageResource(R.drawable.cata);


           if(viewHolder.downloadImageButton.getTag() == "downloaded")
             downloadImageButton = (ImageView)convertView.findViewById(R.id.downloadImageButton);
             downloadImageButton.setImageResource(R.drawable.icon_ok);
           
           else
               downloadImageButton = (ImageView)convertView.findViewById(R.id.downloadImageButton);
                 downloadImageButton.setImageResource(R.drawable.icon_download);
           


           viewHolder.position = position;

        return convertView;

        //close getView 

...

这是我的 ViewHolder 类:

      static class ViewHolder
       ImageView downloadImageButton;
       TextView catlogTitle;
       ImageView icon;
       int position;
   

【问题讨论】:

【参考方案1】:

在下面更改您的代码。我想你错过了。

public class AlphabeticalAdapter extends ArrayAdapter<String> 
    int layoutResourceId;
    private final Context context;
    private List<String> data;
    private List<String> tags;
    private ProgressDialog mProgressDialog;
    private ImageView downloadImageButton;

    public AlphabeticalAdapter(Context context, int resource, List<String> data) 
        super(context, resource, data);
        this.layoutResourceId = resource;
        this.context = context;
        this.data = data;
        tags = new ArrayList<String>();
        int size = data.size();
        for (int i = 0; i < size; i++) 
            tags.add("tag");
        
    

    static class ViewHolder 
        ImageView downloadImageButton;
        TextView catlogTitle;
        ImageView icon;
        int position;
    

    public View getView(final int position, View convertView, ViewGroup parent) 

        // View rowView = convertView;
        final ViewHolder viewHolder;

        if (convertView == null) 
            LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            // convertView = inflater.inflate(R.layout.catalogslist_single_row,
            // parent, false);
            viewHolder = new ViewHolder();
            viewHolder.position = position;
            viewHolder.downloadImageButton
                    .setOnClickListener(new OnClickListener() 
                        @Override
                        public void onClick(View v) 
                            System.out.println("DOWNLOAD PRESSED");
                            viewHolder.downloadImageButton.setTag("downloaded");
                            tags.add(position, "downloaded");
                        
                    );
            convertView.setTag(viewHolder);
         else 
            viewHolder = (ViewHolder) convertView.getTag();
        

        viewHolder.catlogTitle.setText(data.get(position));
        viewHolder.catlogTitle.setTypeface(regularDin);
        viewHolder.icon.setImageResource(R.drawable.cata);

        if (tags.get(position) == "downloaded") 
            downloadImageButton.setImageResource(R.drawable.icon_ok);
         else 
            downloadImageButton.setImageResource(R.drawable.icon_download);
        

        viewHolder.position = position;
        return convertView;
     // close getView

【讨论】:

@Mark 我编辑了我的代码,看到这个并告诉你它的帮助 这是错误的。阅读我的答案以了解原因。 ViewHolder 对象的数量不等于行数。否则 ViewHolder 模式的含义是什么? @kupsef 是的,你是对的。其他不同的列表之一添加了它的工作 @kupsef 你能贴一个例子吗? @Mark 我更新了我认为的代码。您还可以使用数组列表来 hashmap 或 TreeSet 选择位置。【参考方案2】:

ListView 中同时可见的行数与 convertViews 一样多(系统会重复使用它)。所以你实际上有 5 个convertView,因此你有 5 个ImageView 用于图标。问题是您使用那些 ImageView 的标签来存储“下载”信息。那是 5 个状态,这就是为什么您在列表中滚动时会看到每五行下载一次。

我想现在你知道它不起作用了。您需要存储每个项目的下载状态,因此您必须将底层的List&lt;String&gt; 更改为List&lt;ListItem&gt;,其中ListItem 可以存储实际行的下载状态。

之后,您所要做的就是更新convertViewImageView(在getView() 中)以显示正确的图标。

【讨论】:

【参考方案3】:

像这样更改您的代码。在 try 块之前使用 convertView 添加空检查。

    final MenuItem   menuItem = getItem(position);
    View view = convertView;
    final ViewHolder viewHolder;

if (convertView == null) 

    LayoutInflater inflater;

        inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.menu_item, parent, false);
        viewHolder = new ViewHolder();
//      viewHolder.half = (TextView) view.findViewById(R.id.half);
        viewHolder.name = (TextView) view.findViewById(R.id.name);
        viewHolder.description = (TextView) view.findViewById(R.id.description);
        viewHolder.price = (TextView) view.findViewById(R.id.price);
        viewHolder.add = (Button) view.findViewById(R.id.add);
        viewHolder.selectedView = view.findViewById(R.id.selectedView);
        viewHolder.remove = (Button) view.findViewById(R.id.remove);
        viewHolder.total = (TextView) view.findViewById(R.id.itemTotal);
        viewHolder.quantity = (TextView) view.findViewById(R.id.quantity);
        view.setTag(viewHolder);
else
    viewHolder= (ViewHolder)convertView.getTag(); 

【讨论】:

【参考方案4】:

你可以试试这个

public class CustomArrayAdapter extends ArrayAdapter 

// declare your custom list with type;
private List<YourModelClass> allData = new ArrayList<YourModelClass>();

public CustomArrayAdapter(@NonNull Context context, List<YourModelClass> allData) 
    super(context, R.layout.your_layout, allData); // add your_layout.xml
    this.allData = allData;


class ViewHolder 
    TextView name, phone; // declare your your_layout.xml view type


@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) 
    LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    ViewHolder holder = new ViewHolder();
    if (convertView == null) 

        convertView = inflater.inflate(R.layout.your_layout, parent, false); // inflate your_layout.xml

        //initialize your your_layout.xml view
        holder.name = convertView.findViewById(R.id.tv_item_name);
        holder.phone = convertView.findViewById(R.id.tv_item_phone);

        convertView.setTag(holder);
     else 
        holder = (ViewHolder) convertView.getTag();
    

    //set value into your_layout.xml
    holder.name.setText(allData.get(position).getName());
    holder.phone.setText(allData.get(position).getNumber());

    return convertView;

【讨论】:

【参考方案5】:
public class androidListViewActivity extends ListActivity 

   private ListView listView;
   private String names[] = 
        "HV CAPACITOR",
        "LV CAPACITORCSS",

         ;

private String desc[] = 
        "The Powerful Hypter Text Markup Language 5",
        "Cascading Style Sheets",

;


private Integer imageid[] = 
        R.drawable.hv_capacitor,
        R.drawable.lv_capacitor,

;

@Override
public void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
   // setContentView(R.layout.capacitor_layout);
    // storing string resources into Array
    String[] product_name = getResources().getStringArray(R.array.product_name);
    // Binding Array to ListAdapter
    this.setListAdapter(new ArrayAdapter<String>(this, R.layout.list_item, R.id.label, product_name));

    ListView lv = getListView();

    // listening to single list item on click

    lv.setOnItemClickListener(new AdapterView.OnItemClickListener() 
        public void onItemClick(AdapterView<?> parent, View view,
                                int position, long id) 

            // selected item
            String product = ((TextView) view).getText().toString();

            // Launching new Activity on selecting single List Item
            Intent i = new Intent(getApplicationContext(), SingleListItem.class);
            // sending data to new activity
            i.putExtra("product", product);
            startActivity(i);

        
    );


    CapacitorList capacitorList = new CapacitorList(this, names, desc, imageid);
    listView = (ListView) findViewById(R.id.listView);
    listView.setAdapter(capacitorList);

    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() 

        // Launching new Activity on selecting single List Item
        Intent i = new Intent(getApplicationContext(), CapacitorList.class);
        // sending data to new activity

       // startActivity(i);

        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) 
            Toast.makeText(getApplicationContext(), "You Clicked " + names[i], Toast.LENGTH_SHORT).show();
        
    );



    @Override
    public boolean onCreateOptionsMenu (Menu menu)
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    

    @Override
    public boolean onOptionsItemSelected (MenuItem item)
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) 
            return true;
        

        return super.onOptionsItemSelected(item);


    

【讨论】:

以上是关于使用 ViewHolder 的 Android 列表视图的主要内容,如果未能解决你的问题,请参考以下文章

android中ViewHolder模式有啥好处?

Android-进一步封装ViewHolder

android 怎么在外面拿recyclerview 中viewholder的控件

android RecyclerView获得单个Item的ViewHolder

android RecyclerView获得单个Item的ViewHolder

Android知识——ViewHolder的作用与用法