本文翻譯自《50 android hacks》
依照慣例,先從乙個例子說起。
很簡單,3張撲克牌疊在一起顯示。這個布局效果該如何實現呢?有的同學該說了,這很簡單啊,用relativelayout或framelayout,然後為每乙個撲克牌設定margin就能實現了。
ok,那就看一下通過這種方式是如何實現的。**如下:
效果圖
沒錯,通過這種方式是可以實現的。但是,不覺得這種方式有點low嗎?!讓我們用高階一點的方式去實現它,提公升一下自己的逼格!
定製viewgroup之前,我們需要先理解幾個概念。
android繪製檢視的方式
這裡我不會涉及太多的細節,但是需要理解android開發文件中的一段話:
「繪製布局由兩個遍歷過程組成:測量過程和布局過程。測量過程由measure(int, int)方法完成,該方法從上到下遍歷檢視樹。在遞迴遍歷過程中,每個檢視都會向下層傳遞尺寸和規格。當measure方法遍歷結束,每個檢視都儲存了各自的尺寸資訊。第二個過程由layout(int,int,int,int)方法完成,該方法也是由上而下遍歷檢視樹,在遍歷過程中,每個父檢視通過測量過程的結果定位所有子檢視的位置資訊。」
簡而言之,第一步是測量viewgroup的寬度和高度,在onmeasure()方法中完成,viewgroup遍歷所有子檢視計算出它的大小。第二步是根據第一步獲取的尺寸去布局所有子檢視,在onlayout()中完成。
建立cascadelayout
終於到了定製viewgroup的階段了。假設我們已經定製了乙個cascadelayout的容器,我們會這樣使用它。
首先,定義屬性。在values資料夾下面建立attrs.xml,**如下:
同時,為了嚴謹一些,定義一些預設的垂直距離和水平距離,以防在布局中沒有提供這些屬性。
在dimens.xml中新增如下**:
10dp
10dp
準備工作已經做好了,接下來看一下cascadelayout的原始碼,略微有點長,後面幫助大家分析一下。
public class cascadelayout extends viewgroup finally
} @override
protected void onmeasure(int widthmeasurespec, int heightmeasurespec)
width += child.getmeasuredwidth();
height += verticalspacing;
}width += getpaddingright();
height += getchildat(getchildcount() - 1).getmeasuredheight()
+ getpaddingbottom();
setmeasureddimension(resolvesize(width, widthmeasurespec),
resolvesize(height, heightmeasurespec));
} @override
protected void onlayout(boolean changed, int l, int t, int r, int b)
} @override
protected boolean checklayoutparams(viewgroup.layoutparams p)
@override
protected layoutparams generatedefaultlayoutparams()
@override
public layoutparams generatelayoutparams(attributeset attrs)
@override
protected layoutparams generatelayoutparams(viewgroup.layoutparams p)
public static class layoutparams extends viewgroup.layoutparams
public layoutparams(int w, int h)
}}
首先,分析建構函式。
public cascadelayout(context context, attributeset attrs) finally
}
如果在布局中使用casecadelayout,系統就會呼叫這個建構函式,這個大家都應該知道的吧。這裡不解釋why,有興趣的可以去看原始碼,重點看系統是如何解析xml布局的。
建構函式很簡單,就是通過布局檔案中的屬性,獲取水平距離和垂直距離。
然後,分析自定義layoutparams。
public static class layoutparams extends viewgroup.layoutparams
public layoutparams(int w, int h)
}
除此之外,還需要重寫一些方法,checklayoutparams()、generatedefaultlayoutparams()等,這個方法在不同viewgroup之間往往是相同的。
接下來,分析onmeasure()方法。
@override
protected void onmeasure(int widthmeasurespec, int heightmeasurespec)
width += child.getmeasuredwidth();
height += verticalspacing;
}width += getpaddingright();
height += getchildat(getchildcount() - 1).getmeasuredheight()
+ getpaddingbottom();
// 使用計算所得的寬和高設定整個布局的測量尺寸
setmeasureddimension(resolvesize(width, widthmeasurespec),
resolvesize(height, heightmeasurespec));
}
最後,分析onlayout()方法。
@override
protected void onlayout(boolean changed, int l, int t, int r, int b)
}
邏輯很簡單,用onmeasure()方法計算出的值為引數迴圈呼叫子view的layout()方法。
為子檢視新增自定義屬性
作為示例,下面將新增子檢視重寫垂直間距的方法。
第一步是向attrs.xml中新增乙個新的屬性。
這裡的屬性名是layout_vertical_spacing,因為該屬性名字首是layout_,同時,又不是view固有的屬性,所以該屬性會被新增到layoutparams的屬性表中。在cascadelayout類的建構函式中讀取這個新屬性。
public static class layoutparams extends viewgroup.layoutparams finally
}public layoutparams(int w, int h)
}
那怎麼使用這個屬性呢?so easy!
參考資料
教你搞定Android自定義ViewGroup
我們知道viewgroup就是view的容器類,我們經常用的linearlayout,relativelayout等都是viewgroup的子類,因為viewgroup有很多子view,所以它的整個繪製過程相對於view會複雜一點,但是還是三個步驟measure,layout,draw,我們一次說明...
教你搞定Android自定義ViewGroup
我們知道viewgroup就是view的容器類,我們經常用的linearlayout,relativelayout等都是viewgroup的子類,因為viewgroup有很多子view,所以它的整個繪製過程相對於view會複雜一點,但是還是三個步驟measure,layout,draw,我們一次說明...
自定義 如何自定義協議
何為自定義協議,其實是相對標準協議來說的,這裡主要針對的是應用層協議 常見的標準的應用層協議如http ftp smtp等,如果我們在網路通訊的過程中不去使用這些標準協議,那就需要自定義協議,比如我們常用的rpc框架 dubbo,thrift 分布式快取 redis,memcached 等都是自定義...