高灵活低耦合Adapter快速开发攻略

原创
2016/06/27 00:46
阅读数 6.2K

Android开发中经常需要使用Adapter。

传统方法是自定义一个Adapter并继承AndroidSDK内的BaseAdapter,

这种方式代码量大,耦合度高,灵活性差(各种监听事件需要对View单独写,或者自定义一个比较统一的方法);

 

而ZBLibrary中的BaseViewAdapter不仅预处理了通用方法(getCount,getItem等) 以及 Item复用逻辑,

而且将对Item的处理代码单独写在一个BaseView(提供了大量常用方法)的子类,

所以使用继承BaseViewAdapter的新方式能大幅精简代码,并且带来高灵活性和低耦合度。

 

比如这个界面

 

传统方式实现的CommonAdapter代码如下

  1 package com.example.quickadapter;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 
  6 import zuo.biao.library.ui.WebViewActivity;
  7 import zuo.biao.library.util.ImageLoaderUtil;
  8 import android.app.Activity;
  9 import android.view.LayoutInflater;
 10 import android.view.View;
 11 import android.view.View.OnClickListener;
 12 import android.view.ViewGroup;
 13 import android.widget.BaseAdapter;
 14 import android.widget.ImageView;
 15 import android.widget.TextView;
 16 
 17 public class CommonAdapter extends BaseAdapter {
 18 
 19     private Activity context;
 20     private List<User> list;
 21     private LayoutInflater inflater;
 22     public CommonAdapter(Activity context, List<User> list) {
 23         this.context = context;
 24         this.list = new ArrayList<>(list);
 25         this.inflater = context.getLayoutInflater();
 26     }
 27 
 28     @Override
 29     public int getCount() {
 30         return list.size();
 31     }
 32 
 33     @Override
 34     public User getItem(int position) {
 35         return list.get(position);
 36     }
 37 
 38     @Override
 39     public long getItemId(int position) {
 40         return position;
 41     }
 42 
 43     public synchronized void refresh(List<User> list) {
 44         if (list != null && list.size() > 0) {
 45             this.list = new ArrayList<>(list);
 46         }
 47         notifyDataSetChanged();
 48     }
 49 
 50     @Override
 51     public View getView(final int position, View convertView, ViewGroup parent) {
 52         ViewHolder holder = convertView == null ? null : (ViewHolder) convertView.getTag();
 53         if (holder == null) {
 54             convertView = inflater.inflate(R.layout.user_item, parent, false);
 55             holder = new ViewHolder();
 56 
 57             holder.ivUserItemHead = (ImageView) convertView.findViewById(R.id.ivUserItemHead);
 58             holder.ivUserItemStar = (ImageView) convertView.findViewById(R.id.ivUserItemStar);
 59 
 60             holder.tvUserItemSex = (TextView) convertView.findViewById(R.id.tvUserItemSex);
 61 
 62             holder.tvUserItemName = (TextView) convertView.findViewById(R.id.tvUserItemName);
 63             holder.tvUserItemId = (TextView) convertView.findViewById(R.id.tvUserItemId);
 64             holder.tvUserItemNumber = (TextView) convertView.findViewById(R.id.tvUserItemNumber);
 65 
 66             convertView.setTag(holder);
 67         }
 68 
 69         final User user = getItem(position);
 70 
 71         ImageLoaderUtil.loadImage(holder.ivUserItemHead, user.getHead(), ImageLoaderUtil.TYPE_OVAL);
 72         holder.ivUserItemStar.setImageResource(user.getStarred() ? R.drawable.star_light : R.drawable.star);
 73 
 74         holder.tvUserItemSex.setBackgroundResource(user.getSex() == User.SEX_FEMAIL ? R.drawable.circle_pink : R.drawable.circle_blue);
 75         holder.tvUserItemSex.setText(user.getSex() == User.SEX_FEMAIL ?  "女" : "男");
 76         holder.tvUserItemSex.setTextColor(context.getResources().getColor(user.getSex() == User.SEX_FEMAIL ? R.color.pink : R.color.blue));
 77         
 78         holder.tvUserItemName.setText("" + user.getName());
 79         holder.tvUserItemId.setText("ID:" + user.getId());
 80         holder.tvUserItemNumber.setText("Phone:" + user.getPhone());
 81 
 82         holder.ivUserItemHead.setOnClickListener(new OnClickListener() {
 83 
 84             @Override
 85             public void onClick(View v) {
 86                 context.startActivity(WebViewActivity.createIntent(context, user.getName(), user.getHead()));
 87             }
 88         });
 89 
 90         holder.ivUserItemStar.setOnClickListener(new OnClickListener() {
 91 
 92             @Override
 93             public void onClick(View v) {
 94                 user.setStarred(! user.getStarred());
 95                 list.set(position, user);
 96                 refresh(null);
 97             }
 98         });
 99 
100         holder.tvUserItemSex.setOnClickListener(new OnClickListener() {
101 
102             @Override
103             public void onClick(View v) {
104                 user.setSex(user.getSex() == User.SEX_FEMAIL ? User.SEX_MAIL : User.SEX_FEMAIL);
105                 list.set(position, user);
106                 refresh(null);
107             }
108         });
109 
110         return convertView;
111     }
112 
113     class ViewHolder {
114         public ImageView ivUserItemHead;
115         public ImageView ivUserItemStar;
116 
117         public TextView tvUserItemSex;
118 
119         public TextView tvUserItemName;
120         public TextView tvUserItemId;
121         public TextView tvUserItemNumber;
122     }
123 }

 

