分享

打造android偷懒神器———ListView的万能适配器

 guitarhua 2017-08-13

如果你去做任何一个项目,我相信你都会跟我有一样的经历,最最普遍的就是列表显示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 list;20 21 public MyListAdapter(Context context, List list) {22 this.context = context;23 this.list = list;24 }25 26 @Override27 public int getCount() {28 return list == null ? 0 : list.size();29 }30 31 @Override32 public Object getItem(int position) {33 return list.get(position);34 }35 36 @Override37 public long getItemId(int position) {38 return position;39 }40 41 @Override42 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 extends BaseAdapter { 2 private Context context; 3 private List list; 4 5 public MyListAdapter(Context context, List list) { 6 this.context = context; 7 this.list = list; 8 } 9 10 @Override11 public int getCount() {12 return list == null ? 0 : list.size();13 }14 15 @Override16 public Object getItem(int position) {17 return list.get(position);18 }19 20 @Override21 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 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获取对于的控件,如果没有则加入views27 */28 public 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 * 万能适配器的ViewHolder16 * Created by 南尘 on 16-7-28.17 */18 public class ViewHolder {19 //现在对于int作为键的官方推荐用SparseArray替代HashMap20 private final SparseArray 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获取对于的控件,如果没有则加入views44 */45 public 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 extends BaseAdapter {15 private Context context;16 private List list;17 18 19 public CommonAdaper(Context context, List list) {20 this.context = context;21 this.list = list;22 }23 24 @Override25 public int getCount() {26 return list == null ? 0 : list.size();27 }28 29 @Override30 public T getItem(int position) {31 return list.get(position);32 }33 34 @Override35 public long getItemId(int position) {36 return position;37 }38 39 @Override40 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 list;14 15 @Override16 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(this,list) {24 @Override25 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;="">) {39 list.add(new Data('本地 '+i,R.mipmap.ic_launcher));40 }41 42 for (int i = 0; i < 5;="">) {43 list.add(new Data('网络 '+i,'http://pic.cnblogs.com/face/845964/20160301162812.png'));44 }45 }46 }
复制代码

最后上我写的两个xml

一个是Activity_main.xml

复制代码
1 2 RelativeLayout 3 xmlns:android='http://schemas./apk/res/android' 4 xmlns:tools='http://schemas./tools' 5 android:layout_width='match_parent' 6 android:layout_height='match_parent' 7 tools:context='com.example.nanchen.commonadapterforlistviewdemo.MainActivity'> 8 9 10 ListView11 android:layout_width='match_parent'12 android:layout_height='match_parent'13 android:id='@+id/main_lv'/>14
复制代码

list_item.xml

复制代码
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='内容'/>
复制代码

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

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

明天我可能还会带来万能的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 extends BaseAdapter {16 private Context context;17 private List list;18 private LayoutInflater inflater;19 private int itemLayoutId;20 21 22 public CommonAdaper(Context context, List list,int itemLayoutId) {23 this.context = context;24 this.list = list;25 this.itemLayoutId = itemLayoutId;26 inflater = LayoutInflater.from(context);27 }28 29 @Override30 public int getCount() {31 return list == null ? 0 : list.size();32 }33 34 @Override35 public T getItem(int position) {36 return list.get(position);37 }38 39 @Override40 public long getItemId(int position) {41 return position;42 }43 44 @Override45 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 * 万能适配器的ViewHolder16 * Created by 南尘 on 16-7-28.17 */18 public class ViewHolder {19 //现在对于int作为键的官方推荐用SparseArray替代HashMap20 private final SparseArray 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获取对于的控件,如果没有则加入views43 */44 public 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

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多