本系列文章主要介紹天貓團隊開源的Tangram框架的使用心得和原理,因爲Tangram
底層基於vlayout,因此也會簡單講解,該系列將按如下大綱進行介紹:java
本文將對Tangram
進行初步講解。git
基於Tangram最新源碼分析github
筆者Demo代碼json
在Tangram和vlayout介紹這篇文章提到過,Tangram
經過解析json模板獲得佈局方式Card
和具體視圖Cell
,而後將Card
轉換成對應的vlayout
的LayoutHelper
來進行測量和佈局,以下,數組
官網的架構圖以下,緩存
跟進TangramActivity
的engine.setData(data)
,架構
//BaseTangramEngine.java void setData(T data) { //模板解析,json文件 -> JSONArray -> List<Card> List<C> cards = mDataParser.parseGroup(data, this); this.setData(cards); } void setData(List<C> data) { this.mGroupBasicAdapter.setData(data); }
來到GroupBasicAdapter
,框架
//GroupBasicAdapter.java void setData(List<L> cards, boolean silence) { //把cards轉成vlayout的layoutHelpers setLayoutHelpers(transformCards(cards, mData, mCards)); if (!silence) notifyDataSetChanged(); } //cards指json模板中的多個佈局方式card, //data指每一個card裏邊的具體視圖cell //rangeCards指一段管轄範圍內所對應的佈局方式card //假設第1個card對應ColumnLayoutHelper,有3個元素,則管轄範圍是[0,2] //第2個card對應OnePlusNLayoutHelper,有4個元素,則管轄範圍是[3,6],以此類推 List<LayoutHelper> transformCards(List<L> cards, List<C> data, List<Pair<Range<Integer>, L>> rangeCards) { //data.size()初始值爲0 int lastPos = data.size(); List<LayoutHelper> helpers = new ArrayList<>(cards.size()); for (int i = 0, size = cards.size(); i < size; i++) { //遍歷每一個card L card = cards.get(i); //獲取card的類型,如列布局container-fourColumn final String ctype = getCardStringType(card); //獲取card內的cell數組 List<C> items = getItems(card); //若是card裏邊沒有cell,即沒有視圖,直接跳過 if (items == null) { continue; } //記錄每一個card裏邊的多個cell data.addAll(items); int offset = lastPos; lastPos += items.size(); //記錄每一段管轄範圍,和其對應的card rangeCards.add(Pair.create(Range.create(offset, lastPos), card)); //獲取card對應的LayoutHelper,暫不深究 LayoutBinder<L> binder = mCardBinderResolver.create(ctype); LayoutHelper helper = binder.getHelper(ctype, card); if (helper != null) { //設置cell個數 helper.setItemCount(items.size()); helpers.add(helper); } } return helpers; }
Card
被轉換成LayoutHelper
,源碼分析
轉換完成後,調用了notifyDataSetChanged
,是如何顯示到RecyclerView
上的呢?佈局
跟進TangramActivity
的engine.bindView(recyclerView)
,
//BaseTangramEngine.java void bindView(@NonNull final RecyclerView view) { this.mContentView = view; //設置VirtualLayoutManager this.mContentView.setLayoutManager(mLayoutManager); //設置性能監控,mLayoutManager負責監控cell的耗時 mLayoutManager.setPerformanceMonitor(mPerformanceMonitor); if (mGroupBasicAdapter == null) { this.mGroupBasicAdapter = mAdapterBuilder.newAdapter(mContext, mLayoutManager, this); //設置性能監控,mGroupBasicAdapter負責監控card和cell的耗時 mGroupBasicAdapter.setPerformanceMonitor(mPerformanceMonitor); //錯誤報告 mGroupBasicAdapter.setErrorSupport(getService(InternalErrorSupport.class)); } if (mContentView.getRecycledViewPool() != null) { //設置RecyclerView緩存池,InnerRecycledViewPool裝飾了RecycledViewPool mContentView.setRecycledViewPool(new InnerRecycledViewPool(mContentView.getRecycledViewPool())); } //註冊服務,暫不深究 register(GroupBasicAdapter.class, mGroupBasicAdapter); register(RecyclerView.RecycledViewPool.class, mContentView.getRecycledViewPool()); //設置適配器 this.mContentView.setAdapter(mGroupBasicAdapter); }
可見RecyclerView
設置的適配器是GroupBasicAdapter
,看下咱們比較關心的幾個方法,
//GroupBasicAdapter.java int getItemViewType(int position) { C data = mData.get(position); //內部緩存了Map<String, Integer> mStrKeys //String就是cell名字如SingleImageView,Integer就是一系列從0開始遞增的ViewType return getItemType(data); }
官方Demo早期用了int
來聲明Cell
,這樣容易混亂,不利於在json模板裏表意,如今改爲了String
來聲明(爲此還作了些兼容代碼),建議直接使用String
來註冊,可參考Tangram的使用,
{ "id": "banner1", "type": "container-oneColumn", "style": { "aspectRatio": 3.223 }, "items": [ { "bizId":"item1", "type": 110, //不要再使用int聲明cell,建議使用惟一字符串如SingleImageView "msg": "info1" }, { "bizId":"item2", "type": 110, //不要再使用int聲明cell,建議使用惟一字符串如SingleImageView "msg": "info2" } ] }
而後看下onCreateViewHolder
和onBindViewHolder
,
//GroupBasicAdapter.java BinderViewHolder<C, ? extends View> onCreateViewHolder(ViewGroup parent, int viewType) { //根據viewType獲得cell名字 String cellType = getCellTypeFromItemType(viewType); //大概是經過cellType幫咱們建立對應的view,暫不深究 ControlBinder<C, ? extends View> binder = mCompBinderResolver.create(cellType); //一個普通的ViewHolder,提供了bind方法 BinderViewHolder binderViewHolder = createViewHolder(binder, mContext, parent); return binderViewHolder; } void onBindViewHolder(BinderViewHolder<C, ? extends View> holder, int position) { //獲取cell C data = mData.get(position); //綁定cell holder.bind(data); } //省略調用鏈: //BinderViewHolder.bind -> BaseCellBinder.mountView -> MVHelper.mountView // -> MVHelper.postMountView -> ITangramViewLifeCycle.postBindView //回調到業務層,如TestView.java void postBindView(BaseCell cell) { //業務邏輯 textView.setText("xxx"); }
至此,整個流程就跑通了。