继承BaseViewAdapter的新方式实现的QuickAdapter代码如下

  1 package com.example.quickadapter;
  2 
  3 import java.util.List;
  4 
  5 import zuo.biao.library.base.BaseView;
  6 import zuo.biao.library.base.BaseViewAdapter;
  7 import zuo.biao.library.ui.WebViewActivity;
  8 import zuo.biao.library.util.ImageLoaderUtil;
  9 import zuo.biao.library.util.StringUtil;
 10 import android.annotation.SuppressLint;
 11 import android.app.Activity;
 12 import android.content.res.Resources;
 13 import android.view.LayoutInflater;
 14 import android.view.View;
 15 import android.view.View.OnClickListener;
 16 import android.view.ViewGroup;
 17 import android.widget.ImageView;
 18 import android.widget.TextView;
 19 
 20 import com.example.quickadapter.QuickAdapter.UserView;
 21 
 22 public class QuickAdapter extends BaseViewAdapter<User, UserView> {
 23 
 24     public QuickAdapter(Activity context, List<User> list) {
 25         super(context, list);
 26     }
 27 
 28     @Override
 29     public UserView createView(int position, View convertView, ViewGroup parent) {
 30         return new UserView(context, resources);
 31     }
 32 
 33     public class UserView extends BaseView<User> implements OnClickListener {
 34         public UserView(Activity context, Resources resources) {
 35             super(context, resources);
 36         }
 37 
 38         public ImageView ivUserViewHead;
 39         public ImageView ivUserViewStar;
 40         
 41         public TextView tvUserViewSex;
 42 
 43         public TextView tvUserViewName;
 44         public TextView tvUserViewId;
 45         public TextView tvUserViewNumber;
 46         @SuppressLint("InflateParams")
 47         @Override
 48         public View createView(LayoutInflater inflater) {
 49             convertView = inflater.inflate(R.layout.user_view, null);
 50 
 51             ivUserViewHead = findViewById(R.id.ivUserViewHead, this);
 52             ivUserViewStar = findViewById(R.id.ivUserViewStar, this);
 53 
 54             tvUserViewSex = findViewById(R.id.tvUserViewSex, this);
 55             
 56             tvUserViewName = findViewById(R.id.tvUserViewName);
 57             tvUserViewId = findViewById(R.id.tvUserViewId);
 58             tvUserViewNumber = findViewById(R.id.tvUserViewNumber);
 59 
 60             return convertView;
 61         }
 62 
 63         @Override
 64         public void setView(User data){
 65             if (data == null) {
 66                 return;
 67             }
 68             this.data = data;
 69 
 70             ImageLoaderUtil.loadImage(ivUserViewHead, data.getHead(), ImageLoaderUtil.TYPE_OVAL);
 71             ivUserViewStar.setImageResource(data.getStarred() ? R.drawable.star_light : R.drawable.star);
 72             
 73             tvUserViewSex.setBackgroundResource(data.getSex() == User.SEX_FEMAIL ? R.drawable.circle_pink : R.drawable.circle_blue);
 74             tvUserViewSex.setText(data.getSex() == User.SEX_FEMAIL ?  "女" : "男");
 75             tvUserViewSex.setTextColor(getColor(data.getSex() == User.SEX_FEMAIL ? R.color.pink : R.color.blue));
 76             
 77             tvUserViewName.setText(StringUtil.getTrimedString(data.getName()));
 78             tvUserViewId.setText("ID:" + data.getId());
 79             tvUserViewNumber.setText("Phone:" + StringUtil.getNoBlankString(data.getPhone()));
 80         }
 81 
 82         @Override
 83         public void onClick(View v) {
 84             if (data == null) {
 85                 return;
 86             }
 87             switch (v.getId()) {
 88             case R.id.ivUserViewHead:
 89                 toActivity(WebViewActivity.createIntent(context, data.getName(), data.getHead()));
 90                 break;
 91             case R.id.ivUserViewStar:
 92                 data.setStarred(! data.getStarred());
 93                 setView(data);
 94                 break;
 95             case R.id.tvUserViewSex:
 96                 data.setSex(data.getSex() == User.SEX_FEMAIL ? User.SEX_MAIL : User.SEX_FEMAIL);
 97                 setView(data);
 98                 break;
 99             default:
100                 break;
101             }        
102         }
103     }
104 }


 

 

