封装能重用的自定义Adapter,向翔哥致敬

看了翔哥的自定义万能的adapter,自己也做下笔记,分析一下高手的思维方式,让我们一起进入变态程序员的内心世界。

分析万能的adapter之前,我们先分析一下普通的adapter

	public class ReportSpinnerAdapter extends BaseAdapter {
	    private Context context;
	    private List<String> str;
	    public ReportSpinnerAdapter(Context context, List<String> str, int textWidth) {
	        this.context = context;
	        this.str = str;
	        this.textWidth = textWidth;
	    }

	    @Override
	    public int getCount() {
	        return str.size();
	    }

	    @Override
	    public Object getItem(int position) {
	        return str.get(position);
	    }

	    @Override
	    public long getItemId(int position) {
	        return position;
	    }

	    @Override
	    public View getView(int position, View convertView, ViewGroup parent) {
	        Viewholder hold;
	        if (convertView==null){
	            convertView = LayoutInflater.from(context).inflate(R.layout.spinner_layout,parent,false);
	            hold = new ViewHold();
	            hold.textView = (TextView) convertView.findViewById(R.id.textView);
	            convertView.setTag(hold);
	        }else{
	            hold = (ViewHold) convertView.getTag();
	        }
	        hold.textView.setText(str.get(position));
	        return convertView;
	    }
	    static class ViewHolder{
	        TextView textView;
	    }

普通的adapter大致就分为两点,getView和ViewHolder,一个类就解决问题,涉及到控件的复用,就用到了ViewHolder,主要逻辑还是都在getView中,,如果对这个不理解,可以先百度ViewHolder,getView中的逻辑就是如果convertView第一次被使用的时候,初始化绑定布局,并设置标记,否则,就从convertView取出viewHolder,如果要更改布局中控件的参数就从viewHolder中取出控件设置参数,因为这个控件在convertView初始化的时候就一起初始化了,所以直接设置参数就ok了。这是一个标准程序员的通常做法,但是,这样如果我们程序中要经常使用adapter,每个Listview或GridView都要去弄一个adapter,就白白浪费了大量的时间,作为一个变态程序员,翔哥怎么能忍,所以就出了这么一篇博客翔哥博客,让我们也能从中获利,收获了新技术也收获了新的思维方式。

总结自定义万能的adapter,或者叫做可重用的adapter,分拆开来看,就是三点:

1.封装一个特殊的ViewHolder,可以传入参数就直接得到viewHolder,把这步干掉:

if (convertView==null){
	            convertView = LayoutInflater.from(context).inflate(R.layout.spinner_layout,parent,false);
	            hold = new ViewHold();
	            hold.textView = (TextView) convertView.findViewById(R.id.textView);
	            convertView.setTag(hold);
	        }else{
	            hold = (ViewHold) convertView.getTag();
	 }
想要干掉这一步,就要想到,传入什么样的参数,才能直接得到viewHolder呢,就是后面这几个,1.LayoutInflater.from(context)  2.R.layout.spinner_layout 3.parent 4.convertView

知道了需要的条件就好说了,直接写一个

	public class ViewHolder {
	    private SparseArray<View> views;
	    private View convertView;

	    public ViewHolder(ViewGroup parent,LayoutInflater inflater,int layoutId) {
	        this.views = new SparseArray<View>();
	        this.convertView = inflater.inflate(layoutId,parent,false);
	        this.convertView.setTag(this);
	    }

	    /**
	     * 得到viewHolder
	     * @param parent
	     * @param convertView
	     * @param inflater
	     * @param layoutId
	     * @return
	     */
	    public static ViewHolder getViewHolder(ViewGroup parent,View convertView,LayoutInflater inflater,int layoutId){
	        if (convertView==null){
	            return new ViewHolder(parent,inflater,layoutId);
	        }
	        return (ViewHolder) convertView.getTag();
	    }

这个 SparseArray<View> views实际上就是一个键值对,用来保存listview或者gridview上每一行或者每一列上的所有控件的,效率上要高于hashmap,用法就是

	/**
	     * 根据Id得到view
	     * @param viewId
	     * @param <T>
	     * @return
	     */
	    public <T extends View>T getView(int viewId){
	        View view = views.get(viewId);
	        if (view==null){
	            view = convertView.findViewById(viewId);
	            views.put(viewId,view);
	        }
	        return (T) view;
	    }

这样,一个ViewHolder就搞定了。

2.编写一个通用的adapter,通用adapter,基本无法实现,那怎么办呢,做一个半通用的adapter,既然要半通用,就是大体相同,少数差异,那用抽象类继承的方式就最好了

	public abstract class CommonAdapter<T> extends BaseAdapter {
	    private List<T> list;
	    private LayoutInflater inflater;
	    private int layoutId;

	    public CommonAdapter(List<T> list, Context context,int layoutId) {
	        this.list = list;
	        this.inflater = LayoutInflater.from(context);
	        this.layoutId = layoutId;
	    }

	    @Override
	    public int getCount() {
	        return list.size();
	    }

	    @Override
	    public Object getItem(int position) {
	        return list.get(position);
	    }

	    @Override
	    public long getItemId(int position) {
	        return position;
	    }

	    @Override
	    public View getView(int position, View convertView, ViewGroup parent) {
	        ViewHolder viewHolder = ViewHolder.getViewHolder(parent,convertView,inflater,layoutId);
	        convert(viewHolder,list.get(position));
	        return viewHolder.getConvertView();
	    }
	    public abstract void convert(ViewHolder viewHolder,T t);
	}

首先,我们绑定adapter的时候不确定数据源,就得用泛型类和泛型集合了,再就是传入布局id和context上下文,把需要给控件赋值的地方设置为抽象方法,给子类继承,这样封装好了之后,我们需要做的事情就是少之又少了,只需要重写convert方法和构造方法了

	public class MyAdapter extends CommonAdapter<Bean> {

	    public MyAdapter(List<Bean> list, Context context, int layoutId) {
	        super(list, context, layoutId);
	    }

	    @Override
	    public void convert(ViewHolder viewHolder, Bean bean) {
	        ((TextView)viewHolder.getView(R.id.text)).setText("这是测试数据1");
		((TextView)viewHolder.getView(R.id.text)).setText("这是测试数据1");    }
	}

这样,只需要在activity或者fragment中进行绑定MyAdapter,把数据和布局id和content传入就搞定了。只不过(viewHolder.getView(R.id.text)).setText这种写法很蛋疼,代码中写强转的话,还要把光标切回来,反正我个人是很不习惯的,那怎么办呢,可以这样,我们既然有了viewholder对象,我们就改造一下viewHolder对象,让公共代码多一点,我们以后重复写的地方就少一点。在ViewHolder中加入如下方法

/**
	     * 根据id得到TextView
	     * @return
	     */
	    public TextView getTextView(int viewId){
	        return getView(viewId);
	    }
	    /**
	     * 根据id得到ImageView
	     * @return
	     */
	    public ImageView getImageView(int viewId){
	        return getView(viewId);
	    }
	    /**
	     * 根据id得到Button
	     * @return
	     */
	    public Button getButton(int viewId){
	        return getView(viewId);
	    }
	    /**
	     * 根据id得到RadioButton
	     * @return
	     */
	    public RadioButton getRadioButton(int viewId){
	        return getView(viewId);
	    }
	    /**
	     * 根据id得到CheckBox
	     * @return
	     */
	    public CheckBox getCheckBox(int viewId){
	        return getView(viewId);
	    }
	    /**
	     * 根据id得到ImageButton
	     * @return
	     */
	    public ImageButton getImageButton(int viewId){
	        return getView(viewId);
	    }
	    /**
	     * 根据id得到ImageButton
	     * @return
	     */
	    public EditText getEditText(int viewId){
	        return getView(viewId);
	    }

那么我们的Myadapter中的convert方法就可以这样写了

@Override
	    public void convert(ViewHolder viewHolder, Bean bean) {
	        viewHolder.getTextView(R.id.text).setText("这是测试数据1");
	        viewHolder.getTextView(R.id.text).setText("这是测试数据2");
	    }

这样书写起来就会流畅一点,比较跟手,好了,这基本就是翔哥万能的自定义adapter的主要内容,下面图解一下核心思想

最后,源码:点击打开链接

时间: 2024-11-05 12:24:07

封装能重用的自定义Adapter,向翔哥致敬的相关文章

【Android UI】ListView系列二(自定义Adapter订阅新闻栏目)

目标:自定义适配器Adapter实现点击每个item订阅按钮 上一篇介绍了listview的基本属性以及ArrayAdapter和SimpleAdapter的简单实用,链接:listview使用方式基础篇. 今天主要介绍一下自定义adapter,来实现稍微复杂点的功能,今天实现的效果是:类似于listiew展示许多可供订阅的栏目,每个栏目最右端有订阅按钮,用户点击订阅可以订阅该栏目,再次点击可取消订阅,效果图如下,下面我们一步步来实现. 1. 定义主布局文件activity_main.xml 主

自定义Adapter实现列表功能

最后的效果图就是这样的,其实这个效果的话用SimpleAdapter也是可以实现的,但是为了学习自定义adapter还是重新写了一个. 首先我们应该有每一个列表项布局的xml文件 1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 androi

自定义Adapter的listview(很重要)

首先介绍下MVC:M就是modle,V是view,C是control 把数据通过适配器适配一定格式后,给listview输出 listview相当于一个空的架子,把格式化的数据放出来 LayoutInflater跟findById很像,只是findById是找的控件,LayoutInflater找的是xml文件 自定义流程: 1) mainactivity运行本身的布局文件和listview.setadapter启动adapter,adapter要映射 数据 到 布局 2) 要找到布局要用Lay

