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

  1. 需求背景
  2. Tangram和vlayout介绍
  3. Tangram的使用
  4. vlayout原理
  5. Tangram原理
  6. 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即可,上边手动注册了两个CellImageTextViewSingleImageView,我们先来看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"        }    ]}

运行如下,

下面再来看另一个自定义CellSingleImageView很简单,就是单图,

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等,可以通过TangramEngineadd方法注册,如下,

//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官网