看下ZBLibrary中的BaseView,里面有大量常用的 且 AndroidSDK内的BaseAdapter没有提供的 方法。

package zuo.biao.library.base;

import zuo.biao.library.util.CommonUtil;
import zuo.biao.library.util.Log;
import android.app.Activity;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;

/**基础自定义View
 * @author Lemon
 * @param <T> 数据模型(model/JavaBean)类
 * @see #onDestroy
 * @use extends BaseView<T>, 具体参考.DemoView
 */
public abstract class BaseView<T> {
	private static final String TAG = "BaseView";

	/**
	 * 传入的Activity,可在子类直接使用
	 */
	protected Activity context;
	protected Resources resources;
	public BaseView(Activity context, Resources resources) {
		this.context = context;
		this.resources = resources == null ? context.getResources() : resources;
	}

	/**数据改变回调接口
	 * (Object) getData() - 改变的数据
	 */
	public interface OnDataChangedListener {
		void onDataChanged();
	}

	public OnDataChangedListener onDataChangedListener;//数据改变回调监听类的实例
	/**设置数据改变事件监听类
	 * @param l
	 */
	public void setOnDataChangedListener(OnDataChangedListener l) {
		onDataChangedListener = l;
	}

	public OnTouchListener onTouchListener;//接触View回调监听类的实例
	/**设置接触View事件监听类
	 * @param l
	 */
	public void setOnTouchListener(OnTouchListener l) {
		onTouchListener = l;
	}

	public OnClickListener onClickListener;//点击View回调监听类的实例
	/**设置点击View事件监听类
	 * @param l
	 */
	public void setOnClickListener(OnClickListener l) {
		onClickListener = l;
	}

	public OnLongClickListener onLongClickListener;//长按View回调监听类的实例
	/**设置长按View事件监听类
	 * @param l
	 */
	public void setOnLongClickListener(OnLongClickListener l) {
		onLongClickListener = l;
	}



	/**
	 * 子类整个视图,可在子类直接使用
	 * @must createView方法内对其赋值且不能为null
	 */
	protected View convertView = null;

	/**通过id查找并获取控件,使用时不需要强转
	 * @param id
	 * @return 
	 */
	@SuppressWarnings("unchecked")
	public <V extends View> V findViewById(int id) {
		return (V) convertView.findViewById(id);
	}
	/**通过id查找并获取控件,并setOnClickListener
	 * @param id
	 * @param l
	 * @return
	 */
	public <V extends View> V findViewById(int id, OnClickListener l) {
		V v = findViewById(id);
		v.setOnClickListener(l);
		return v;
	}
	/**创建一个新的View
	 * @return
	 */
	public abstract View createView(@NonNull LayoutInflater inflater);

	/**获取convertView的宽度
	 * @warn 只能在createView后使用
	 * @return
	 */
	public int getWidth() {
		return convertView.getWidth();
	}
	/**获取convertView的高度
	 * @warn 只能在createView后使用
	 * @return
	 */
	public int getHeight() {
		return convertView.getHeight();
	}

	/**
	 * data在列表中的位置
	 * @must 只使用setView(int position, T data)方法来设置position,保证position与data对应正确
	 */
	protected int position = 0;
	/**获取data在列表中的位置
	 */
	public int getPosition() {
		return position;
	}

	protected T data = null;
	/**获取数据
	 * @return
	 */
	public T getData() {
		return data;
	}

	/**设置并显示内容
	 * @warn 只能在createView后使用
	 * @param position - data在列表中的位置
	 * @param data - 传入的数据
	 */
	public void setView(int position, T data) {
		this.position = position;
		setView(data);
	}
	/**设置并显示内容
	 * @warn 只能在createView后使用
	 * @param data - 传入的数据
	 */
	public abstract void setView(T data);

	/**获取可见性
	 * @warn 只能在createView后使用
	 * @return 可见性 (View.VISIBLE, View.GONE, View.INVISIBLE);
	 */
	public int getVisibility() {
		return convertView.getVisibility();
	}
	/**设置可见性
	 * @warn 只能在createView后使用
	 * @param visibility - 可见性 (View.VISIBLE, View.GONE, View.INVISIBLE);
	 */
	public void setVisibility(int visibility) {
		convertView.setVisibility(visibility);
	}


	/**设置背景
	 * @warn 只能在createView后使用
	 * @param resId
	 */
	public void setBackground(int resId) {
		if (resId > 0 && convertView != null) {
			try {
				convertView.setBackgroundResource(resId);
			} catch (Exception e) {
				Log.e(TAG, "setBackground   try { convertView.setBackgroundResource(resId);" +
						" \n >> } catch (Exception e) { \n" + e.getMessage());
			}
		}
	}


	//resources方法<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
	public String getString(int id) {
		return resources.getString(id);
	}
	public int getColor(int id) {
		return resources.getColor(id);
	}
	public Drawable getDrawable(int id) {
		return resources.getDrawable(id);
	}
	public float getDimension(int id) {
		return resources.getDimension(id);
	}
	//resources方法>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


	//show short toast 方法<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
	/**快捷显示short toast方法,需要long toast就用 Toast.makeText(string, Toast.LENGTH_LONG).show(); ---不常用所以这个类里不写
	 * @param stringResId
	 */
	public void showShortToast(int stringResId) {
		CommonUtil.showShortToast(context, stringResId);
	}
	/**快捷显示short toast方法,需要long toast就用 Toast.makeText(string, Toast.LENGTH_LONG).show(); ---不常用所以这个类里不写
	 * @param string
	 */
	public void showShortToast(String string) {
		CommonUtil.showShortToast(context, string);
	}
	//show short toast 方法>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


	//启动新Activity方法<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

	/**打开新的Activity,向左滑入效果
	 * @param intent
	 */
	public void toActivity(final Intent intent) {
		CommonUtil.toActivity(context, intent);
	}
	/**打开新的Activity
	 * @param intent
	 * @param showAnimation
	 */
	public void toActivity(final Intent intent, final boolean showAnimation) {
		CommonUtil.toActivity(context, intent, showAnimation);
	}
	/**打开新的Activity,向左滑入效果
	 * @param intent
	 * @param requestCode
	 */
	public void toActivity(final Intent intent, final int requestCode) {
		CommonUtil.toActivity(context, intent, requestCode);
	}
	/**打开新的Activity
	 * @param intent
	 * @param requestCode
	 * @param showAnimation
	 */
	public void toActivity(final Intent intent, final int requestCode, final boolean showAnimation) {
		CommonUtil.toActivity(context, intent, requestCode, showAnimation);
	}
	//启动新Activity方法>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

	/**销毁并回收内存,建议在对应的View占用大量内存时使用
	 * @warn 只能在UI线程中调用
	 */
	public void onDestroy() {
		if (convertView != null) {
			try {
				convertView.destroyDrawingCache();
			} catch (Exception e) {
				Log.w(TAG, "onDestroy  try { convertView.destroyDrawingCache();" +
						" >> } catch (Exception e) {\n" + e.getMessage());
			}
			convertView = null;
		}
		
		onDataChangedListener = null;
		onTouchListener = null;
		onClickListener = null;
		onLongClickListener = null;
		
		data = null;
		position = 0;
		
		context = null;
	}

}

 

 

本示例工程QuickAdapter

下载地址

http://files.cnblogs.com/files/tommylemon/QuickAdapter.zip

下载试用

QuickAdapter.apk

 

用到的开源库ZBLibrary(欢迎Star,欢迎Fork)

 

下载地址(欢迎Star,欢迎Fork)

https://github.com/TommyLemon/Android-ZBLibrary

下载试用

ZBLibraryDemoApp.apk

 

 

 

展开阅读全文
打赏
2
56 收藏
分享
加载中

引用来自“付三”的评论

‘SparseArray<View>’只是为了实现一种缓存机制,避免多次调用findViewById,为了实现一个统一的ViewHolder 不存在多占资源的情况了 你实例中的UserView就相当一个功能丰富的ViewHoler 一样要引用到那些控件啊

引用来自“TommyLemon”的评论

提个建议,博客中加上效果图片、Demo的APK以及源码,这样更容易被读者接受和推荐0
好的
2016/06/30 23:34
回复
举报

引用来自“付三”的评论

‘SparseArray<View>’只是为了实现一种缓存机制,避免多次调用findViewById,为了实现一个统一的ViewHolder 不存在多占资源的情况了 你实例中的UserView就相当一个功能丰富的ViewHoler 一样要引用到那些控件啊
提个建议,博客中加上效果图片、Demo的APK以及源码,这样更容易被读者接受和推荐0
2016/06/30 22:58
回复
举报

引用来自“付三”的评论

‘SparseArray<View>’只是为了实现一种缓存机制,避免多次调用findViewById,为了实现一个统一的ViewHolder 不存在多占资源的情况了 你实例中的UserView就相当一个功能丰富的ViewHoler 一样要引用到那些控件啊
UserView相当于ViewHolder这个是对的。
本博客两种方法都实现了item的复用,避免了多次调用findViewById。(BaseViewAdapter的getView就有复用逻辑)。
UserAdapter中用UserView,相当于那篇博客BaseAdapter中用ViewHolder
2016/06/30 22:56
回复
举报
‘SparseArray<View>’只是为了实现一种缓存机制,避免多次调用findViewById,为了实现一个统一的ViewHolder 不存在多占资源的情况了 你实例中的UserView就相当一个功能丰富的ViewHoler 一样要引用到那些控件啊
2016/06/30 22:46
回复
举报

