共计 7078 个字符,预计需要花费 18 分钟才能阅读完成。
本系列文章主要介绍天猫团队开源的 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.java
void 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.java
mEngine.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
@Override
public 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 官网