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