关于ListView万能适配器,网上有茫茫多详细的教程,这里实现简单的万能适配器。
关于万能,最好的万能工具就是API,同API一样,只要做到把可以复用的代码抽取出来封装成方法或类,只需要提供给使用者一个接口传入不同的参数就可以达到适配器的通用。
代码附注释,直接撸代码:
Bean:
package com.chan.allpowerful_adapter.bean;
/**
* Created by Chan on 2016/5/20.
*
* 数据Bean类,包含四个String和一个图片
*/
public class Bean {
private String title;
private String content;
private String time;
private String phone;
private int img;
public Bean(String title, String content, String time, String phone,int img) {
this.title = title;
this.content = content;
this.time = time;
this.phone = phone;
this.img = img;
}
...(省略set/get)
通用的viewHolder:
package com.chan.allpowerful_adapter.utils;
import android.content.Context;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
/**
* Created by Chan on 2016/5/20.
*
* 通用的ViewHolder,封装了平常写自定义Adapter中复用的getView中的代码,并抽取出一些常用组件如TextView,
* ImageView的设置方法,大大简化调用时的代码
*/
public class ViewHolderUtils {
private Context mContext;
private int mPosition;
private View mConvertView;
// 类似于Map,保存键值对,Key为int,value为object,效率比Map高
private SparseArray<View> mSparseArray;
public ViewHolderUtils(Context context, int position, View convertView,
ViewGroup parent, int layoutId) {
this.mContext = context;
this.mPosition = position;
this.mConvertView = convertView;
mSparseArray = new SparseArray<>();
convertView.setTag(this);
}
/**
* 入口方法:封装了判断convertView是否为空的判断,如果convertView
* 为空则创建一个ViewHolderUtils对象,在ViewHolderUtils构造方法里加载convertView的布局
* ,如果convertView 不为空则通过convertView.getTag(),返回viewHolder。
* 可以把这个方法看做是在这个ViewHolderUtils对BaseAdapter的getView()内部操作的封装抽取 并在万能适配器
* CommonAdapter 中的getView()中使用
*
* @param mContext
* @param position
* @param convertView
* @param parent
* @param layoutId
* @return
*/
public static ViewHolderUtils get(Context mContext, int position,
View convertView, ViewGroup parent, int layoutId) {
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(layoutId,
parent, false);
return new ViewHolderUtils(mContext, position, convertView, parent,
layoutId);
} else {
ViewHolderUtils holder = (ViewHolderUtils) convertView.getTag();
return holder;
}
}
/**
* 获取ViewHolder中存入的各种控件的实例,获取后强转为具体的控件类
*
* @param resId
* 控件的ID
* @param <T>
* @return View的泛型
*/
public <T extends View> T getSingleView(int resId) {
View view = mSparseArray.get(resId);
if (view == null) {
view = mConvertView.findViewById(resId);
mSparseArray.put(resId, view);
}
return (T) view;
}
/**
* 设置viewHolder中的TextView
*
* @param id
* 要设置TextView控件的id,用于在mSparseArray中找到控件
* @param text
* 要设置的文字
*/
public void setViewHolderText(int id, String text) {
TextView tv = getSingleView(id);
tv.setText(text);
}
/**
* 设置viewHolder中的ImageView
*
* @param id
* 要设置ImageView控件的id,用于在mSparseArray中找到控件
* @param resId
* 要设置的图片
*/
public void setViewHolderImg(int id, int resId) {
ImageView iv = getSingleView(id);
iv.setImageResource(resId);
}
/* 添加事件监听 */
public void setOnClickListener(int viewId, View.OnClickListener listener) {
View view = getSingleView(viewId);
view.setOnClickListener(listener);
}
public void setOnTouchListener(int viewId, View.OnTouchListener listener) {
View view = getSingleView(viewId);
view.setOnTouchListener(listener);
}
public void setOnLongClickListener(int viewId,
View.OnLongClickListener listener) {
View view = getSingleView(viewId);
view.setOnLongClickListener(listener);
}
public void updatePosition(int position) {
mPosition = position;
}
/**
* 获取一顿操作后的convertView
*
* @return
*/
public View getConvertView() {
return mConvertView;
}
}
万能适配器:
package com.chan.allpowerful_adapter.adapter;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import com.chan.allpowerful_adapter.utils.ViewHolderUtils;
import java.util.List;
/**
* Created by Chan on 2016/5/20.
*
* 万能适配器,封装了一般自定义Adapter的getCount(),getItem(),getItemId(),getView()方法。
* 使用者只需要实现convert()方法就可以在getView中进行操作.其中包含了ViewHolderUtils万能的ViewHolder.
*/
public abstract class CommonAdapter<T> extends BaseAdapter {
protected List<T> data;
protected Context mContext;
protected int layoutId;
/**
* @param context
* @param data
* 一个泛型集合,为了让使用者使用不同内容的包装好的Bean
* @param layoutId
* 每个列表项的布局id
*
*/
public CommonAdapter(Context context, List<T> data, int layoutId) {
this.data = data;
mContext = context;
this.layoutId = layoutId;
}
/*
* 封装了原自定义Adapter要实现的方法, 不管写何种的ListView,其实都是对数据集合进行操作,所以使用了泛型data
*/
@Override
public int getCount() {
return data.size();
}
@Override
public T getItem(int position) {
return data.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
/**
* 封装子Adapter要重写的getView(),子Adapter就不需要再去重写getView而编写重复的代码。在getView()
* 中使用了ViewHolderUtils
* ,进行内部convertView的操作,通过调用convert()操作使用者具体要进行的例如:列表项控件的获取,各个控件的填入数据.
* 避免子Adapter重写getView()导致重复代码。
*
* @param position
* @param convertView
* @param parent
* @return
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolderUtils holder = ViewHolderUtils.get(mContext, position,
convertView, parent, layoutId);
convert(holder, getItem(position));
return holder.getConvertView();
}
/**
* 提供给子adapter实现,通过这个方法可以获取viewHolder的控件并对控件填入数据
*
* @param holder
* getView()中使用的viewHolder
* @param bean
* 每个列表项对应的bean
*/
protected abstract void convert(ViewHolderUtils holder, T bean);
}
使用万能适配器:
package com.chan.allpowerful_adapter;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ListView;
import android.widget.Toast;
import com.chan.allpowerful_adapter.adapter.CommonAdapter;
import com.chan.allpowerful_adapter.bean.Bean;
import com.chan.allpowerful_adapter.utils.ViewHolderUtils;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private ListView mListView;
private List<Bean> mBeanList;
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_main);
initData (); // 初始化数据
initView (); // 初始化视图
}
private void initData () {
mBeanList = new ArrayList<> ();
for (int i = 0; i < 6; i++) {
Bean bean = new Bean ("title" + i, "谁能代表肯德基", "520", "123", R.mipmap.ic_launcher);
mBeanList.add (bean);
}
}
private void initView () {
mListView = (ListView) findViewById (R.id.listView);
mListView.setAdapter (new CommonAdapter<Bean> (this, mBeanList, R.layout.my_item) {
@Override
protected void convert (final ViewHolderUtils holder, Bean bean) {
holder.setViewHolderText (R.id.title, bean.getTitle ());
holder.setViewHolderText (R.id.content, bean.getContent ());
holder.setViewHolderText (R.id.phone, bean.getPhone ());
holder.setViewHolderText (R.id.time, bean.getTime ());
holder.setViewHolderImg (R.id.img, bean.getImg ());
holder.setOnClickListener (R.id.title, new View.OnClickListener () {
@Override
public void onClick (View v) {
Toast.makeText (MainActivity.this, "点击了tv_Title", Toast.LENGTH_SHORT).show ();
}
});
holder.setOnLongClickListener (R.id.img, new View.OnLongClickListener () {
@Override
public boolean onLongClick (View v) {
Toast.makeText (MainActivity.this, "长按了IMG", Toast.LENGTH_SHORT).show ();
return true;
}
});
}
});
}
}
xml文件:
主布局只包含一个ListView
ListView的列表项布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/activity_horizontal_margin">
<ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="title"/>
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:text="content"/>
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text="time"/>
<TextView
android:id="@+id/phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/content"
android:text="phone"/>
</RelativeLayout>
运行:
最后:
- 这里只是给出了一种简单的单一布局的万能适配器,要想实现混合布局或者加上头尾标题推荐下面的:
- 以这个例子提升代码封装重构的能力比适配器本身更重要(虽然听起来很美)。
- 跟我一样Java是印度人教的,对于泛型不理解可以参考