在《RecyclerView体验简介》里已经介绍了RecyclerView目前的可用情况,也提供了一些文章的链接来了解这个新的Android视图的使用方法,API的调用方式。但由于RecyclerView的Adapter需要开发者重新实现,这相当于一次完整的重构了。本文就来介绍一下我在尝鲜RecyclerView所使用到的非常规手段,如果您也只是想尝试一下把原有的程序转到RecyclerView下看看效果,也许本文会有一些帮助。
ViewHolder-挂羊头卖狗肉
之前已经强调RecyclerView强制要求使用ViewHolder模式。但即使是强制,也可以采用“上有政策,下有对策”,敷衍地实现一个子类就好:
- public class DummyViewHolder extends RecyclerView.ViewHolder {
- super(itemView);
- }
- }
至于缓存子项视图的内部视图到变量上,就先免了。
对于比较复杂的ListView子视图应该都会遵循使用ViewHolder,只是没有具体继承与某个基类。所以只要将那些自定义的ViewHolder显示继承RecyclerView.ViewHolder,再在构造函数里调用一下super(view);
就好。
Adapter-新瓶装旧酒
有了ViewHolder,就可以开始创建RecyclerView的Adapter<VH extends ViewHolder>
了。首先上整个实现的代码,再来慢慢解释这恶劣的手段。
- public class AdapterWrapper extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
-
- // Any negative value could be invalid, as position could not less than 0
- private static final int INVALID_VALUE = -9999;
-
- private Adapter mAdapter;
- private int mRecentRequestedType = INVALID_VALUE;
- private int mPositionOfRequestedType = INVALID_VALUE;
-
- public AdapterWrapper(Adapter adapter) {
- mAdapter = adapter;
- }
-
- @Override
- public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
- if (mRecentRequestedType != viewType) {
- + "Saved: " + mRecentRequestedType + ", wanted: " + viewType);
- }
- if (mPositionOfRequestedType == INVALID_VALUE) {
- }
-
- final RecyclerView.ViewHolder holder = onCreateViewHolder(mPositionOfRequestedType, viewGroup);
-
- mRecentRequestedType = INVALID_VALUE;
- mPositionOfRequestedType = INVALID_VALUE;
- return holder;
- }
-
- public RecyclerView.ViewHolder onCreateViewHolder(int position, ViewGroup viewGroup) {
- if (tag instanceof RecyclerView.ViewHolder) {
- return (RecyclerView.ViewHolder) tag;
- }
-
- return new DummyViewHolder(getView(position, viewGroup));
- }
-
- return mAdapter.getView(position, null, viewGroup);
- }
-
- @Override
- public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
- // BindView suppose not use the ViewGroup, hence 'null' is passed here
- mAdapter.getView(i, viewHolder.itemView, null);
- }
-
- @Override
- public int getItemCount() {
- return mAdapter.getCount();
- }
-
- @Override
- public int getItemViewType(int position) {
- mPositionOfRequestedType = position;
- return mRecentRequestedType = mAdapter.getItemViewType(position);
- }
-
- @Override
- public long getItemId(int position) {
- return mAdapter.getItemId(position);
- }
-
- return mAdapter.getItem(position);
- }
-
- /**
- * This class provides concrete object of abstract {@link RecyclerView.ViewHolder}.
- */
- static class DummyViewHolder extends RecyclerView.ViewHolder {
- super(itemView);
- }
- }
- }
要让RecyclerView的Adapter兼容ListView的Adapter最大的麻烦是创建视图时得到的参数只有viewType
而不是以往的子项的index
。不过翻看源码可以看到在调用新建ViewHolder函数时总是会去调用getItemViewTyp()
:
- if (holder == null) {
- holder = mAdapter.createViewHolder(RecyclerView.this,
- mAdapter.getItemViewType(offsetPosition));
- if (DEBUG) {
- Log.d(TAG, "getViewForPosition created new ViewHolder");
- }
- }
因此就可以在getItemViewType()
被调用时缓存下子项的index
然后在接着立刻发生创建方法中拿来使用,这也是就是代码中mPositionOfRequestedType
变量的意义。不过一旦Google改变这段源码,将子项目类型存放在局部变量中,该黑科技可能就会是有效了。但是看这段的实现方式,也许也是为了保证在其内部也能通过这个黑科技来偷懒吧。
- public int getItemViewType(int position) {
- mPositionOfRequestedType = position;
- return mRecentRequestedType = mAdapter.getItemViewType(position);
- }
- public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
- if (mRecentRequestedType != viewType) {
- + "Saved: " + mRecentRequestedType + ", wanted: " + viewType);
- }
- if (mPositionOfRequestedType == INVALID_VALUE) {
- }
-
- final RecyclerView.ViewHolder holder = onCreateViewHolder(mPositionOfRequestedType, viewGroup);
-
- mRecentRequestedType = INVALID_VALUE;
- mPositionOfRequestedType = INVALID_VALUE;
- return holder;
- }
另外对于返回ViewHolder的问题,针对前面定义的方式,就可以相应的得到ViewHolder对象:如果原来存放在tag里的ViewHolder已经继承了RecyclerView.ViewHolder,就可以直接重用它,否则就创建一个什么都不做的DummyViewHolder。
- public RecyclerView.ViewHolder onCreateViewHolder(int position, ViewGroup viewGroup) {
- if (tag instanceof RecyclerView.ViewHolder) {
- return (RecyclerView.ViewHolder) tag;
- }
-
- return new DummyViewHolder(getView(position, viewGroup));
- }
至于剩下的任务,就全都转给了原来使用在ListView上的Adapter。
- public AdapterWrapper(Adapter adapter) {
- mAdapter = adapter;
- }
LinearLayoutManager-鱼目混珠
RecyclerView的呈现形式非常自由,要让它变得跟ListView一样,就要用LinearLayoutManager,创建方式也很简单:
- LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context);
RecyclerView-乱锅炖
最后要把上边所有的东西都糅合成一起在RecyclerView上呈现出来。首先要把在Layout定义中把ListView替换成RecyclerView
- <android.support.v7.widget.RecyclerView
- android:id="@+id/recyclerView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
之后在代码中将之前的一切装配进去:
- mRecyclerView.setAdapter(new AdapterWrapper(listAdapter));
- mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
剩下的则还是原来使用的代码。
黑科技介绍完毕,不过也看到对于这些手段所用的描绘都不是好的词,毕竟这不是应用RecyclerView所使的常规方式,部分代码依赖于当前RecyclerView库的具体实现方式,ViewHolder也没有尽到责任。所以如果只是想感受ListView迁移到RecyclerView的效果,可以用这里提供的方式来做个原型展示。当真正要使用RecyclerView时,还是应该按照其设计要求去实现每个类和接口。