最近在开发一个新项目,由于是给平板用的,且公司平台用 recyclerview 比较麻烦,索性就用listview,毕竟也不用太多复杂布局;但由于用到比较多的listview,那么要每个多写吗?当然不用,我们可以写一个基类,然后继承即可。
参考鸿洋大神的文章 :http://blog.csdn.net/lmj623565791/article/details/38902805/
先看效果图:
再附上张图片:
上述的listview 只要几行代码就可以了:
mAdapter = new CommonAdapter<RunAppInfo>(SpeedUpActivity.this, datas, R.layout.listview_item) { @Override public void convert(ViewHolder viewHolder, RunAppInfo data) { viewHolder.setText(R.id.app_name,data.getName()); viewHolder.setText(R.id.app_size,data.getMemery()); viewHolder.setDrawable(R.id.app_icon,data.getIcon()); } }; mListView.setAdapter(mAdapter);
1、 常用listview的 baseadapter写法
public class AppContentAdapter extends BaseAdapter{ private Context mContext; private List<AppContent> mList; @SuppressWarnings("deprecation") public AppContentAdapter(Context context,List<AppContent> list){ mContext = context; mList = list; } @Override public int getCount() { // TODO Auto-generated method stub return mList.size(); } @Override public Object getItem(int arg0) { // TODO Auto-generated method stub return mList.get(arg0); } @Override public long getItemId(int arg0) { // TODO Auto-generated method stub return arg0; } class ViewHolder{ TextView appname; } @Override public View getView(int arg0, View contentView, ViewGroup arg2) { ViewHolder viewHolder = null; if (contentView == null) { contentView = LayoutInflater.from(mContext).inflate(R.layout.appcontent, null); viewHolder = new ViewHolder(); viewHolder.appname = (TextView) contentView.findViewById(R.id.app_name); contentView.setTag(viewHolder); }else{ viewHolder = (ViewHolder) contentView.getTag(); } viewHolder.appname.setText(mList.get(arg0).getName()); return contentView; } }
相信你已经对它很熟悉了,但是加入,我们一个项目要用到很多的 listview 我们其实是在做重复功,那我们能不能把这个listview 给封装起来呢?
2、通用的viewholder
当然可以,首先,我们先从getview来封装,注意到这是个 viewholder,那我们就先封装个 viewholder好了。首先,我们通过 setTag 和 getTag 这种方式来获取 view 的。那么实体类首先如下所示,getViewHolder 表示viewholder的实例:
public class ViewHolder { /** * 可以理解成单例吧,获取一个viewholder * @param context * @param converView * @param layout * @param parent * @param position * @return */ public static ViewHolder getViewHolder(Context context, View converView, int layout, ViewGroup parent,int position){ if (converView ==null){ //当为空时,我们需要实例化viewholder //注意这里,其实跟baseadapter的判断是一致的,其中settag也是在viewholder里 ViewHolder viewholder = new ... return viewholder; }else{ ViewHolder holder = (ViewHolder) converView.getTag(); return holder; } } }
在 getViewHolder 中,就是用来获取 viewholder 的,当然这里我们并未写实例化,只写了 getTag 而传递的参数我们也是比较熟悉,接着完善一下,提供一下setTag,代码如下:
public class ViewHolder { private Context mContext; private View mConverView; private static int mPosition; public ViewHolder(Context context, int layout, ViewGroup parent,int position){ mContext = context; mConverView = LayoutInflater.from(mContext).inflate(layout,parent,false); mConverView.setTag(this); mPosition = position; //防止滑动导致item的position错误 } /** * 可以理解成单例吧,获取一个viewholder * @param context * @param converView * @param layout * @param parent * @param position * @return */ public static ViewHolder getViewHolder(Context context, View converView, int layout, ViewGroup parent,int position){ if (converView ==null){ //注意这里,其实跟baseadapter的判断是一致的,其中settag也是在viewholder里 return new ViewHolder(context,layout,parent,position); }else{ ViewHolder holder = (ViewHolder) converView.getTag(); mPosition = position; return holder; } } /** * 返回一个viewholder * @return */ public View getConverView() { return mConverView; } }
可以看到,当我们在用的时候,其实用 getViewHolder 这种类似单例的方式的,而在实例中,加载 layout;ok,既然 viewholder 的setTag 和getTag 已经写好,接着就是通过 viewholder 来获取 view 了,那么就是一个 id 对应一个 view 了,我们可以用 sparsearray 来达到我们的效果,由于我们不知道TextView 、ImageView 等等,所以这里用泛型来写,顺便,我们也把一些常用方法写上,改良之后的代码如下:
public class ViewHolder { private Context mContext; private View mConverView; private static int mPosition; private SparseArray<View> mViewSparseArray; public ViewHolder(Context context, int layout, ViewGroup parent,int position){ mContext = context; mConverView = LayoutInflater.from(mContext).inflate(layout,parent,false); mViewSparseArray = new SparseArray<>(); mConverView.setTag(this); mPosition = position; } /** * 可以理解成单例吧,获取一个viewholder * @param context * @param converView * @param layout * @param parent * @param position * @return */ public static ViewHolder getViewHolder(Context context, View converView, int layout, ViewGroup parent,int position){ if (converView ==null){ //注意这里,其实跟baseadapter的判断是一致的,其中settag也是在viewholder里 return new ViewHolder(context,layout,parent,position); }else{ ViewHolder holder = (ViewHolder) converView.getTag(); mPosition = position; return holder; } } /** * 返回一个viewholder * @return */ public View getConverView() { return mConverView; } /** * key-value 通过sparseArray来获取view,属性不同,这里用一个泛型来实现 * @param viewid * @param <T> * @return */ public <T extends View> T getView(int viewid){ View view = mViewSparseArray.get(viewid); if (view == null){ view = mConverView.findViewById(viewid); mViewSparseArray.put(viewid,view); } return (T) view; } /** * 设置text * @param viewid * @param msg */ public void setText(int viewid,String msg){ TextView tv = (TextView) mConverView.findViewById(viewid); tv.setVisibility(View.VISIBLE); tv.setText(msg); } /** * 设置drawable * @param viewid * @param drawable */ public void setDrawable(int viewid, Drawable drawable){ ImageView iv = (ImageView) mConverView.findViewById(viewid); iv.setImageDrawable(drawable); } /** * 设置checkbox * @param viewid * @param status */ public void setCheckbox(int viewid,boolean status){ CheckBox cb = (CheckBox) mConverView.findViewById(viewid); cb.setVisibility(View.VISIBLE); cb.setChecked(status); } /** * 设置itemview 的背景 * @param viewid * @param status */ public void setItemBackground(int viewid,int resourid){ View view = mConverView.findViewById(viewid); //Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(),resourid); view.setBackgroundResource(resourid); } }
这样,我们的一个通用viewholder就可以了。但我们的工作还没有完成,viewholder封装好了,但是 adapter呢?所以,我们封装后的adapter 如下:
public abstract class CommonAdapter<T> extends BaseAdapter { protected Context mContext; protected List<T> mDatas; private int mLayoutId; public CommonAdapter(Context context, List<T> datas,int layoutid) { mContext = context; mDatas = datas; mLayoutId = layoutid; } @Override public int getCount() { return mDatas.size(); } @Override public T getItem(int i) { return mDatas.get(i); } @Override public long getItemId(int i) { return i; } public abstract void convert(ViewHolder viewHolder,T data); /** * 这里把布局什么的,都在getview中,设置好,其中viewholder是我们的通用的adapter * 然后把需要实现的逻辑用抽象方法公布出去,自己去实现 * @param i * @param view * @param viewGroup * @return */ @Override public View getView(int i, View view, ViewGroup viewGroup) { ViewHolder viewHolder = ViewHolder.getViewHolder(mContext,view,mLayoutId,viewGroup,i); convert(viewHolder,getItem(i)); return viewHolder.getConverView(); }; }
可以看到,我们的 listview 基本都是用一个实体类来实现的,所以在初始化的时候,用一个泛型来表示,接着,我们在 getView 中,初始化上面封装好的 ViewHolder,把要实例化itemview 的方法公布出来,让用户自己去定制。这样,在是用的就可以这样用了:
//采用通用listview封装adapter mAdapter = new CommonAdapter<RunAppInfo>(SpeedUpActivity.this, datas, R.layout.listview_item) { @Override public void convert(ViewHolder viewHolder, RunAppInfo data) { viewHolder.setText(R.id.app_name,data.getName()); viewHolder.setText(R.id.app_size,data.getMemery()); viewHolder.setDrawable(R.id.app_icon,data.getIcon()); } }; mListView.setAdapter(mAdapter);
封装好的代码,是不是比以前整洁好看多了;当然,这里我们只用了一次,并不能体现它的好处,加入我还要用多一个 listview 呢?就不用再去写多一个 listview ,直接这样使用即可:
CommonAdapter<StorageInfo> adapter = new CommonAdapter<StorageInfo>(DeepClearActivity.this, mScanDatas,R.layout.listview_detail_item) { @Override public void convert(ViewHolder viewHolder, StorageInfo data) { viewHolder.setDrawable(R.id.list_icon,data.getIcon()); viewHolder.setText(R.id.list_name,data.getName()); viewHolder.setText(R.id.list_path,data.getPath()); viewHolder.setText(R.id.list_size,Formatter.formatFileSize(DeepClearActivity.this,data.getSize())); int status = data.getStatus(); String statustext = null; if (status != ToolUtils.UNINSTALLED){ statustext = getString(R.string.installed); }else{ statustext = getString(R.string.clickinstall); } viewHolder.setText(R.id.list_status,statustext); } }; mDeepDetailListview.setAdapter(adapter);
3、listview 动画
上面的效果图中,我们是有效果图的,那么怎么做呢? listview 是一个 viewground,所以,它也支持 setLayoutAnimation ,所以,我们的动画可以这样写
listview 进入 :
//设置listview 进入动画 Animation translate = new TranslateAnimation(800,0,0,0); translate.setDuration(300); translate.setInterpolator(new DecelerateInterpolator()); LayoutAnimationController lac = new LayoutAnimationController(translate); lac.setOrder(LayoutAnimationController.ORDER_NORMAL); mDeepDetailListview.setLayoutAnimation(lac); mDeepDetailListview.startLayoutAnimation();
退出动画
//设置listview 退出动画 Animation translate = new TranslateAnimation(0,800,0,0); translate.setDuration(300); translate.setInterpolator(new DecelerateInterpolator()); translate.setFillAfter(true); LayoutAnimationController lac = new LayoutAnimationController(translate); lac.setOrder(LayoutAnimationController.ORDER_REVERSE); mDeepDetailListview.setLayoutAnimation(lac); mDeepDetailListview.startLayoutAnimation();
非常简单,就是一个简单的补件动画