本系列文章主要介绍天猫团队开源的Tangram框架的使用心得和原理,由于Tangram
底层基于vlayout,所以也会简单讲解,该系列将按以下大纲进行介绍:
- 需求背景
- Tangram和vlayout介绍
- Tangram的使用
- vlayout原理
- Tangram原理
- Tangram二次封装
本文将对Tangram
的简单使用进行介绍。
Demo代码
基础使用
引入依赖:
//tangram相关:tangram使用3.0之前的最新版本,其他直接使用最新版本 implementation 'com.alibaba.android:tangram:2.2.5@aar' //tangram底层支持:vlayout implementation 'com.alibaba.android:vlayout:1.2.36@aar' //tangram虚拟视图(更灵活的视图,后面单独开篇讲) implementation('com.alibaba.android:virtualview:1.4.6@aar') { transitive true } //tangram支持banner翻页用的 implementation 'com.alibaba.android:ultraviewpager:1.0.7.8@aar' //tangram内部需要rxjava implementation 'io.reactivex.rxjava2:rxjava:2.1.12' implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
初始化,主要是传递上下文进去,和设置图片加载能力,这里我们使用Glide
,
public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); TangramBuilder.init(this, new IInnerImageSetter() { @Override public <IMAGE extends ImageView> void doLoadImageUrl(@NonNull IMAGE view, @Nullable String url) { Glide.with(view.getContext()).load(url). error(R.mipmap.ic_launcher). into(view); } }, NetImageView.class); }}
在activity中使用,
//MainActivity.javavoid onCreate(Bundle savedInstanceState) { //创建builder来配置参数 TangramBuilder.InnerBuilder builder = TangramBuilder.newInnerBuilder(this); //注册自己的cell builder.registerCell(ImageTextView.class.getSimpleName(), ImageTextView.class); builder.registerCell(SingleImageView.class.getSimpleName(), SingleImageView.class); //创建引擎 mEngine = builder.build(); //绑定RecyclerView mEngine.bindView(mBinding.rvList); mBinding.rvList.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); //在 scroll 事件中触发 engine 的 onScroll,内部会触发需要异步加载的卡片去提前加载数据 mEngine.onScrolled(); } }); //设置数据,触发渲染 String file = FileUtil.getAssertsFile(this, "main.json"); try { mEngine.setData(new JSONArray(file)); } catch (JSONException e) { e.printStackTrace(); }}
前边的介绍篇提到过,Tangram
内置了一些布局方式Card
,基本满足需求了,所以我们只需定制自己的具体View也就是Cell
即可,上边手动注册了两个Cell
,ImageTextView
和SingleImageView
,我们先来看ImageTextView
,他是一个LinearLayout
,上边一个图标,下边一个文本,需要实现ITangramViewLifeCycle
接口,在相应的回调里执行自己的逻辑,
public class ImageTextView extends LinearLayout implements ITangramViewLifeCycle { private NetImageView mImgIcon; private TextView mTvTitle; public ImageTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setOrientation(VERTICAL); setGravity(Gravity.CENTER); inflate(getContext(), R.layout.cell_image_text, this); mImgIcon = findViewById(R.id.img_icon); mTvTitle = findViewById(R.id.tv_title); } @Override public void cellInited(BaseCell cell) { } @Override public void postBindView(BaseCell cell) { mImgIcon.load(cell.optStringParam("imgUrl")); mTvTitle.setText(cell.optStringParam("title")); } @Override public void postUnBindView(BaseCell cell) { }}
其布局文件cell_image_text.xml
如下,
<merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content" android:layout_height="wrap_content"> <com.holiday.tangram.view.NetImageView android:id="@+id/img_icon" android:layout_width="50dp" android:layout_height="50dp" android:scaleType="centerCrop" tools:background="@color/colorAccent" /> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:ellipsize="end" android:maxLines="1" tools:text="@string/app_name" /></merge>
Cell
都准备好了,下面就是准备json模板
了,先来一个四列布局container-fourColumn
,
{ "type": "container-fourColumn", "items": [ { "imgUrl": "https://tva1.sinaimg.cn/large/007S8ZIlgy1geqp9zhftrj303r03r3yl.jpg", "title": "标题1", "type": "ImageTextView" }, { "imgUrl": "https://tva1.sinaimg.cn/large/007S8ZIlgy1geqp9zhftrj303r03r3yl.jpg", "title": "标题2", "type": "ImageTextView" }, { "imgUrl": "https://tva1.sinaimg.cn/large/007S8ZIlgy1geqp9zhftrj303r03r3yl.jpg", "title": "标题3", "type": "ImageTextView" }, { "imgUrl": "https://tva1.sinaimg.cn/large/007S8ZIlgy1geqp9zhftrj303r03r3yl.jpg", "title": "标题4", "type": "ImageTextView" } ]}
运行如下,
下面再来看另一个自定义Cell
,SingleImageView
很简单,就是单图,
public class SingleImageView extends NetImageView implements ITangramViewLifeCycle { public SingleImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setScaleType(ScaleType.CENTER_CROP); } @Override public void cellInited(BaseCell cell) { } @Override public void postBindView(BaseCell cell) { load(cell.optStringParam("imgUrl")); } @Override public void postUnBindView(BaseCell cell) { }}
然后来一个1拖n布局container-onePlusN
,
{ "type": "container-onePlusN", "style": { "aspectRatio": "1.778", "margin": "[10,0,0,0]" }, "items": [ { "imgUrl": "https://wanandroid.com/blogimgs/942a5c62-ca87-4e7c-a93d-41ff59a15ba4.png", "type": "SingleImageView" }, { "imgUrl": "https://www.wanandroid.com/blogimgs/62c1bd68-b5f3-4a3c-a649-7ca8c7dfabe6.png", "type": "SingleImageView" }, { "imgUrl": "https://www.wanandroid.com/blogimgs/50c115c2-cf6c-4802-aa7b-a4334de444cd.png", "type": "SingleImageView" }, { "imgUrl": "https://www.wanandroid.com/blogimgs/90c6cc12-742e-4c9f-b318-b912f163b8d0.png", "type": "SingleImageView" } ]}
图片取自玩安卓的banner图,运行如下,
因为有4条数据,所以展示的效果就是1拖3。
然后再来个轮播图布局container-banner
,他的Cell
还是使用单图SingleImageView
,
{ "type": "container-banner", "style": { "margin": "[0,0,10,0]", "pageWidth": 200, "pageHeight": 100, "indicatorMargin": "5", "infinite": "true", "indicatorImg2": "https://img.alicdn.com/tps/TB1XRNFNXXXXXXKXXXXXXXXXXXX-32-4.png", "indicatorImg1": "https://img.alicdn.com/tps/TB16i4qNXXXXXbBXFXXXXXXXXXX-32-4.png", "scrollMarginLeft": "10", "indicatorGap": "2", "indicatorHeight": "1.5", "itemRatio": "2.654", "scrollMarginRight": "10", "indicatorGravity": "center", "hGap": "20" }, "items": [ { "imgUrl": "https://wanandroid.com/blogimgs/942a5c62-ca87-4e7c-a93d-41ff59a15ba4.png", "type": "SingleImageView" }, { "imgUrl": "https://www.wanandroid.com/blogimgs/62c1bd68-b5f3-4a3c-a649-7ca8c7dfabe6.png", "type": "SingleImageView" }, { "imgUrl": "https://www.wanandroid.com/blogimgs/50c115c2-cf6c-4802-aa7b-a4334de444cd.png", "type": "SingleImageView" }, { "imgUrl": "https://www.wanandroid.com/blogimgs/90c6cc12-742e-4c9f-b318-b912f163b8d0.png", "type": "SingleImageView" } ]}
可以看出可供配置的参数还是很多的,文档可以看这里,运行如下,
整体效果如下,
内置support支持
Tangram
内置了一些support
支持,如处理点击SimpleClickSupport
,卡片数据加载CardLoadSupport
,曝光逻辑ExposureSupport
等,可以通过TangramEngine
的add
方法注册,如下,
//MainActivity.javamEngine.addSimpleClickSupport(new MyClickSupport());
然后看MyClickSupport
,
public class MyClickSupport extends SimpleClickSupport { public MyClickSupport() { setOptimizedMode(true); } @Override public void defaultClick(View targetView, BaseCell cell, int eventType) { super.defaultClick(targetView, cell, eventType); QrToast.show(cell.stringType); }}
我们可以在自己的Cell
里对view设置点击事件,也可以把点击事件交给support
全局处理,如果要使用support
处理点击事件,需要在Cell
里加这行代码,
//SingleImageView.java@Overridepublic void cellInited(BaseCell cell) { setOnClickListener(cell);}
比如商城首页,大多数模块都是点击进行页面跳转,行为比较单一,不需要每个Cell
都去做点击事件,SimpleClickSupport
就能很好的支持,在defaultClick
里取出页面短链进行跳转即可,如,
public class MyClickSupport extends SimpleClickSupport { public MyClickSupport() { setOptimizedMode(true); } @Override public void defaultClick(View targetView, BaseCell cell, int eventType) { super.defaultClick(targetView, cell, eventType); //取出页面短链,进行跳转。短链既可以是h5的,也可以是native的,由Router内部处理 PageRouter.to(cell.optStringParam("link")); }}
用起来很简单有木有!好啦,Tangram
的使用就介绍到这里了,后面会分析原理,实现模板和数据分离等等,敬请期待~
参考文章
- GitHub-Tangram使用文档
- Tangram官网