【转】Android中Spinner下拉列表(使用ArrayAdapter和自定义Adapter实现)

原文网址:http://embed.21ic.com/software/android/201403/31603.html 1 :Android中Spinner下拉列表(使用ArrayAdapter和自定义Adapter实现) . 今天学习了Spinner组件,使用Spinner相当于从下拉列表中选择项目,下面演示一下Spinner的使用(分别使用ArrayAdapter和自定义Adapter实现) (一):使用ArrayAdapter进行适配数据: ①:首先定义一个布局文件: android:

自定义Adapter中实现startActivityForResult的分析

最近几天在做文件上传的时候,想在自定义Adapter中启动activity时也返回Intent数据,于是想到了用startActivityForResult,可是用mContext怎么也调不出这个方法,只能调用startActivity这个方法,于是在网上搜一下,可以利用一个方式可以间接的解决这个问题,果断贴代码: Intent mIntent = new Intent(mContext,clazz);((Activity) mContext).startActivityForResult(mI

Android 自定义Adapter 但listview 只显示第一条数据

楼主让这个问题郁闷了一晚上.....在logcat里明明显示adapter的getview方法里的list大于一条数据 ,但posotion却一直是0.....运行后也只显示list[0]里面的数据....最后的最后原来错误出在布局文件上 我以前的是这样的; <ScrollView android:layout_width="fill_parent" android:layout_height="wrap_content" > <!-- listv

Android 自定义Adapter实现多视图Item的ListView

自定义Adapter实现多视图Item的ListView http://www.devdiv.com/adapter_item_listview-blog-20-7539.html Android 自定义Adapter实现多视图Item的ListView,布布扣,bubuko.com

【转】Android自定义Adapter的ListView的思路及代码

原文网址:http://www.jb51.net/article/37236.htm Android自定义Adapter的ListView的思路及代码,需要的朋友可以参考一下 在开发中,我们经常使用到ListView这个控件.Android的API也提供了许多创建ListView适配器的快捷方式.例如ArrayAdapter.SimpleAdapter和SimpleCursorAdapter等.但你是否发现,如果采用这些系统自带的适配器,对于事件的响应只能局限在一个行单位.假设一行里面有一个按钮

Android学习----自定义Adapter实现ListView

前言: 对于ListView而言,自定义的Adapter对于显示复杂的界面有很大的灵活性 .使用自定义的Adapter需要继承BaseAdapter,然后重写getCount(),getView(),getItem,getItemId()4个方法.adapter在绘制listview时是先根据getCount()获得底层数据的个数来判断绘制item的个数,然后通过getView绘制单个item. ListView实现的效果如下: 详细步骤: 1.新建Activity,在对应的布局文件中放置lis