日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

UI2Code智能生成Flutter代码——机器生成代码

發(fā)布時(shí)間:2024/8/23 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 UI2Code智能生成Flutter代码——机器生成代码 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

背景

在《UI2CODE--整體設(shè)計(jì)》篇中,我們提到UI2Code工程的整體流程。前步圖片分析之后,我們可以得到對(duì)應(yīng)的DSL布局描述。利用DSL的資訊,結(jié)合IntelliJ Plugin介面工具,面向使用者提供生成對(duì)應(yīng)Flutter代碼。

本篇主要介紹我們?nèi)绾翁幚鞤SL的資訊,想法上即是Flutter的翻譯機(jī)。總體概念如下:

輸入的DSL是什么?

DSL做為一種描述語言,抽象表示為了解決某一類任務(wù)而專門設(shè)計(jì)的計(jì)算機(jī)語言。在此我們的DSL代表圖像識(shí)別和布局識(shí)別側(cè)的輸出,為一JSON格式。

這些資訊主要描述了這個(gè)圖層(Layer)的范圍(Frame)、是什么樣子的類型(Type)、是什么樣子的樣式(Styles)、含有哪些數(shù)據(jù)(Value)等等。圖層集(Layers)欄位則代表了這張視覺稿的所有圖層。

核心思路

本節(jié)的目標(biāo)是將DSL翻譯成目標(biāo)的Flutter代碼。我們首先需要理解的是分散的圖層間的關(guān)系,可能會(huì)有交疊、可能是并列排版。知道了關(guān)系之后,需想辦法轉(zhuǎn)化成Flutter widget的視圖,根據(jù)此視圖來生產(chǎn)對(duì)應(yīng)代碼。

架構(gòu)上我們把DSL tree和Flutter tree的建立,分拆為兩個(gè)獨(dú)立的分界。這樣比較容易定義問題,并且保持彈性。如果今天的目標(biāo)語言換成Weex或是iOS UI,我們就只需要更動(dòng)代碼翻譯的模組。

第一把刀:DSL tree建立

上圖的左側(cè)代表了來源DSL的layers資料,代表者一個(gè)一個(gè)的圖層。右側(cè)是目標(biāo)的DSL Tree,這棵樹的結(jié)構(gòu)上明確敘述了圖層之間的包裹、交疊等關(guān)系。并且包含了某些特殊關(guān)系的節(jié)點(diǎn)聚合。

作法上利用每個(gè)Layer的Frame,以及所屬的類別(文字、圖像、容器),利用下面的規(guī)則組合樹的關(guān)系:

  • 圖層之間的包裹關(guān)系,例如某些圖層為容器,代表下面是可以掛其他節(jié)點(diǎn)的(這邊帶有背景屬性的容器,我們定義稱之為Shape
  • 區(qū)塊式組件(Block, 如ListView/GridView)。可以將圖層組成View item的關(guān)系
  • 閑魚定義的組件資訊(如CI以及BI),這部份非閑魚工程可以忽略
  • 重復(fù)布局(Repeat)的資訊,將相同的圖層歸類合并,目的為簡(jiǎn)化樹
  • 根據(jù)以上我們采用了分層,由大至小的次序?qū)ayer分群合并。另外,在合并時(shí)layer之間彼此可能有關(guān)聯(lián);它們可能同屬于Block,也可能同屬于某個(gè)Repeat。所以對(duì)于上面定義的Repeat、BI、Block、CI、Shape都可能有交錯(cuò)的嵌套關(guān)系,這是必須要處理的部份。

    第二把刀:Flutter tree建立

    在Flutter Tree的建構(gòu)中,核心概念先處理布局。布局的概念如剝洋蔥一般,我們先去除四周的padding,然后以人類視覺layout的直覺先嘗試橫切分,再進(jìn)行豎切分

    1.先剝洋蔥去除padding

    2.接著我們的算法會(huì)先嘗試是否可以橫切,如下圖我們可以切割成為Row1/ Row2

    3.針對(duì)Row1在嘗試再進(jìn)行豎切,如下圖可以得到Column1/ Column2/ Column3

    根據(jù)以上切分的規(guī)則,我們就可以定義出如Row、Column、Padding的幾個(gè)節(jié)點(diǎn),以及它們的Parent/ Child關(guān)系。將DSL tree同一層的節(jié)點(diǎn)做切分,一邊切分一邊建立Flutter node,遍歷完整顆DSL,即可得到粗略的Flutter tree關(guān)系。

    = 無法切分時(shí)的處理

    當(dāng)圖層切分不開時(shí),這時(shí)候就要使用絕對(duì)布局疊層的概念,這個(gè)概念在Flutter內(nèi)稱之為Stack。

    多個(gè)圖層在DSL tree的關(guān)系為兄弟節(jié)點(diǎn),根據(jù)此些圖層的Frame,我們判斷出來它們是彼此相交的,我們會(huì)以Z-order概念,來決定上下交疊的關(guān)系。最后,這些圖層將組成一個(gè)新Stack節(jié)點(diǎn),并且產(chǎn)生此節(jié)點(diǎn)的Frames為此些圖層覆蓋的范圍。

    = 針對(duì)文字的進(jìn)階處理

    基本上交疊的圖層以Stack的處理就可以正確顯示,但在文字圖層上可能含有誤區(qū)。

    如上圖因?yàn)槲淖直旧淼纳舷伦笥沂呛衟adding的,在我們圖層的識(shí)別時(shí),可能會(huì)計(jì)算出彼此的frame是交疊的,但實(shí)際上UI希望它們并不是Stack關(guān)系。

    ?

    為了解決這個(gè)問題,我們引入了一個(gè)oriFrame的概念,用文字最原始的像素當(dāng)做是oriFrame。所以遇到為文字的圖層時(shí),我們會(huì)先判斷本身的oriFrame是否交疊,如果是的話才采用Stack切割,否則就以此oriFrame對(duì)原始的frame做修正。

    文字還有什么特性?

    另外,因?yàn)槲淖值膬?nèi)容通常是動(dòng)態(tài)的,所以擁有了”所見不一定為所得”的特性。這些特性主要包含了是否該換行、內(nèi)容區(qū)域是否可以拉伸、文字Padding等,這些特性都會(huì)影響到我們的布局。

    以下圖為例,我們?cè)谔幚鞮ayout時(shí)肉眼很明顯可以知道這些特徵。文字的行數(shù)我們可以以視覺稿當(dāng)做最大顯示范本,文字區(qū)域的寬度部分,則需要特別判斷哪些區(qū)域是可以被拉伸的。

    確立文字范圍

    在決定拉伸對(duì)象之前,我們需要定義哪些widget是將內(nèi)容完整顯示,不能被拉伸的:如圖片、Container容器、Stack區(qū)域、Component組件

    接著處理的流程如下:

  • 首先判斷所有Child內(nèi)是否有多行文字或?qū)挾裙潭ǖ奈淖?#xff0c;如果是的話針對(duì)其處理。需要加上Flex。
  • 若無以上的狀況,則判斷Child間的Padding關(guān)系
  • 如果可拉伸的widget的Padding大于平均值的個(gè)數(shù)有多個(gè),則這些都加上Flex
  • 如果只有單個(gè)時(shí),則找尋最大Padding的widget(使用分群拉伸算法)
  • 最后,但當(dāng)Row里面存有拉伸的狀況時(shí),需要把Row的最后一個(gè)child加上Right padding,否則拉伸元素會(huì)填滿父容器。
  • 分群拉伸算法:這個(gè)算法的目的是找到最佳拉伸的對(duì)象。我們的思考上將Widget做分組,分組后判斷整體的Alignment(如左右對(duì)齊)或是拉伸關(guān)系。若在拉伸狀況下,判斷適合讓哪個(gè)組別拉伸,在進(jìn)一步判斷適合讓組別的內(nèi)部元件拉伸。

    舉例如下為一個(gè)Row排列的控件,其中排列為Image、CI、Text1、Text2、Text3:

    依據(jù)Widget之間的距離,在上圖分為了Group1及Group2兩個(gè)群體。先以Group1判斷是否存在可拉伸的對(duì)象, 接著才判斷Group2。所以這5個(gè)Widget分別得到了3, 2, 1, 4, 5的優(yōu)先級(jí)。以本例而言,Text1為最高優(yōu)先,而且其為可拉伸的,故決定將Flex屬性加于此。

    在Expanded的處理上,是我們目前遇到最大的困難點(diǎn),甚至人工判斷都可能有歧義。上面的規(guī)則是我們歸納出眾多視覺稿的通解,但不能100%完全解決問題。所以這部份判斷錯(cuò)誤的部分,我們期待在Plugin的交互中使用人工解決。

    = 判斷Alignment優(yōu)化

    以上的處理已經(jīng)可以正確生成Flutter tree,但是我們想進(jìn)一步地將Flutter代碼更加優(yōu)雅。在此我們針對(duì)了三種元件的Alignment做了處理,分別是Container、Row、Column,其概念都是分析內(nèi)部元件的padding關(guān)系,決定為居左、居中、或是居右對(duì)齊。

    舉例如Column內(nèi)部的children我們?nèi)ヅ袛嘧笥业膒adding是否相等。若是則移除其padding,并且加上crossAxisAlignment為center。

    針對(duì)Row/ Container我們則會(huì)判斷crossAxisAlignment(垂直方向)以及mainAxisAlignment(水平方向)。水平部份,這邊我們采用更精細(xì)的方法,我們利用歐式距離建立一個(gè)非監(jiān)督算法,計(jì)算views是更為接近哪一個(gè)(居左、居中、居右)。算法這邊先不詳述,之后再以篇幅介紹。

    最后:生成Flutter代碼

    經(jīng)過前面的步驟后,最終我們產(chǎn)生了一個(gè)Flutter Tree。生成時(shí)在節(jié)點(diǎn)的定義上,我們分為了兩種,分別是View與Layout,以是否可以擁有Child為區(qū)別。以下是我們針對(duì)Flutter Tree所定義的部份類別:

    在節(jié)點(diǎn)的定義中,皆存儲(chǔ)了各節(jié)點(diǎn)的Parent、Child屬性。根據(jù)這些關(guān)系,我們定義每個(gè)節(jié)點(diǎn)的代碼樣板,例如FColumn對(duì)應(yīng)的樣板為:

    ?

    new Column( #{alignment}, children: <Widget>[#{children}, ]

    ),?

    最后我們以Root widget開始遍歷整顆樹,將每個(gè)節(jié)點(diǎn)所生成的Flutter代碼結(jié)合,這樣我們就可以得到整個(gè)Widget tree的代碼了。

    數(shù)據(jù)分離

    為了更好的重復(fù)利用生成代碼,我們把生成的代碼和數(shù)據(jù)再進(jìn)一步做分離。分離后輸出分為代碼區(qū)以及Data model數(shù)據(jù)區(qū):

    我們切割這些區(qū)域的目的為簡(jiǎn)化Widget tree直觀上的代碼復(fù)雜度,以及將數(shù)據(jù)抽離,讓資料可由外部呼叫傳入,以達(dá)成動(dòng)態(tài)性。

    整體架構(gòu)回顧

    總合以上的概念,工程的細(xì)部架構(gòu)如下:

    前面所說的針對(duì)文字以及Alignment的處理,在這邊我們?cè)O(shè)計(jì)了一個(gè)工廠模式,如上圖中經(jīng)過Flutter Tree Builder后,我們可以去遍歷整顆Widget tree,在工廠中判斷判斷符合條件的規(guī)則,經(jīng)過處理去震蕩優(yōu)化原本的Widget tree。在這邊未來我們可以不斷地加上合適的規(guī)則,讓W(xué)idget tree更加優(yōu)化。

    整體架構(gòu)使用靜態(tài)分析的方法,讀到此各位可能會(huì)有疑問:一些如動(dòng)態(tài)的事件、View的Visibility、Input輸入文字框等怎么處理?由于這些動(dòng)態(tài)性在靜態(tài)分析下無法解決,所以我們?cè)鰪?qiáng)了Plugin上的編輯性,使用者只要勾選某些屬性,即會(huì)在生成代碼時(shí)自動(dòng)判斷,在Flutter自動(dòng)增加對(duì)應(yīng)的邏輯。以彌補(bǔ)靜態(tài)圖無法處理的問題。

    由于UI的靈活性高,十個(gè)人寫的代碼可能有十種不同風(fēng)格。并且在分析上游的UI2DSL,以及Flutter代碼的翻譯,某些部份的精確性取決于我們的樣本的認(rèn)知,是否能夠在有限的樣本內(nèi)觀查出泛化的定律,分析上還是存有很多挑戰(zhàn)性。

    結(jié)合落地業(yè)務(wù)

    在整個(gè)UI2CODE的效果中,大約七成以上的頁面都可以正確分析出來,剩下的是一些小細(xì)節(jié)如文字的處理等,基本上我們工具都能夠?qū)⒋罂蚣艿奶幚砗?#xff0c;使用者可能只需微小的調(diào)整。

    UI2CODE案子在內(nèi)部團(tuán)隊(duì)上線后,已經(jīng)在閑魚APP內(nèi)的"玩家頁面"采用了自動(dòng)化生成的代碼。在采用自動(dòng)化工具后,大約減少了三分之二的UI開發(fā)時(shí)間(因初期還在熟悉工作流程,未來相信可以更快速)。同時(shí),若在客戶端大量采用我們工具,還可以讓團(tuán)隊(duì)的代碼結(jié)構(gòu)有一些的規(guī)范,讓生成工具來規(guī)范Widget UI以及Data Binding的框架,一致性以及后續(xù)的維護(hù),相信是一個(gè)很大的誘因。

    并且閑魚團(tuán)隊(duì)近期計(jì)畫開發(fā)一款新的APP,在初期時(shí)能夠快速開發(fā)UI,也將采用我們的工具。期望有更多的業(yè)務(wù)和經(jīng)驗(yàn)積累。

    后續(xù)計(jì)畫

    近期我們推出了第一版UI2CODE,先計(jì)畫于內(nèi)部團(tuán)隊(duì)使用,利用使用的經(jīng)驗(yàn),讓我們?cè)诏B代之下不斷提高準(zhǔn)確性。并且,我們正在調(diào)研結(jié)合NLP以及AST(語法樹)的可能性,希望能夠產(chǎn)出更有質(zhì)量的代碼。

    我們也期望未來能將此工具開放于Flutter community,對(duì)于推動(dòng)整個(gè)Flutter技術(shù)有所推進(jìn)。希望能讓更多人跟我們一起找尋更有效率的寫代碼方法,如果有任何想法歡迎與我們交流,我們也持續(xù)不斷地在進(jìn)化工具中,謝謝各位的閱讀!


    原文鏈接
    本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。

    總結(jié)

    以上是生活随笔為你收集整理的UI2Code智能生成Flutter代码——机器生成代码的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。