Android | Tangram动态页面之路(二)介绍

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本系列文章主要介绍天猫团队开源的"},{"type":"link","attrs":{"href":"https://github.com/alibaba/Tangram-Android.git","title":""},"content":[{"type":"text","text":"Tangram"}]},{"type":"text","text":"框架的使用心得和原理,由于"},{"type":"codeinline","content":[{"type":"text","text":"Tangram"}]},{"type":"text","text":"底层基于"},{"type":"link","attrs":{"href":"https://github.com/alibaba/vlayout.git","title":""},"content":[{"type":"text","text":"vlayout"}]},{"type":"text","text":",所以也会简单讲解,该系列将按以下大纲进行介绍:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/e6c67e3f493e378edf74312e1","title":""},"content":[{"type":"text","text":"需求背景"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"Tangram和vlayout介绍"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"3","normalizeStart":"3"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"Tangram的使用"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"4","normalizeStart":"4"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"vlayout原理"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"5","normalizeStart":"5"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"Tangram原理"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"6","normalizeStart":"6"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","text":"Tangram二次封装"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文主要对"},{"type":"codeinline","content":[{"type":"text","text":"Tangram"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"vlayout"}]},{"type":"text","text":"的一些概念进行介绍。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"vlayout"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因为"},{"type":"codeinline","content":[{"type":"text","text":"Tangram"}]},{"type":"text","text":"底层基于"},{"type":"codeinline","content":[{"type":"text","text":"vlayout"}]},{"type":"text","text":",所以需要先了解下"},{"type":"codeinline","content":[{"type":"text","text":"vlayout"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,在view上的性能消耗通常有以下几种:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"布局嵌套导致多重measure/layout"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以使用"},{"type":"codeinline","content":[{"type":"text","text":"ConstraintLayout"}]},{"type":"text","text":"或"},{"type":"codeinline","content":[{"type":"text","text":"RelativeLayout"}]},{"type":"text","text":"减少布局嵌套"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"view的频繁创建与销毁"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"列表使用"},{"type":"codeinline","content":[{"type":"text","text":"RecyclerView"}]},{"type":"text","text":"来复用布局"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"xml转换成view解析过程产生的内存和耗时"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果列表的样式不多,使用"},{"type":"codeinline","content":[{"type":"text","text":"RecyclerView"}]},{"type":"text","text":"的复用机制可以避免大量的xml解析;如果样式比较多比如商品图墙等,则有必要把xml解析提前到编译期,在编译期根据注解将xml转成对应的view类,直接使用view类创建viewHolder,当然这么做会势必会增大包体积,需要克制使用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然后,"},{"type":"codeinline","content":[{"type":"text","text":"vlayout"}]},{"type":"text","text":"主要解决前两点,做到复杂布局下扁平和细粒度复用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"常规的"},{"type":"codeinline","content":[{"type":"text","text":"RecyclerView"}]},{"type":"text","text":"使用:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"// 设置适配器,管理数据源和view\nrecyclerView.setAdapter()\n// 设置LayoutManager,指定布局方式\nrecyclerView.setLayoutManager()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中"},{"type":"codeinline","content":[{"type":"text","text":"LayoutManager"}]},{"type":"text","text":"有3种,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"LinearLayoutManager extends LayoutManager //线性\nGridLayoutManager extends LinearLayoutManager //网格\nStaggeredGridLayoutManager extends LayoutManager //瀑布流"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在面对比较复杂的布局时,如1拖3样式,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/94/94b59bf010a94a378593c970e9acf29e.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常只能在1拖3外边套上一层layout,然后使用"},{"type":"codeinline","content":[{"type":"text","text":"LinearLayoutManager"}]},{"type":"text","text":"实现。为了解决这个问题,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"vlayout自定义了一个VirtualLayoutManager,它继承自 LinearLayoutManager;引入了 LayoutHelper 的概念,它负责具体的布局逻辑;VirtualLayoutManager管理了一系列LayoutHelper,将具体的布局能力交给LayoutHelper来完成,每一种LayoutHelper提供一种布局方式,框架内置提供了几种常用的布局类型,包括:网格布局、线性布局、瀑布流布局、悬浮布局、吸边布局等。这样实现了混合布局的能力,并且支持扩展外部,注册新的LayoutHelper,实现特殊的布局方式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"引用自"},{"type":"link","attrs":{"href":"http://pingguohe.net/2017/02/28/vlayout-design.html","title":""},"content":[{"type":"text","text":"苹果核 - Tangram 的基础 —— vlayout(Android)"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大致意思是这样,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/eb/ebb5d155c23898b27b0bec8dc747abb4.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在"},{"type":"codeinline","content":[{"type":"text","text":"vlayout"}]},{"type":"text","text":"Demo中,使用代码是这样,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//VLayoutActivity.java\n\n//子适配器集合\nList adapters = new LinkedList<>();\n\n//创建子适配器,需指定其布局方式\nSubAdapter subAdapter1 = new SubAdapter(new LinearLayoutHelper());\nadapters.add(subAdapter1);\nSubAdapter subAdapter2 = new SubAdapter(new ColumnLayoutHelper());\nadapters.add(subAdapter2);\n\n//把子适配器集合设置给代理适配器\ndelegateAdapter.setAdapters(adapters);\n\nrecyclerView.setAdapter(delegateAdapter);"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看到,随着布局样式越来越多,代码量也会越来越多,于是,"},{"type":"codeinline","content":[{"type":"text","text":"用json模板描述页面"}]},{"type":"text","text":"的"},{"type":"codeinline","content":[{"type":"text","text":"Tangram"}]},{"type":"text","text":"诞生了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Tangram"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"把"},{"type":"codeinline","content":[{"type":"text","text":"vlayout"}]},{"type":"text","text":"直接给到业务方使用,这样的接入成本是不能接受的,于是需要屏蔽掉"},{"type":"codeinline","content":[{"type":"text","text":"vlayout"}]},{"type":"text","text":"细节,让业务方用的更舒服。至于为何要引入"},{"type":"codeinline","content":[{"type":"text","text":"json模板"}]},{"type":"text","text":","},{"type":"link","attrs":{"href":"https://juejin.im/post/5eb6ae935188256d5a0d9482","title":""},"content":[{"type":"text","text":"需求背景"}]},{"type":"text","text":"里已经做过介绍。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Tangram"}]},{"type":"text","text":"的意思是七巧板,旨在用七巧板的方式拼凑出各式各样的页面。他抽象了两个概念,"},{"type":"codeinline","content":[{"type":"text","text":"Card"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"Cell"}]},{"type":"text","text":","},{"type":"codeinline","content":[{"type":"text","text":"Card"}]},{"type":"text","text":"用于描述布局方式,"},{"type":"codeinline","content":[{"type":"text","text":"Cell"}]},{"type":"text","text":"用于描述在这个布局方式下,用什么样的view去展示,比如"},{"type":"codeinline","content":[{"type":"text","text":"Tangram"}]},{"type":"text","text":"Demo里的"},{"type":"codeinline","content":[{"type":"text","text":"data.json"}]},{"type":"text","text":","}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"json"},"content":[{"type":"text","text":"[\n {\n \"type\": \"container-oneColumn\", //Card,布局方式\n \"items\": [\n {\n \"imgUrl\": \"https://gw.alicdn.com/tfs/TB1vqF.PpXXXXaRaXXXXXXXXXXX-110-72.png\",\n \"arrowImgUrl\": \"https://gw.alicdn.com/tfs/TB1vqF.PpXXXXaRaXXXXXXXXXXX-110-72.png\",\n \"title\": \"标题1\",\n \"type\": \"vvtest\" //Cell,具体展示的view\n },\n {\n \"imgUrl\": \"https://gw.alicdn.com/tfs/TB1vqF.PpXXXXaRaXXXXXXXXXXX-110-72.png\",\n \"arrowImgUrl\": \"https://gw.alicdn.com/tfs/TB1vqF.PpXXXXaRaXXXXXXXXXXX-110-72.png\",\n \"title\": \"标题2\",\n \"type\": \"vvtest\"\n }\n ]\n }\n]"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这些布局方式"},{"type":"codeinline","content":[{"type":"text","text":"Card"}]},{"type":"text","text":",在"},{"type":"codeinline","content":[{"type":"text","text":"Tangram"}]},{"type":"text","text":"内部会进行注册,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//TangramBuilder.java\nvoid installDefaultRegistry(final DefaultResolverRegistry registry) {\n // built-in cards\n registry.registerCard(TYPE_CONTAINER_BANNER, BannerCard.class);\n registry.registerCard(TYPE_SINGLE_COLUMN_COMPACT, SingleColumnCard.class);\n //...\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"布局方式确定好后,需要具体的View来展示,也就是"},{"type":"codeinline","content":[{"type":"text","text":"Cell"}]},{"type":"text","text":",比如单图"},{"type":"codeinline","content":[{"type":"text","text":"SingleImageView"}]},{"type":"text","text":",纯文本"},{"type":"codeinline","content":[{"type":"text","text":"RatioTextView"}]},{"type":"text","text":"等等,这些"},{"type":"codeinline","content":[{"type":"text","text":"Cell"}]},{"type":"text","text":"则需手动注册,如果是偏业务的"},{"type":"codeinline","content":[{"type":"text","text":"Cell"}]},{"type":"text","text":",可以在业务层按需注册,如果是更抽象的通用"},{"type":"codeinline","content":[{"type":"text","text":"Cell"}]},{"type":"text","text":",则应该下沉到基础库里全局注册,更抽象的"},{"type":"codeinline","content":[{"type":"text","text":"Cell"}]},{"type":"text","text":"意味着需要提供更为通用的配置属性,能提供给更多不同的业务方使用。"},{"type":"codeinline","content":[{"type":"text","text":"Cell"}]},{"type":"text","text":"的手动注册如下,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//TangramActivity.java\nvoid onCreate(Bundle savedInstanceState) {\n //Step 3: register business cells and cards\n builder.registerCell(1, TestView.class);\n builder.registerCell(2, SimpleImgView.class);\n \n //绑定recyclerView\n engine.bindView(recyclerView);\n \n JSONArray data = new JSONArray(new String(getAssertsFile(this, \"data.json\")));\n //设置json数据\n engine.setData(data);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Card"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"Cell"}]},{"type":"text","text":"都注册好了,通过"},{"type":"codeinline","content":[{"type":"text","text":"TangramEngine"}]},{"type":"text","text":"将数据设置进去,跟进去可以看到,"},{"type":"codeinline","content":[{"type":"text","text":"Tangram"}]},{"type":"text","text":"把布局能力交给了"},{"type":"codeinline","content":[{"type":"text","text":"vlayout"}]},{"type":"text","text":","}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//GroupBasicAdapter extends VirtualLayoutAdapter\nvoid setData(List cards, boolean silence) {\n //把Card转成LayoutHelper\n setLayoutHelpers(transformCards(cards, mData, mCards));\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Tangram"}]},{"type":"text","text":"把json模板中描述的一个个"},{"type":"codeinline","content":[{"type":"text","text":"Card"}]},{"type":"text","text":"解析成了所对应的"},{"type":"codeinline","content":[{"type":"text","text":"vlayout"}]},{"type":"text","text":"的布局方式"},{"type":"codeinline","content":[{"type":"text","text":"LayoutHelper"}]},{"type":"text","text":","}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f2/f2520be9788ea3a344e0bf0e100e6749.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"值得注意的是,"},{"type":"codeinline","content":[{"type":"text","text":"Tangram"}]},{"type":"text","text":"Demo里的json模板都是包含了业务数据的,这么做可能是为了剔除掉网络请求的代码,方便开源学习,而在实际业务中不太可能把数据绑定在模板里,这样模板会很臃肿,我们要做的是,"},{"type":"codeinline","content":[{"type":"text","text":"用模板描述页面结构和数据源"}]},{"type":"text","text":",而非数据本身,如,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"json"},"content":[{"type":"text","text":"{\n \"template\":[\n {\n \"card\": \"container-banner\", //轮播图模块,使用banner布局方式\n \"cell\":\"imageView\", //具体的view就是一张图片\n \"data\":\"makeup:banner\" //数据来源于聚合接口,key为banner\n },\n {\n \"card\": \"container-fiveColumn\", //类目模块,使用Column布局方式\n \"cell\":\"imageAndTextView\", //具体的view是上图下文本\n \"data\":\"makeup:category\" //数据来源于聚合接口,key为category\n },\n {\n \"card\": \"container-waterfall\", //商品流模块,使用Staggered布局方式\n \"cell\":\"goodsView\", //具体的view是商品样式\n \"data\":\"request:recommend\" //数据来源于request,key为recommend\n }\n ]\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"既然json模板可以由后端下发,也就意味着,我们可以让运营同学通过后台拖动模块搭建页面,选择资源位设置数据源,然后生成json模板下发,开发同学从此就可以解放双手,做"},{"type":"text","marks":[{"type":"del"}],"text":"更有趣"},{"type":"text","text":"(更有挑战)的事情了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不过这都是后话了,对"},{"type":"codeinline","content":[{"type":"text","text":"Tangram"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"vlayout"}]},{"type":"text","text":"的介绍就到这里了,下篇文章将对Tangram的使用进行更详细的讲解,点关注,不迷路~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"参考文章"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"http://pingguohe.net/2017/02/28/vlayout-design.html","title":""},"content":[{"type":"text","text":"苹果核 - Tangram 的基础 —— vlayout(Android)"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"http://pingguohe.net/2016/12/20/Tangram-design-and-practice.html","title":""},"content":[{"type":"text","text":"苹果核 - 页面动态化的基础 —— Tangram"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a5/a59a19a54e6436e666d4a9b74fff8c29.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章