本系列文章主要介绍天猫团队开源的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"); }
至此,整个流程就跑通了。