您的位置:首页 > 移动开发 > Android开发

Android中的万能适配器——base-adapter-helper解析

2016-04-04 11:36 706 查看
在Android开发中,我们经常会用到ListView、GridView,每次编码的时候都需要为他们写对应的Adapter,写多了就感觉很烦躁了,因为基本的编程思想都是一样的,但是每次都要重复去写,所以我们能不能把它们抽象成一个通用的模板,这样就不用每次都重复写相同的代码了,直接重复使用,这样不是更好,下面我们就来介绍介绍一个开源项目base-adapter-helper。

传统Adapter的编码思路,主要看Adapter中的getView方法。
public View getView(int pos, View convertView, ViewGroup parent){
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text));
holder.icon = (ImageView) convertView.findViewButId(R.id.icon));
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.text.setText(DATA[pos]);
holder.icon.setImageBitmap((pos & 1) == 1 ? mIcon1 : mIcon2);
return convertView;
}

static class ViewHolder {
TextView text;
ImageView icon;
}
上面使用了一个ViewHolder用来缓存对应Item中的view,并且重用移出的Item,它对应的就是convertView。这样注意为了节省资源,提高效率。这种写法大家都应该很熟悉了。
下面来看看base-adapter-helper是怎样对其进行抽象封装的。首先来看看它的类继承图。

github链接:base-adapter-helper



可以看到BaseQuickAdapter继承自BaseAdapter,同样我们重点关注它的getView函数。

@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (getItemViewType(position) == 0) {
final H helper = getAdapterHelper(position, convertView, parent);
T item = getItem(position);
helper.setAssociatedObject(item);
convert(helper, item);
return helper.getView();
}

return createIndeterminateProgressView(convertView, parent);
}

private View createIndeterminateProgressView(View convertView, ViewGroup parent) {
if (convertView == null) {
FrameLayout container = new FrameLayout(context);
container.setForegroundGravity(Gravity.CENTER);
ProgressBar progress = new ProgressBar(context);
container.addView(progress);
convertView = container;
}
return convertView;
}
下面我们分析分析getView的代码:

第3行代码就是获取该postion的Item类型,上面定义了两种类型的Item,一种是我们需要显示的View的Item,一种是底部的加载的View的Item。当Item类型为0是就为需要显示的Item,当Item类型为1是就为底部加载的Item,上面的第11行代码的createIndeterminateProgressView就是创建底部用来的加载的Item,可以看到它是一个ProgressBar。另外可以通过showIndeterminateProgress(boolean)来显示或者隐藏这个item。



第4行代码的getAdapterHelper获取一个BaseAdapterHelper对象,我们可以看到,在BaseQuickAdapter类中,getAdapterHelper是一个抽象函数,所以我们来看看BaseQuickAdapter的子类QuickAdapter,在这个类中它实现了getAdapterHelper方法,执行的实质是BaseAdapterHelper的静态get方法。另外需要提到的是,QuickAdapter类的构造函数有一个参数为layoutResId,它就是传入我们要显示Item的布局文件。
static BaseAdapterHelper get(Context context, View convertView, ViewGroup parent, int layoutId, int position) {
if (convertView == null) {
return new BaseAdapterHelper(context, parent, layoutId, position);
}

// Retrieve the existing helper and update its position
BaseAdapterHelper existingHelper = (BaseAdapterHelper) convertView.getTag();
existingHelper.position = position;
return existingHelper;
}

从上面可以看到它也对convertView进行了重用,当convertView为null,那么我们需要创建一个BaseAdapterHelper,传入的就是要显示的位置position以及layoutId布局文件,这个position为前面getView中的那个position参数,layoutId为创建QuickAdapter传入的参数,就是Item的布局文件。
protected BaseAdapterHelper(Context context, ViewGroup parent, int layoutId, int position) {
this.context = context;
this.position = position;
this.views = new SparseArray<View>();
convertView = LayoutInflater.from(context) //
.inflate(layoutId, parent, false);
convertView.setTag(this);
}
在BaseAdapterHelper的构造函数里面,定义了一个views,它其实就是传统Adapter里面的那个ViewHolder用来存放Item里面的各个view。convertView为我们要显示的Item的View,接着通过setTag函数将BaseAdapterHelper对象本身关联到convertView上面,所以我们知道每个Item对象都关联了一个BaseAdapterHelper对象。
当convertView不为null的时候,我们就直接可以复用这个convertView,首先从convertView中取出它关联的BaseAdapterHelper对象,这样就可以复用这个convertView对应的BaseAdapterHelper对象。
接着分析最上面的代码,第5行代码getItem用来得到需要显示的数据,这里需要说明的是,我们应该把需要传入的数据放在一个List中,我们可以看看QuickAdapter的构造函数

public QuickAdapter(Context context, int layoutResId, List<T> data) {
super(context, layoutResId, data);
}
可以看到我们传入Item布局的layoutResId和要显示的数据data,data是List类型的。第6行代码是将我们向显示的数据项与BaseAdapterHelper对象关联起来。

第7行代码convert函数同样是一个抽象函数,它需要我们进行实现,来具体设置对应的Item里面的内容。

第8行代码是当BaseAdapterHelper对象设置为Item里面的内容之后,然后就可以得到这个Item的View对象进行返回。

下面我们来具体画个图来说明。假如我们要显示的是一个ListView。
当刚开始显示的时候,因为对应的convertView为null,所以会创建BaseAdapterHelper,核心代码如下:
protected BaseAdapterHelper(Context context, ViewGroup parent, int layoutId, int position) {
this.context = context;
this.position = position;
this.views = new SparseArray<View>();
convertView = LayoutInflater.from(context) //
.inflate(layoutId, parent, false);
convertView.setTag(this);
}



因为在BaseAdapterHelper对象中包含有对应的Item对应的convertView和对应的position以及convertView里面的子view集合,因此我们可以直接通过BaseAdapterHelper对象来完成各项操作。


当List向上滑动的时候,第一个Item移出List,底部就需要再显示一个Item,这个时候getView里面的convertView就是第一个移出的View,我们可以直接对它重用来显示下一个Item,核心代码为:
BaseAdapterHelper existingHelper = (BaseAdapterHelper) convertView.getTag();
existingHelper.position = position;
他直接得到BaseAdapterHelper对象,然后重新设置它对应的位置postion,因为BaseAdapterHelper对象中引用到了重用的convertView,这样就可以直接使用这个view的Item了。
下面来举个简单的例子:
public class MainActivity extends AppCompatActivity {
private ListView listView;

private List<String> data = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
data.add("text1");
data.add("text2");
data.add("text3");
data.add("text4");
data.add("text5");
data.add("text6");

listView = (ListView) findViewById(R.id.listview);
QuickAdapter adapter = new QuickAdapter<String>(this, R.layout.item, data) {
@Override
protected void convert(BaseAdapterHelper helper, String item) {
helper.setText(R.id.textView, item);
}
};
listView.setAdapter(adapter);
}

}

也就是说我们只需要定义一个QuickAdapter重写convert方法就可以了,在convert方法里面我们直接使用BaseAdapterHelper对象完成对数据内容item的设置就可以了。



欢迎关注我的公众号:DroidMind
精品内容,独家发布
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: