前文介紹了模板的基本格式、虛擬控件與原生控件混合使用的方式。本文重點在把這兩塊內容串起來介紹一下,如何實現從模板生成一個運行時的控件,並如何註冊一個自定義控件使用。html
舉個簡單的例子,在 XML 模板裏,可能會有這麼一塊控件的使用:java
<NText
id="1"
text="title"
textSize="12"
textColor="#333333"
layoutWidth="wrap_content"
layoutHeight="wrap_content"
lineSpaceMultiplier="1.1"
lines="2"
flag="flag_event|flag_exposure|flag_clickable"
/>
複製代碼
這在 VirtualView 表示引用一個文本控件(VirtualView 內置支持的全部控件見文檔),在《VirtualView Android實現詳解(一)—— 文件格式與模板編譯》裏曾講過會將 XML 裏的字符串等編譯成整型數值或者索引來下降解析成本。所以從在 XML 裏使用一個控件到運行時渲染它,就要通過一系列的轉換過程,其中有一半的過程是事先離線執行的,另外一半的過程纔是在客戶端裏運行時執行。如下這張圖歸納了整個流程:android
說明一下每一個步驟:git
以建立一個 PicassoImage 爲例(雖然內置了 VImage 和 NImage 兩個控件,但在實際業務場景中,仍是使用一個自定義的圖片控件比較合適,這樣能夠更好利用起結合圖片庫的內存管理、性能優化等 feature)。github
在編譯工具裏配置文件裏定義:性能優化
VIEW_ID_PicassoImage=1014
,其中 PicassoImage
就是 XML 裏的標籤名,id 值爲 1014,這個是自定義的,建議從 1001開始,前 1000 保留給系統使用;degree=Float
,表示屬性名是 degree ,屬性值按 Float 類型編譯解析;url=String
,表示屬性名是 url,屬性值按 String 類型編譯,不過未在配置文件裏聲明的屬性都是按 String 類型編譯的,因此能夠省略;取名 PicassoImageView
,繼承 ImageView
,實現 IView
接口,由於 demo 比較簡單,除此以外不作其餘邏輯,主要實現 IView
的接口調用對應的系統 measure、layout 方法,由於這些方法是不能在外部調用的,只能經過 IView
的接口封裝一下暴露出去。bash
詳細代碼:PicassoImageView.java網絡
取名 PicassoImage
,繼承 ViewBase
,在構造函數裏實例化 PicassoImageView
,並獲取自定義屬性的 id;app
public PicassoImage(VafContext context,
ViewCache viewCache) {
super(context, viewCache);
mPicassoImageView = new PicassoImageView(context.getContext());
StringSupport mStringSupport = context.getStringLoader();
// 這裏會取加載的模板數據裏取獲取對應的 id,第一個參數是屬性名,第二個參數應當爲 false;
urlId = mStringSupport.getStringId("url", false);
degreeId = mStringSupport.getStringId("degree", false);
}
複製代碼
因爲 ViewBase
自己也是實現 IView
接口的,因此複寫幾個 IView
的 measure、layout 接口,去調用對應的 PicassoImageView
裏的接口。在 VirtualView 體系內部,都是經過 ViewBase
對象來驅動佈局計算的,所以必須經過 IView
接口調用系統 View
真正的計算接口。框架
@Override
public void onComMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mPicassoImageView.onComMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public void onComLayout(boolean changed, int l, int t, int r, int b) {
mPicassoImageView.onComLayout(changed, l, t, r, b);
}
@Override
public void comLayout(int l, int t, int r, int b) {
super.comLayout(l, t, r, b);
//這一步很關鍵,不然 view 不顯示。
mPicassoImageView.comLayout(l, t, r, b);
}
複製代碼
剩下的主要邏輯是處理自定義屬性,有幾個 setAttribute
,setRPAttribute
重載的方法,它們用於接收不一樣類型的屬性值:
boolean setAttribute(int key, int value)
處理編譯成整數類型的屬性;boolean setAttribute(int key, float value)
處理編譯成浮點數類型的屬性;boolean setAttribute(int key, String stringValue)
處理編譯成字符串類型的屬性,包括那些本該編譯成整數或者浮點數但由於寫了表達式被編譯成字符串類型的;boolean setRPAttribute(int key, int value)
處理編譯成整數類型的尺寸屬性,單位是 rp(介紹在此);boolean setRPAttribute(int key, float value)
處理編譯成浮點數類型的尺寸屬性,單位是 rp;基礎 ViewBase
裏解析處理了大量基礎屬性,因此自定義控件只要處理新增的自定義屬性就好了。以上這些重載方法都有一個 Boolean 返回值,它遵循冒泡邏輯,當你返回 true 的時候,當前層級處理了這個屬性,不然表示當前層級處理不了這個屬性,須要進一步交給子類解析;在本文的示例裏,是這麼處理的:
@Override
protected boolean setAttribute(int key, float value) {
boolean ret = true;
if (key == degreeId) {
//從模板裏直接獲取到旋轉角度屬性值
degrees = value;
} else {
ret = super.setAttribute(key, value);
}
return ret;
}
@Override
protected boolean setAttribute(int key, String stringValue) {
boolean ret = true;
if (key == degreeId) {
//從模板裏直接獲取到旋轉角度屬性值是一個表達式,暫存到 viewCache 裏,等傳入數據的時候再次解析,而後回調到上述 setAttribute(int key, float value) 方法裏獲取最終值
if (Utils.isEL(stringValue)) {
mViewCache.put(this, degreeId, stringValue, Item.TYPE_FLOAT);
}
} else if (key == urlId) {
//從模板裏直接獲取到url屬性值多是一個表達式,也多是個直接的 url,若是是表達式,暫存到 viewCache 裏,等傳入數據的時候再次解析,而後回調本方法裏獲取最終值
if (Utils.isEL(stringValue)) {
mViewCache.put(this, urlId, stringValue, Item.TYPE_STRING);
} else {
url = stringValue;
}
} else {
ret = super.setAttribute(key, stringValue);
}
return ret;
}
複製代碼
最後就是使用這些屬性值,在 onParseValueFinised()
裏一次性應用屬性:
@Override
public void onParseValueFinished() {
super.onParseValueFinished();
Picasso.with(mContext.getContext()).load(url).rotate(degrees).into(mPicassoImageView);
}
複製代碼
詳細代碼:PicassoImage.java
經過 ViewManager
裏的 ViewFactory
註冊,以下:
sViewManager.getViewFactory().registerBuilder(1014,new PicassoImage.Builder());
複製代碼
XML 裏這麼寫:
<VHLayout
flag="flag_exposure|flag_clickable"
orientation="V"
layoutWidth="match_parent"
layoutHeight="match_parent"
>
<VText
text="Title: Loading Image with Picasso"
textSize="12"
textColor="#333333"
background="#008899"
layoutWidth="match_parent"
layoutHeight="20" />
<PicassoImage
url="${url}"
degree="90"
layoutWidth="match_parent"
layoutHeight="300" />
</VHLayout>
複製代碼
綁定的數據:
{
"url": "https://user-gold-cdn.xitu.io/2018/3/20/16242d5e3adadb1f?w=200&h=200&f=png&s=16211"
}
複製代碼
運行的結果:
圖片原圖是這樣的:
能夠看到,經過添加自定義的 degree 屬性,並調用 Picasso 的 ratate 方法,最終加載了圖片,也旋轉了圖片,能夠根據此思路繼續爲 PicassImage
添加更多 Picasso 支持的屬性。
本文裏用到的例子也上傳到了 demo 裏,從上午的源碼連接裏能夠獲取到完整的 demo。
仍是那句話,講得再多,不如親自上手體驗一下,能夠參考《天貓客戶端組件動態化的方案——VirtualView 上手體驗》、《提高開發體驗,預覽 VirtualView》來體驗。