引用来自“付三”的评论

android中的方法数有限,BaseView的封装略重,每个实现必须继承它,其中的一些事件监听,在常规场景下,使用概率低。还要跳转逻辑,显示toast这些功能应该封装为全局工具类,这里没有必要了。不好意思 个人观点
CommonUtil就是你说的全局工具类,你看看BaseView中的showShortToast,toActivity这些方法,都是调用CommonUtil的相应方法,比子类直接调用省事。
public void showShortToast(int stringResId) {
CommonUtil.showShortToast(context, stringResId);
}
public void toActivity(final Intent intent) {
CommonUtil.toActivity(context, intent);
}
CommonUtil.toActivity(context, intent, requestCode);

至于封装过重,性能测试过不用担心,而且这些封装都没有SparseArray占资源啊,毕竟View是很耗资源的,图片加载一般都得用LruCache
2016/06/30 22:29
回复
举报
android中的方法数有限,BaseView的封装略重,每个实现必须继承它,其中的一些事件监听,在常规场景下,使用概率低。还要跳转逻辑,显示toast这些功能应该封装为全局工具类,这里没有必要了。不好意思 个人观点
2016/06/30 22:19
回复
举报

引用来自“付三”的评论

http://www.jianshu.com/p/1f6c66669cd6 可以看看这个吧 更加简洁

引用来自“TommyLemon”的评论

那篇博客的作者名字是付三君,你这个是付三,应该是同一个人吧?0

引用来自“付三”的评论

嗯嗯 谢谢你的建议 只是我们抽象方式不太一样 那个是适合复杂业务的了 是从五百万级日活产品中抽象出来的
互相学习吧,我这个漏了itemVeiwType,之后会补上83
2016/06/30 22:16
回复
举报

引用来自“付三”的评论

http://www.jianshu.com/p/1f6c66669cd6 可以看看这个吧 更加简洁

引用来自“TommyLemon”的评论

那篇博客的作者名字是付三君,你这个是付三,应该是同一个人吧?0
嗯嗯 谢谢你的建议 只是我们抽象方式不太一样 那个是适合复杂业务的了 是从五百万级日活产品中抽象出来的
2016/06/30 22:01
回复
举报

引用来自“付三”的评论

http://www.jianshu.com/p/1f6c66669cd6 可以看看这个吧 更加简洁
那篇博客的作者名字是付三君,你这个是付三,应该是同一个人吧?0
2016/06/30 21:44
回复
举报

引用来自“付三”的评论

http://www.jianshu.com/p/1f6c66669cd6 可以看看这个吧 更加简洁
如果拿这两个博客的示例直接比较显然是不公平的:
1.本博客示例QuickAdapter的item内View有6个;而那篇的只有2个。
2.本博客示例QuickAdapter的item内有3个onClickListener监听;而那篇博客示例没有,如果要写肯定比我这个麻烦。
3.本博客示例QuickAdapter的item内数据更复杂,涉及文字,颜色,图片,网络加载头像等;而那篇博客示例只有两个setText。

而且那篇博客推荐方法的缺点很明显:
1.不够灵活,不能统一地进行事件管理,不适合复杂业务(朋友圈,QQ空间等)。
2.没有对item完全解耦,不能单独使用。比如QQ空间的说说,如果用那种方法,那么说说列表的item和说说详情view要分别写两个。但如果用上面介绍的方法,一个TellView就够了,TellAdapter就8行。
3.没有提供toast,启动新activity,resources等常用方法。
4.性能不够好,SparseArray mViews占更多资源。

所以,那篇博客只有在实现非常简单的View时写法更简洁,并且不适合复杂View,更不适合复杂业务,Item也不能单独使用。
2016/06/30 21:33
回复
举报
更多评论
打赏
18 评论
56 收藏
2
分享
返回顶部
顶部