Android自定義View系列
- Android自定義View之Paint繪製文字和線
- Android自定義View之影像的色彩處理
- Android自定義View之Canvas
- Android自定義View之圖片外形特效——輕鬆實現圓角和圓形圖片
- Android自定義View之雙緩衝機制和SurfaceView
- Android自定義View之invalidate方法和postInvalidate方法
- Android自定義View之Window、ViewRootImpl和View的三大流程
- Android自定義View之事件分發機制總結
- Android自定義View之requestLayout方法和invalidate方法
自定義View的分類
- 繼承View重寫onDraw方法
主要用於實線不規則的效果,即這種效果不方便通過佈局的組合方式來實現。相當於就是得自己“畫”了。採用這種方式需要自己支援wrap_content,padding也需要自己處理
- 繼承ViewGroup派生特殊的Layout
主要用於實現自定義的佈局,看起來很像幾種View組合在一起的時候,可以使用這種方式。這種方式需要合適地處理ViewGroup的測量和佈局,並同時處理子元素的測量和佈局過程。比如自定義一個自動換行的LinerLayout等。
- 繼承特定的View,比如TextView
這種方法主要是用於擴充套件某種已有的View,增加一些特定的功能。這種方法比較簡單,也不需要自己支援wrap_content和padding。
- 繼承特定的ViewGroup,比如LinearLayout
這種方式也比較常見,和上面的第2種方法比較類似,第2種方法更佳接近View的底層。
自定義View有多種方式,需要根據實際需要選擇一種簡單低成本的方式來實現
自定義View需要注意的地方
- 讓View支援wrap_content
直接繼承View和ViewGroup的控制元件需要在onMeasure方法中處理wrap_content的方法。處理方法是在wrap_content的情況下設定一個固定的尺寸
//處理wrap_content的套路
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//處理WAP_CONTENT
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(200,200);
}else if (widthSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(200, heightSize);
}else if (heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSize, 200);
}
}
複製程式碼
- 讓View支援padding
直接繼承View的控制元件需要在onDraw方法中處理padding,否則使用者設定padding屬性就不會起作用。直接繼承ViewGroup的控制元件需要在onMeasure和onLayout中考慮padding和子元素的margin對其造成的影響,不然將導致padding和子元素的margin失效。
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
//獲取padding,然後根據實際情況處理就好
mPaddingLeft = getPaddingLeft();
mPaddingRight = getPaddingRight();
mPaddingTop = getPaddingTop();
mPaddingBottom = getPaddingBottom();
mWidth = getWidth() - mPaddingLeft - mPaddingRight;
mHeight = getHeight() - mPaddingTop - mPaddingBottom;
}
複製程式碼
- 儘量不要在View中使用Handler
View中已經提供了post系列方法,完全可以替代Handler的作用。
@UiThread
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
...
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
public boolean postDelayed(Runnable action, long delayMillis) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.postDelayed(action, delayMillis);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().postDelayed(action, delayMillis);
return true;
}
...
}
複製程式碼
- View中如果有執行緒或者動畫,需要及時停止
在View的onDetachedFromWindow方法可以停止執行緒和動畫,因為當View被remove或是包含此View的Activity退出時,就會呼叫View的onDetachedFromWindow方法。如果不處理的話很可能會導致記憶體洩漏
-
View帶有滑動巢狀時,需要處理好滑動衝突問題
-
在View的onDraw方法中不要建立太多的臨時物件,也就是new出來的物件。因為onDraw方法會被頻繁呼叫,如果有大量的臨時物件,就會引起記憶體抖動,影響View的效果
今天你進步了嘛?歡迎關注我的微信公眾號,和我一起每天進步一點點!