Android | Tangram 动态页面之路(五)Tangram 原理

用户头像
哈利迪
关注
发布于: 2020 年 05 月 20 日
Android | Tangram动态页面之路(五)Tangram原理

本系列文章主要介绍天猫团队开源的Tangram框架的使用心得和原理,由于Tangram底层基于vlayout,所以也会简单讲解,该系列将按以下大纲进行介绍:



  1. 需求背景



  1. Tangram和vlayout介绍



  1. Tangram的使用



  1. vlayout原理



  1. Tangram原理



  1. Tangram二次封装



本文将对Tangram进行初步讲解。



基于Tangram最新源码分析



笔者Demo代码



Tangram



Tangram和vlayout介绍这篇文章提到过,Tangram通过解析json模板得到布局方式Card和具体视图Cell,然后将Card转换成对应的vlayoutLayoutHelper来进行测量和布局,如下,





官网的架构图如下,





Card转成LayoutHelper



跟进TangramActivityengine.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上的呢?



RecyclerView展示



跟进TangramActivityengine.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"
}
]
}



然后看下onCreateViewHolderonBindViewHolder



//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");
}



至此,整个流程就跑通了。



参考文章







发布于: 2020 年 05 月 20 日 阅读数: 529
用户头像

哈利迪

关注

公众号:哈利迪ei,技术从未如此性感 2019.02.13 加入

还未添加个人简介

评论 (1 条评论)

发布
用户头像
感谢分享,内容推荐到InfoQ首页了。
2020 年 05 月 20 日 21:48
回复
没有更多了
Android | Tangram动态页面之路(五)Tangram原理