4.4 自定義View
自定義View
是一個綜合的技術(shù)體系,它涉及View
的層次結(jié)構(gòu)斟赚、事件分發(fā)機(jī)制和View
的工作原理等技術(shù)細(xì)節(jié)着降。
4.4.1 自定義View的分類
1. 繼承View重寫onDraw方法
這種方法主要用于實現(xiàn)一些不規(guī)則的效果,即這種效果不方便通過布局的組合方式來達(dá)到拗军,往往需要靜態(tài)或者動態(tài)地顯示一些不規(guī)則的圖形鹊碍。很顯然這需要通過繪制的方式來實現(xiàn)厌殉,即重寫
onDraw
方法食绿。采用這種方式需要自己支持wrap_content
侈咕,并且padding
也需要自己處理。
2. 繼承ViewGroup派生特殊的Layout
這種方法主要用于實現(xiàn)自定義的布局器紧,即除了
LinearLayout
耀销,RelativeLayout
,FrameLayout
這幾種系統(tǒng)的布局之外铲汪,我們重新定義一種新布局熊尉,當(dāng)某種效果看起來很像幾種View
組合在一起的時候,可以采用這種方法來實現(xiàn)掌腰。采用這種方式稍微復(fù)雜一些狰住,需要合適地處理ViewGroup
的測量、布局這兩個過程齿梁,并同時處理子元素的測量和布局過程催植。
3. 繼承特定的View(比如TextView)
這種方法比較常見,一般是用于拓展某種已有的
View
的功能勺择、比如TextView
创南、這種方法比較容易實現(xiàn)。這種方法不需要自己支持wrap_content
和padding
等省核。
4. 繼承特定的ViewGroup(比如LinearLayout)
這種方法也比較常見稿辙,當(dāng)某種效果看起來很像幾種
VIew
組合在一起的時候,可以采用這種方法來實現(xiàn)气忠。采用這種方法不需要自己處理ViewGroup
的測量和布局這兩個過程邻储。需要注意這種方法和方法2的區(qū)別,一般來說方法2能實現(xiàn)的效果方法4也都能實現(xiàn)旧噪,兩者的主要差別在于方法2更接近View
的底層吨娜。
4.4.2 自定義VIew須知
本節(jié)將介紹自定義View過程中的一些注意事項,這些問題如果處理不好舌菜,有些會影響View
的正常使用萌壳,而有些則會導(dǎo)致內(nèi)存泄漏等。
1. 讓View
支持wrap_content
這是因為直接繼承
View
或者ViewGroup
的控件日月,如果不在onMeasure
中對wrap_content
做特殊處理袱瓮,那么當(dāng)外界在布局中使用wrap_content
時就無法達(dá)到預(yù)期的效果。
2. 如果有必要爱咬,讓你的View
支持padding
這是因為直接繼承
View
的控件尺借,如果不在draw
方法中處理padding
,那么padding
屬性是無法起作用的精拟。另外燎斩,直接繼承自ViewGroup
的控件需要在onMeasure
和onLayout
中考慮padding
和子元素的margin
對其造成的影響虱歪,不然將導(dǎo)致padding
和子元素的margin
失效。
3. 盡量不要在View
中使用Handler
栅表,沒必要
這是因為
View
內(nèi)部本身就提供了post
系列的方法笋鄙,完全可以替代Handler
的作用,當(dāng)然除非你很明確地要使用Handler
來發(fā)送消息怪瓶。
4. View
中如果有線程或者動畫萧落,需要及時停止,參考View#onDetachedFromWindow
這一條也很好理解如果有線程或者動畫需要停止時洗贰,那么
onDetachedFromWindow
是一個很好的時機(jī)找岖。當(dāng)包含此View
的Activity
退出或者當(dāng)前View
被remove
時,View
的onDetachedFromWindow
方法會被調(diào)用敛滋,和此方法對應(yīng)的是onAttachedToWindow
许布,當(dāng)包含此View
的Activity
啟動時,View
的onAttachedToWindow
方法會被調(diào)用绎晃。同時蜜唾,當(dāng)View
變得不可見時我們也需要停止線程和動畫,如果不及時處理這種問題箕昭,有可能會造成內(nèi)存的泄露灵妨。
5. View
帶有滑動嵌套情形時,需要處理好滑動沖突
如果有滑動沖突的話落竹,那么要合適地處理滑動沖突泌霍,否則將會嚴(yán)重影響
View
的效果。
4.4.3 自定義View的示例
1. 繼承View重寫onDraw()方法
這種方法主要用于實現(xiàn)一些不規(guī)則的效果述召,一般需要重寫onDraw()
方法朱转。采用這種方式需要自己支持wrap_content
,并且padding
也需要自己處理积暖。
為了實現(xiàn)一個規(guī)范的控件藤为,在實現(xiàn)過程中必須考慮到wrap_content
模式以及padding
,同時為了提高便攜性夺刑,還要對外提供自定義屬性缅疟。
public class CircleView extends View {
private int mColor = Color.RED;
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
public CircleView(Context context) {
super(context);
init();
}
public CircleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs,0);
init();
}
public CircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint.setColor(mColor);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
int radius = Math.min(width,height) / 2;
canvas.drawCircle(width / 2,height / 2, radius,mPaint);
}
}
上面的代碼實現(xiàn)了一個具有圓形效果的自定義View
,它會在自己的中心點以寬/高的最小值為直徑繪制一個紅色的實心圓遍愿,它的代碼實現(xiàn)很簡單存淫。