一個規範的自訂View——Android開發藝術探索筆記
一個不規範的自訂View
這個自訂的View很簡單,就是畫一個圓,實現一個圓形效果的自訂View。
先看一個不規範的自訂View是怎麼做的
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, AttributeSet attrs) { this(context, attrs, 0); } public CircleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mPaint.setColor(mColor); } @Override protected void onDraw(Canvas canvas) { int width = getWidth(); int height = getHeight(); int radius = Math.min(width, height) / 2; canvas.drawCircle(width / 2, height / 2, radius, mPaint); }}
對應的xml
這樣雖然也能畫出一個圓來,但是這並不是一個規範的自訂View,主要存在以下問題:
android:padding屬性是不能使用的 使用wrap_content就相當於使用match_partent一個規範的自訂View
為瞭解決以上問題需要重寫View的onMeasure和onDraw方法。
完整代碼如下:
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, AttributeSet attrs) { this(context, attrs, 0); } public CircleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleView); mColor = a.getColor(R.styleable.CircleView_circle_color, Color.RED); a.recycle(); init(); } private void init() { mPaint.setColor(mColor); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(200, 200); } else if (widthSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(200, heightSpecSize); } else if (heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(widthSpecSize, 200); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); final int paddingLeft = getPaddingLeft(); final int paddingRight = getPaddingRight(); final int paddingTop = getPaddingTop(); final int paddingBottom = getPaddingBottom(); int width = getWidth() - paddingLeft - paddingRight; int height = getHeight() - paddingTop - paddingBottom; int radius = Math.min(width, height) / 2; canvas.drawCircle(paddingLeft + width / 2, paddingTop + height / 2, radius, mPaint); }}
添加自訂屬性
在values檔案夾下添加attrs.xml
自訂的屬性集合CircleView,在這個屬性集合裡只定義了一個格式為color的屬性circle_color。
在View的建構函式中解析自訂的屬性
public CircleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleView); mColor = a.getColor(R.styleable.CircleView_circle_color, Color.RED); a.recycle(); init(); }
在布局檔案中使用自訂屬性
在使用自訂的屬性時,要在schemas聲明:xmlns:app=”http://schemas.android.com/apk/res-auto”,使用時與普通屬性類似,app:circle_color=”@color/light_green” 。
自訂View須知自訂的View中margin屬性可以使用,因為它是由父容器控制的 直接繼承View或ViewGroup的需要自己處理wrap_content View要在onDraw方法中要處理padding,而ViewGroup要在onMeasure和onLayout中處理padding和margin View中的post方法可以取代handler 在View的onDetachedFromWindow中停止動畫,防止記憶體泄露 有滑動嵌套情形時,注意滑動衝突處理
想要自訂出漂亮的View並不容易,只有多讀,多寫,多測,才能更好的掌握。自己造一個輪子,然後再對比成熟的輪子去找差距和不足。
歡迎轉載,轉載請註明原文連結http://blog.csdn.net/l664675249/article/details/50787973