先上圖应又,調(diào)用過程看起來極其復雜彻亲。
類雖然很多,但是按照功能作用可以分成幾個模塊
- 負責給RecyclerView傳遞數(shù)據(jù)的Adaptor
- 負責展示的View
- 負責傳包裝繪制信息的Canvas類
關(guān)于ViewHolder的疑惑
在使用RecyclerView無可避免的要使用Adapter來進行數(shù)據(jù)的存放,而Adapter是有固定寫法的缝裤,一般需要onCreateViewHolder來創(chuàng)建ViewHolder和OnBindViewHolder來綁定數(shù)據(jù)件缸,但是我發(fā)現(xiàn)在我學習的項目中铜靶,這兩個方法的參數(shù)和數(shù)據(jù)的綁定有所區(qū)別,所以他炊,adapter到底是根據(jù)什么來綁定要顯示的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; } }
-
項目版
//子類分別實現(xiàn) @Override public ViewModelHolder onCreateViewHolder(ViewGroup parent, int innerViewType) { return new ViewModelHolder(TVViewModelFactory.create(parent, innerViewType)); } //父類統(tǒng)一實現(xiàn) @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()); } }
項目中的思想是,統(tǒng)一實現(xiàn)一個專用于ViewModel的Adapter痊末,在這些Adapter中使用的都是ViewModel蚕苇,所以傳入ViewHolder的是ViewModel。
其實可以發(fā)現(xiàn)凿叠,無論在onCreateViewHolder中傳入的參數(shù)是DataBinding還是ViewModel涩笤,只要在自定義的ViewHolder的構(gòu)造方法中把要顯示的View傳進就可以了,這可以在源碼中發(fā)現(xiàn)盒件,
-
源碼
public ViewHolder(View itemView) { if (itemView == null) { throw new IllegalArgumentException("itemView may not be null"); } this.itemView = itemView; }
-
當參數(shù)為ViewModel時自定義的ViewHolder調(diào)用的super
//子類 public ViewModelHolder(@NonNull TVViewModel viewModel) { super(viewModel.getRootView()); mViewModel = viewModel; } //父類 public TvViewHolder(View itemView) { super(itemView); }
-
當參數(shù)為DataBinding時的super
public ViewHolder(@NonNull ItemListBinding binding) { super(binding.getRoot()); this.binding = binding; }
其實發(fā)現(xiàn)蹬碧,無論自定義的ViewHolder傳入的參數(shù)是什么類型的,調(diào)用父類的構(gòu)造方法的時候履恩,傳入的都是view锰茉。所以只要獲取的參數(shù)中的view傳給父類構(gòu)造方法,也就完成了綁定切心。
Adaptor部分
RowItemAdapter繼承于AnsyncListVMAdapter繼承于ViewModelAdapter飒筑,ViewModelAdapter是所有使用ViewModel的Adapter的父類,onBindViewHolder在這個類中實現(xiàn)绽昏,統(tǒng)一進行數(shù)據(jù)綁定协屡,
RowItemAdapter和其他子類中實現(xiàn)了onCreateViewHoler方法,用于每個不同的子類調(diào)用工廠以創(chuàng)建出不同的符合條件的ViewModel全谤,這兩個方法在上面已經(jīng)貼出肤晓,不展示了。但是onCreateViewHoler用于構(gòu)建合適的ViewModel的標志innerViewType是從何而來呢,從同一個類的如下方法中返回补憾,
@Override
public int getItemViewType(int position)
{
final RowItem item = getItem(position);
return item == null ? ViewType._VIEW_TYPE_EMPTY : item.mInnerViewType;
}
實際上傳入Adapter的數(shù)據(jù)是元素為RowItem的Array漫萄,而這個RowItem是一個抽象類,所以Array中的數(shù)據(jù)是存放了一系列實現(xiàn)了特定抽象方法的對象盈匾,看看這個RowItem類
public abstract class RowItem
{
public final int mInnerViewType;
public RowItem(int innerViewType)
{
this.mInnerViewType = innerViewType;
}
public abstract void updateViewData(@NonNull TVViewModel model);
}
可以看到實現(xiàn)這個類中有mInnerViewType變量腾务,所以,數(shù)組中每一個元組都有這個值削饵,根據(jù)position獲取到特定位置的元素岩瘦,即可獲取到type從而創(chuàng)建ViewModel,在下面即將介紹的CanvasRowItem就必須要繼承于RowItem窿撬,才能作為數(shù)據(jù)集被傳到Adapter中启昧,在RowItem中有一個updateViewData方法,也就是子類要實現(xiàn)的抽象方法劈伴,用于進行數(shù)據(jù)更新密末。
自繪View--Canvas部分
-
CanvasNode繪制信息類,提供了用于繪制各種情況下的繪制方法宰啦,
舉兩個例子苏遥,實際上對于不同狀態(tài)只是返回一個含有不同狀態(tài)變量的對象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); } //構(gòu)造方法,傳入state private CanvasNode(int state, @NonNull CanvasBuilder<T> builder, @Nullable CanvasRefresher<T> refresher) { mState = state; mCanvasBuilder = builder; mRefresher = refresher; }
CanvasBundle容器類赡模,用于封裝CanvasNode的第一層容器
-
CanvasBundleExt工廠類田炭,用于創(chuàng)建各種用途的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的數(shù)組變量中,
-
CanvasViewModel用于獲取實際顯示的View辆布,在onCreateViewHolder中調(diào)用瞬矩,
@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*/); } } }