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}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章