zoukankan      html  css  js  c++  java
  • 打造android偷懒神器———ListView的万能适配器

    如果你去做任何一个项目,我相信你都会跟我有一样的经历,最最普遍的就是列表显示ListView,当然,写N个自定义的适配器也是情理之中。虽说程序员本身就是搬砖,做这些枯燥无味的重复的事情也是理所当然,但不得不说,谁都想做点高效率的事情的。

    而我们一向写的自定义适配器,无非就是继承ArrayAdapter,或者继承自BaseAdapter,然后重写4个方法,前三个方法基本相同,不同在于getView方法,getView里面为了减少绑定和View的重建,又会引入一个静态类ViewHolder,我相信下面这段代码你一点见过不少。

     1 package com.example.nanchen.commonadapterforlistviewdemo;
     2 
     3 import android.content.Context;
     4 import android.view.LayoutInflater;
     5 import android.view.View;
     6 import android.view.ViewGroup;
     7 import android.widget.BaseAdapter;
     8 import android.widget.ImageView;
     9 import android.widget.TextView;
    10 
    11 import java.util.List;
    12 
    13 /**
    14  * 常见的ListView的Adapter适配器
    15  * Created by 南尘 on 16-7-28.
    16  */
    17 public class MyListAdapter extends BaseAdapter {
    18     private Context context;
    19     private List<Data> list;
    20 
    21     public MyListAdapter(Context context, List<Data> list) {
    22         this.context = context;
    23         this.list = list;
    24     }
    25 
    26     @Override
    27     public int getCount() {
    28         return list == null ? 0 : list.size();
    29     }
    30 
    31     @Override
    32     public Object getItem(int position) {
    33         return list.get(position);
    34     }
    35 
    36     @Override
    37     public long getItemId(int position) {
    38         return position;
    39     }
    40 
    41     @Override
    42     public View getView(int position, View convertView, ViewGroup viewGroup) {
    43         MyViewHolder holder = null;
    44         if (convertView == null) {
    45             convertView = LayoutInflater.from(context).inflate(R.layout.list_item,viewGroup,false);
    46             holder = new MyViewHolder();
    47             holder.iv = (ImageView) convertView.findViewById(R.id.item_image);
    48             holder.tv = (TextView) convertView.findViewById(R.id.item_text);
    49             convertView.setTag(holder);
    50         } else {
    51             holder = (MyViewHolder) convertView.getTag();
    52         }
    53         Data data = (Data) getItem(position);
    54         holder.iv.setImageResource(data.getImageId());
    55         holder.tv.setText(data.getText());
    56         return convertView;
    57     }
    58 
    59     public static class MyViewHolder {
    60         ImageView iv;
    61         TextView tv;
    62     }
    63 }

    可以毫不犹豫地说这个东西我现在闭着眼睛都能流利地写出来,可见少了数百次是难以做到的。

    有时候我们也想盛点时间去打点小地主,撩下小妹子,如果要是可以打造一个万能的适配器就好了。

    仔细观察上面的Adapter,的确是前三个方法一样。我们要是可以全部抽出来就好了。所以可以抽出来,写一个泛型使其变成一个抽象的基类,继承自BaseAdapter.其子类只需要去关心其getView方法。

     1 public abstract class MyListAdapter<T> extends BaseAdapter {
     2     private Context context;
     3     private List<T> list;
     4 
     5     public MyListAdapter(Context context, List<T> list) {
     6         this.context = context;
     7         this.list = list;
     8     }
     9 
    10     @Override
    11     public int getCount() {
    12         return list == null ? 0 : list.size();
    13     }
    14 
    15     @Override
    16     public Object getItem(int position) {
    17         return list.get(position);
    18     }
    19 
    20     @Override
    21     public long getItemId(int position) {
    22         return position;
    23     }
    24 }

    好像没什么不对,但是这也没解决多少问题呀,要是我们在写大项目的时候还可以抽点时间出来打LOL拿个首胜什么的就更好了。

    再来看看getView方法,基本都是先判断ViewHolder是否为空,为空则去Inflate一个xml文件进来,再绑定下视图,设置一个标记,不为空的时候直接引用标记。

    或许这里我们可以试一下在ViewHolder上做点什么。

    我们要是想把ViewHolder提取出来,只能把每一个Item都固定在ViewHolder里面,而Item又不是固定的,怎么办?

    要是我们可以把这个Item直接作为参数传进来就好了,可是传控件好像不能区分,仔细一想,我们能看到一个控件对应着一个id,这个好像可以用HashMap的键值对处理。

    而键值由于是Int型的,在新的java API中明确表示在键值为Integer的HashMap中我们要用SparseArray作代替,这样不仅简单,而且性能更优。

    我们尝试着封装一下ViewHolder

     1 public class ViewHolder {
     2     //现在对于int作为键的官方推荐用SparseArray替代HashMap
     3     private final SparseArray<View> views;
     4     private int position;
     5     private View convertView;
     6     private Context context;
     7 
     8     private ViewHolder(Context context,ViewGroup parent, int layoutId, int position) {
     9         this.context = context;
    10         this.views = new SparseArray<>();
    11         this.convertView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
    12         convertView.setTag(this);
    13     }
    14 
    15     /**
    16      * 拿到一个ViewHolder对象
    17      */
    18     public static ViewHolder get(View convertView, ViewGroup parent, int layoutId, int position) {
    19         if (convertView == null) {
    20             return new ViewHolder(parent.getContext(),parent, layoutId, position);
    21         }
    22         return (ViewHolder) convertView.getTag();
    23     }
    24 
    25     /**
    26      * 通过控件的Id获取对于的控件,如果没有则加入views
    27      */
    28     public <T extends View> T getView(int viewId) {
    29         View view = views.get(viewId);
    30         if (view == null) {
    31             view = convertView.findViewById(viewId);
    32             views.put(viewId, view);
    33         }
    34         return (T) view;
    35     }
    36 }

    这样的话我们的getView可能会变成这样。

    1 @Override  
    2     public View getView(int position, View convertView, ViewGroup parent){  
    3         ViewHolder viewHolder = ViewHolder.get(convertView, parent,  
    4                 R.layout.list_item, position);  
    5         TextView mTitle = viewHolder.getView(R.id.id_tv_title);  
    6         mTitle.setText(((Data) list.get(position)).getText());  
    7         //这里就不设置ImageView了
    8         return viewHolder.getConvertView();  
    9     } 

    好吧。与其这样。我们不如直接写在Activity中。

    并且如果我们想设置东西也许可以在Holder里面设置,我们可以试一试。

    封装后的ViewHolder是这样。

     1 package com.example.nanchen.commonadapterforlistviewdemo;
     2 
     3 import android.content.Context;
     4 import android.graphics.Bitmap;
     5 import android.util.SparseArray;
     6 import android.view.LayoutInflater;
     7 import android.view.View;
     8 import android.view.ViewGroup;
     9 import android.widget.ImageView;
    10 import android.widget.TextView;
    11 
    12 import com.squareup.picasso.Picasso;
    13 
    14 /**
    15  * 万能适配器的ViewHolder
    16  * Created by 南尘 on 16-7-28.
    17  */
    18 public class ViewHolder {
    19     //现在对于int作为键的官方推荐用SparseArray替代HashMap
    20     private final SparseArray<View> views;
    21     private int position;
    22     private View convertView;
    23     private Context context;
    24 
    25     private ViewHolder(Context context,ViewGroup parent, int layoutId, int position) {
    26         this.context = context;
    27         this.views = new SparseArray<>();
    28         this.convertView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
    29         convertView.setTag(this);
    30     }
    31 
    32     /**
    33      * 拿到一个ViewHolder对象
    34      */
    35     public static ViewHolder get(View convertView, ViewGroup parent, int layoutId, int position) {
    36         if (convertView == null) {
    37             return new ViewHolder(parent.getContext(),parent, layoutId, position);
    38         }
    39         return (ViewHolder) convertView.getTag();
    40     }
    41 
    42     /**
    43      * 通过控件的Id获取对于的控件,如果没有则加入views
    44      */
    45     public <T extends View> T getView(int viewId) {
    46         View view = views.get(viewId);
    47         if (view == null) {
    48             view = convertView.findViewById(viewId);
    49             views.put(viewId, view);
    50         }
    51         return (T) view;
    52     }
    53 
    54     public View getConvertView() {
    55         return convertView;
    56     }
    57 
    58     /**
    59      * 设置字符串
    60      */
    61     public ViewHolder setText(int viewId,String text){
    62         TextView tv = getView(viewId);
    63         tv.setText(text);
    64         return this;
    65     }
    66 
    67     /**
    68      * 设置图片
    69      */
    70     public ViewHolder setImageResource(int viewId,int drawableId){
    71         ImageView iv = getView(viewId);
    72         iv.setImageResource(drawableId);
    73         return this;
    74     }
    75 
    76     /**
    77      * 设置图片
    78      */
    79     public ViewHolder setImageBitmap(int viewId, Bitmap bitmap){
    80         ImageView iv = getView(viewId);
    81         iv.setImageBitmap(bitmap);
    82         return this;
    83     }
    84 
    85     /**
    86      * 设置图片
    87      */
    88     public ViewHolder setImageByUrl(int viewId,String url){
    89         Picasso.with(context).load(url).into((ImageView) getView(viewId));
    90 //        ImageLoader.getInstance().init(ImageLoaderConfiguration.createDefault(context));
    91 //        ImageLoader.getInstance().displayImage(url, (ImageView) getView(viewId));
    92         return this;
    93     }
    94 
    95     public int getPosition(){
    96         return position;
    97     }
    98 }

    上面的图片网络加载我用Picasso加载框架,这个网上很多图片加载框架,我前面博客也有很多Demo,大家可以自行查找。

    再看看我们的万能适配器,这里我们把它写做一个抽象类,传入一个泛型作为参数。

     1 package com.example.nanchen.commonadapterforlistviewdemo;
     2 
     3 import android.content.Context;
     4 import android.view.View;
     5 import android.view.ViewGroup;
     6 import android.widget.BaseAdapter;
     7 
     8 import java.util.List;
     9 
    10 /**
    11  * 打造ListView的万能适配器
    12  * Created by 南尘 on 16-7-28.
    13  */
    14 public abstract class CommonAdaper<T> extends BaseAdapter {
    15     private Context context;
    16     private List<T> list;
    17 
    18 
    19     public CommonAdaper(Context context, List<T> list) {
    20         this.context = context;
    21         this.list = list;
    22     }
    23 
    24     @Override
    25     public int getCount() {
    26         return list == null ? 0 : list.size();
    27     }
    28 
    29     @Override
    30     public T getItem(int position) {
    31         return list.get(position);
    32     }
    33 
    34     @Override
    35     public long getItemId(int position) {
    36         return position;
    37     }
    38 
    39     @Override
    40     public View getView(int i, View view, ViewGroup viewGroup) {
    41         ViewHolder holder = ViewHolder.get(view,viewGroup,R.layout.list_item,i);
    42         convert(holder,getItem(i));
    43         return holder.getConvertView();
    44     }
    45 
    46     public abstract void convert(ViewHolder holder,T item);
    47 
    48 }

    再看看我们主页面怎么调用的。

     1 package com.example.nanchen.commonadapterforlistviewdemo;
     2 
     3 import android.support.v7.app.AppCompatActivity;
     4 import android.os.Bundle;
     5 import android.widget.ListView;
     6 
     7 import java.util.ArrayList;
     8 import java.util.List;
     9 
    10 public class MainActivity extends AppCompatActivity {
    11 
    12     private ListView listView;
    13     private List<Data> list;
    14 
    15     @Override
    16     protected void onCreate(Bundle savedInstanceState) {
    17         super.onCreate(savedInstanceState);
    18         setContentView(R.layout.activity_main);
    19 
    20         listView = (ListView) findViewById(R.id.main_lv);
    21         initList();
    22 
    23         listView.setAdapter(new CommonAdaper<Data>(this,list) {
    24             @Override
    25             public void convert(ViewHolder holder, Data item) {
    26                 holder.setText(R.id.item_text,item.getText());
    27                 if (item.getImageUrl() != null){
    28                     holder.setImageByUrl(R.id.item_image,item.getImageUrl());
    29                 }else {
    30                     holder.setImageResource(R.id.item_image,item.getImageId());
    31                 }
    32             }
    33         });
    34     }
    35 
    36     private void initList() {
    37         list = new ArrayList<>();
    38         for (int i = 0; i < 5; i++) {
    39             list.add(new Data("本地 "+i,R.mipmap.ic_launcher));
    40         }
    41 
    42         for (int i = 0; i < 5; i++) {
    43             list.add(new Data("网络 "+i,"http://pic.cnblogs.com/face/845964/20160301162812.png"));
    44         }
    45     }
    46 }

    最后上我写的两个xml

    一个是Activity_main.xml

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <RelativeLayout
     3     xmlns:android="http://schemas.android.com/apk/res/android"
     4     xmlns:tools="http://schemas.android.com/tools"
     5     android:layout_width="match_parent"
     6     android:layout_height="match_parent"
     7     tools:context="com.example.nanchen.commonadapterforlistviewdemo.MainActivity">
     8 
     9 
    10     <ListView
    11         android:layout_width="match_parent"
    12         android:layout_height="match_parent"
    13         android:id="@+id/main_lv"/>
    14 </RelativeLayout>

    list_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:orientation="horizontal">
    
        <ImageView
            android:id="@+id/item_image"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:src="@mipmap/ic_launcher"/>
    
    
        <TextView
            android:id="@+id/item_text"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="内容"/>
    
    </LinearLayout>

    这个你想怎么定义就想定义了。

    本人亲测这个东西可以用,还没入坑的小伙伴赶快入坑吧。

    明天我可能还会带来万能的RecyclerView的适配器,还请大家持续关注~~~~

    ———————————————————————————————————————————————————————————————————————————— 

    7月30日补充:

    今天在自己思考写最近大火的RecyclerView的万能适配器的时候参考这边的时候,发现在MainActivity中竟然不能设置其他的布局,才发现自己之前的逻辑有点问题,应该把Layout的ID也作为参数传到Adater去中。看来自己有时候想的也不是很周到,大家看的时候也要多加思考呀。

    这是改动后的Adpter和ViewHolder

     1 package com.example.nanchen.commonadapterforlistviewdemo;
     2 
     3 import android.content.Context;
     4 import android.view.LayoutInflater;
     5 import android.view.View;
     6 import android.view.ViewGroup;
     7 import android.widget.BaseAdapter;
     8 
     9 import java.util.List;
    10 
    11 /**
    12  * 打造ListView的万能适配器
    13  * Created by 南尘 on 16-7-28.
    14  */
    15 public abstract class CommonAdaper<T> extends BaseAdapter {
    16     private Context context;
    17     private List<T> list;
    18     private LayoutInflater inflater;
    19     private int itemLayoutId;
    20 
    21 
    22     public CommonAdaper(Context context, List<T> list,int itemLayoutId) {
    23         this.context = context;
    24         this.list = list;
    25         this.itemLayoutId = itemLayoutId;
    26         inflater = LayoutInflater.from(context);
    27     }
    28 
    29     @Override
    30     public int getCount() {
    31         return list == null ? 0 : list.size();
    32     }
    33 
    34     @Override
    35     public T getItem(int position) {
    36         return list.get(position);
    37     }
    38 
    39     @Override
    40     public long getItemId(int position) {
    41         return position;
    42     }
    43 
    44     @Override
    45     public View getView(int position, View convertView, ViewGroup parent) {
    46         ViewHolder holder = getViewHolder(position,convertView,parent);
    47         convert(holder,getItem(position));
    48         return holder.getConvertView();
    49     }
    50 
    51     public abstract void convert(ViewHolder holder,T item);
    52 
    53     private ViewHolder getViewHolder(int position,View convertView,ViewGroup parent){
    54         return ViewHolder.get(context,convertView,parent,itemLayoutId,position);
    55     }
    56 
    57 }

    思想里面应该很清楚了吧。

     1 package com.example.nanchen.commonadapterforlistviewdemo;
     2 
     3 import android.content.Context;
     4 import android.graphics.Bitmap;
     5 import android.util.SparseArray;
     6 import android.view.LayoutInflater;
     7 import android.view.View;
     8 import android.view.ViewGroup;
     9 import android.widget.ImageView;
    10 import android.widget.TextView;
    11 
    12 import com.squareup.picasso.Picasso;
    13 
    14 /**
    15  * 万能适配器的ViewHolder
    16  * Created by 南尘 on 16-7-28.
    17  */
    18 public class ViewHolder {
    19     //现在对于int作为键的官方推荐用SparseArray替代HashMap
    20     private final SparseArray<View> views;
    21     private View convertView;
    22     private Context context;
    23 
    24     private ViewHolder(Context context,ViewGroup parent,int itemLayoutId,int position) {
    25         this.context = context;
    26         this.views = new SparseArray<>();
    27         this.convertView = LayoutInflater.from(context).inflate(itemLayoutId,parent,false);
    28         convertView.setTag(this);
    29     }
    30 
    31     /**
    32      * 拿到一个ViewHolder对象
    33      */
    34     public static ViewHolder get(Context context,View convertView, ViewGroup parent, int layoutId, int position) {
    35         if (convertView == null) {
    36             return new ViewHolder(context,parent, layoutId, position);
    37         }
    38         return (ViewHolder) convertView.getTag();
    39     }
    40 
    41     /**
    42      * 通过控件的Id获取对于的控件,如果没有则加入views
    43      */
    44     public <T extends View> T getView(int viewId) {
    45         View view = views.get(viewId);
    46         if (view == null) {
    47             view = convertView.findViewById(viewId);
    48             views.put(viewId, view);
    49         }
    50         return (T) view;
    51     }
    52 
    53     public View getConvertView() {
    54         return convertView;
    55     }
    56 
    57     /**
    58      * 设置字符串
    59      */
    60     public ViewHolder setText(int viewId,String text){
    61         TextView tv = getView(viewId);
    62         tv.setText(text);
    63         return this;
    64     }
    65 
    66     /**
    67      * 设置图片
    68      */
    69     public ViewHolder setImageResource(int viewId,int drawableId){
    70         ImageView iv = getView(viewId);
    71         iv.setImageResource(drawableId);
    72         return this;
    73     }
    74 
    75     /**
    76      * 设置图片
    77      */
    78     public ViewHolder setImageBitmap(int viewId, Bitmap bitmap){
    79         ImageView iv = getView(viewId);
    80         iv.setImageBitmap(bitmap);
    81         return this;
    82     }
    83 
    84     /**
    85      * 设置图片
    86      */
    87     public ViewHolder setImageByUrl(int viewId,String url){
    88         Picasso.with(context).load(url).into((ImageView) getView(viewId));
    89 //        ImageLoader.getInstance().init(ImageLoaderConfiguration.createDefault(context));
    90 //        ImageLoader.getInstance().displayImage(url, (ImageView) getView(viewId));
    91         return this;
    92     }
    93 }

    同步重新同步至Github:https://github.com/nanchen2251/CommonAdapterListViewDemo

  • 相关阅读:
    【LOJ】#2071. 「JSOI2016」最佳团体
    【LOJ】#2070. 「SDOI2016」平凡的骰子
    【LOJ】#2069. 「SDOI2016」齿轮
    【LOJ】#2068. 「SDOI2016」探险路线
    【LOJ】#2067. 「SDOI2016」硬币游戏
    【LOJ】#2066. 「SDOI2016」墙上的句子
    【LOJ】#2065. 「SDOI2016」模式字符串
    【LOJ】#2064. 「HAOI2016」找相同字符
    【LOJ】#2063. 「HAOI2016」字符合并
    【LOJ】#2062. 「HAOI2016」地图
  • 原文地址:https://www.cnblogs.com/liushilin/p/5716306.html
Copyright © 2011-2022 走看看