先上图,调用过程看起来极其复杂。
类虽然很多,但是按照功能作用可以分成几个模块
- 负责给RecyclerView传递数据的Adaptor
- 负责展示的View
- 负责传包装绘制信息的Canvas类
关于ViewHolder的疑惑
在使用RecyclerView无可避免的要使用Adapter来进行数据的存放,而Adapter是有固定写法的,一般需要onCreateViewHolder来创建ViewHolder和OnBindViewHolder来绑定数据,但是我发现在我学习的项目中,这两个方法的参数和数据的绑定有所区别,所以,adapter到底是根据什么来绑定要显示的item的呢?
-
标准版:
@Override public RecyclerView.ViewHolder onCreateMyViewHolder(ViewGroup parent, int viewType) { Item1Binding binding = DataBindingUtil.inflate(inflater,R.layout.item_1,parent,false); return new ViewHolder(binding); } @Override public void onBindMyViewHolder(RecyclerView.ViewHolder holder, int position) { Fruit fruitBean = list.get(position); ((ViewHolder) holder).getBinding().setFruit(fruitBean); ((ViewHolder) holder).getBinding().executePendingBindings(); //解决databinding闪烁问题 } class ViewHolder extends RecyclerView.ViewHolder { private ItemListBinding binding; public ItemListBinding getBinding() { return binding; } public ViewHolder(@NonNull ItemListBinding binding) { super(binding.getRoot()); this.binding = binding; } }
-
项目版
//子类分别实现 @Override public ViewModelHolder onCreateViewHolder(ViewGroup parent, int innerViewType) { return new ViewModelHolder(TVViewModelFactory.create(parent, innerViewType)); } //父类统一实现 @Override public void onBindViewHolder(ViewModelHolder holder, int position, List<Object> payloads) { super.onBindViewHolder(holder, position, payloads); if (holder.getAsyncState() != TvViewHolder.ASYNC_STATE_SUCC) { updateData(position, getItem(position), holder.getViewModel()); } final TVLifecycleOwner source = getTVLifecycleOwner(); if (source != null) { holder.getViewModel().onBind(source); } else { mModelGroup.add(holder.getViewModel()); } }
项目中的思想是,统一实现一个专用于ViewModel的Adapter,在这些Adapter中使用的都是ViewModel,所以传入ViewHolder的是ViewModel。
其实可以发现,无论在onCreateViewHolder中传入的参数是DataBinding还是ViewModel,只要在自定义的ViewHolder的构造方法中把要显示的View传进就可以了,这可以在源码中发现,
-
源码
public ViewHolder(View itemView) { if (itemView == null) { throw new IllegalArgumentException("itemView may not be null"); } this.itemView = itemView; }
-
当参数为ViewModel时自定义的ViewHolder调用的super
//子类 public ViewModelHolder(@NonNull TVViewModel viewModel) { super(viewModel.getRootView()); mViewModel = viewModel; } //父类 public TvViewHolder(View itemView) { super(itemView); }
-
当参数为DataBinding时的super
public ViewHolder(@NonNull ItemListBinding binding) { super(binding.getRoot()); this.binding = binding; }
其实发现,无论自定义的ViewHolder传入的参数是什么类型的,调用父类的构造方法的时候,传入的都是view。所以只要获取的参数中的view传给父类构造方法,也就完成了绑定。
Adaptor部分
RowItemAdapter继承于AnsyncListVMAdapter继承于ViewModelAdapter,ViewModelAdapter是所有使用ViewModel的Adapter的父类,onBindViewHolder在这个类中实现,统一进行数据绑定,
RowItemAdapter和其他子类中实现了onCreateViewHoler方法,用于每个不同的子类调用工厂以创建出不同的符合条件的ViewModel,这两个方法在上面已经贴出,不展示了。但是onCreateViewHoler用于构建合适的ViewModel的标志innerViewType是从何而来呢,从同一个类的如下方法中返回,
@Override
public int getItemViewType(int position)
{
final RowItem item = getItem(position);
return item == null ? ViewType._VIEW_TYPE_EMPTY : item.mInnerViewType;
}
实际上传入Adapter的数据是元素为RowItem的Array,而这个RowItem是一个抽象类,所以Array中的数据是存放了一系列实现了特定抽象方法的对象,看看这个RowItem类
public abstract class RowItem
{
public final int mInnerViewType;
public RowItem(int innerViewType)
{
this.mInnerViewType = innerViewType;
}
public abstract void updateViewData(@NonNull TVViewModel model);
}
可以看到实现这个类中有mInnerViewType变量,所以,数组中每一个元组都有这个值,根据position获取到特定位置的元素,即可获取到type从而创建ViewModel,在下面即将介绍的CanvasRowItem就必须要继承于RowItem,才能作为数据集被传到Adapter中,在RowItem中有一个updateViewData方法,也就是子类要实现的抽象方法,用于进行数据更新。
自绘View--Canvas部分
-
CanvasNode绘制信息类,提供了用于绘制各种情况下的绘制方法,
举两个例子,实际上对于不同状态只是返回一个含有不同状态变量的对象public static <T extends BaseCanvas> CanvasNode<T> focused(@NonNull CanvasBuilder<T> canvas) { return new CanvasNode<>(CanvasState.VIEW_FOCUSED, canvas, null); } public static <T extends BaseCanvas> CanvasNode<T> normal(@NonNull CanvasBuilder<T> canvas, @NonNull CanvasRefresher<T> refresher) { return new CanvasNode<>(CanvasState.NONE, canvas, refresher); } //构造方法,传入state private CanvasNode(int state, @NonNull CanvasBuilder<T> builder, @Nullable CanvasRefresher<T> refresher) { mState = state; mCanvasBuilder = builder; mRefresher = refresher; }
CanvasBundle容器类,用于封装CanvasNode的第一层容器
-
CanvasBundleExt工厂类,用于创建各种用途的CanvasBundle,例如按钮类,文本类,
public static CanvasBundle createItem(long hash, String logoUrl, String content) { final int width = 541; final int height = 80; return new CanvasBundle(hash, hash, width, height, Arrays.asList( // 背景 CanvasNode.focused((context, bundle) -> buildViewBg(R.drawable.common_view_bg_normal, width, height, context)), // 文字 CanvasNode.focused((context, bundle) -> { final TextCanvas title = new TextCanvas(); title.setDesignTextSize(32); title.setText(content); title.setTextColor(ContextCompat.getColor(context, R.color.ui_color_white_100)); title.setMaxLines(1); title.setMaxDesignWidth(width - 65 - 30); title.setEllipsize(TextUtils.TruncateAt.MARQUEE); title.setMarqueeRepeatLimit(TextCanvas.MARQUEE_REPEAT_FOREVER); final int textHeight = title.getTextDesignHeight(); title.setDesignRect( 65, (height - textHeight) >> 1, width - 30, (height + textHeight) >> 1); title.getTextDesignHeight(); // 消除dirty return title; }), CanvasNode.normal((context, bundle) -> { final TextCanvas title = new TextCanvas(); title.setDesignTextSize(32); title.setText(content); title.setTextColor(ContextCompat.getColor(context, R.color.ui_color_white_80)); title.setMaxLines(1); title.setMaxDesignWidth(width - 65 - 30); final int textHeight = title.getTextDesignHeight(); title.setDesignRect( 65, (height - textHeight) >> 1, width - 30, (height + textHeight) >> 1); title.getTextDesignHeight(); // 消除dirty return title; }), CanvasNode.focused((context, bundle) -> buildLightAnim(width, height, context)).disableExternalAlpha() )).setFocusScale(1.05f).setTopMargin(8).setBottomMargin(8); }
实际上就是把所有的CanvasNode作为元素存储在CanvasBundle的数组变量中,
-
CanvasViewModel用于获取实际显示的View,在onCreateViewHolder中调用,
@Override public void initView(@NonNull ViewGroup parent) { mView = new CanvasView(parent.getContext()); mView.setLayoutParams(new GridLayoutManager.LayoutParams(GridLayoutManager.LayoutParams.WRAP_CONTENT, GridLayoutManager.LayoutParams.WRAP_CONTENT)); setRootView(mView); } @Override public void updateViewData(@NonNull CanvasBundle data) { super.updateViewData(data); mView.setCanvasBundle(data); setItemInfo(data.mItemInfo); mRunnable = data.mRunnable; setFocusScale(data.mFocusScale); }
View部分
CanvasView继承自SpecifySizeView继承自View
-
CanvasView 在CanvasViewModel的初始化过程中被实例化,并在CanvasViewModel的updataViewData中注入CanvasBundle,
@Override protected void onDrawNormal(Canvas canvas) { if (mBundle != null && mBundle.mPivotX != Integer.MIN_VALUE) { setPivotX(mBundle.mPivotX); } else { setPivotX(getWidth() >> 1); } if (mBundle != null && mBundle.mPivotY != Integer.MIN_VALUE) { setPivotY(mBundle.mPivotY); } else { setPivotY(getHeight() >> 1); } if (mBundle != null) { mDrawingRecord.clear(); if (mBundle.onDraw(getCanvasState(), canvas, mDrawingRecord)) { removeCallbacks(mInvalidateRunnable); } else { postDelayed(mInvalidateRunnable, 500/*ms*/); } } }