這篇文章是基于以下兩篇文章的實踐:
1.自定義View - 基礎(chǔ)
2.自定義View - Canvas - 圖形繪制
我們在常用的 UI 布局中,常會用到這樣的圓點:

當(dāng)然我們可以用一張圖片,利用 tintMode 顯示出不同的顏色,從而實現(xiàn)這樣的效果,這一點,我在之前 Material Design 中已經(jīng)講過。
今天,我們利用自定義 View 來實現(xiàn)。
一、第一步:考慮需要的屬性
對于這個圓點,在項目需求中,通常只是一個指示器,因此需要設(shè)置的屬性并不多。大致有兩個:
- 顏色
- 大小 ,這里我們將大小參數(shù)定為半徑
設(shè)定的屬性需要寫在 styles.xml 中(為了區(qū)分,你也可以新建一個 xml 文件用于存儲自定義 View 的屬性聲明)
<declare-styleable name="PointView">
<attr name="radio" format="dimension"></attr>
<attr name="pointColor" format="color"></attr>
</declare-styleable>
說明:
- 聲明屬性要用 declare-styleable 標(biāo)簽包裹,其中 name 屬性為 新建類的類名。
- attr 中為各個屬性的說明,包括名稱(name),類型(format)
二、第二步:新建類,并獲取屬性
這里我自定義的 View 為 PointView。首先繼承 View 并重寫構(gòu)造器。在構(gòu)造器中,我們需要初始化畫筆,并獲取 xml 中自定義的屬性。
public class PointView extends View {
private float mRadio = 3;
private int mPointColor = Color.BLACK;
private Paint mPaint;
public PointView(Context context) {
super(context);
initPaint();
}
public PointView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
TypedArray ta = null;
try {
ta = context.obtainStyledAttributes(attrs, R.styleable.PointView);
mRadio = ta.getDimension(R.styleable.PointView_radio, dp2px(3));
mPointColor = ta.getColor(R.styleable.PointView_pointColor, Color.YELLOW);
} finally {
if (ta != null) {
ta.recycle();
}
}
initPaint();
}
private void initPaint(){
if (mPaint == null) {
mPaint = new Paint();
mPaint.setColor(mPointColor);
mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
}
}
}
三、第三步:繪制圖形
這里的圓點圖形比較簡單,根據(jù)設(shè)置的半徑 mRadio 直接繪制圓形
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(mRadio,mRadio,mRadio,mPaint);
}
四、第四步:測量視圖
我們需要重寫 onMeasure 方法,在這里根據(jù) Mode 獲取 width 和 height 的準(zhǔn)確值,并通過 setMeasuredDimension( resultWidth,resultHeight ) 使測量值生效。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int resultWidth = 0;
int resultHeight = 0;
int specWidthMode = MeasureSpec.getMode(widthMeasureSpec);
int specWidthSize = MeasureSpec.getSize(widthMeasureSpec);
int specHeightMode = MeasureSpec.getMode(heightMeasureSpec);
int specHeightSize = MeasureSpec.getSize(heightMeasureSpec);
if (specWidthMode == MeasureSpec.AT_MOST) {//wrap_content
resultWidth = (int) (mRadio * 2);
}else{//match_parent
resultWidth = specWidthSize;
}
if (specHeightMode == MeasureSpec.AT_MOST) {//wrap_content
resultHeight = (int) (mRadio * 2);
}else{//match_parent
resultHeight = specHeightSize;
}
setMeasuredDimension(resultWidth,resultHeight);
}
五、暴露部分方法
PointView 有兩個屬性:顏色和大小。上面的代碼只能保證在布局文件中聲明的 PointView 顯示正常,而代碼中初始化的 PointView 需要我們暴露出設(shè)置顏色和大小的 set 方法。
這里的 set 方法除了像普通實體類做了賦值操作之外,還必須調(diào)用 invalidate 方法,通知視圖重新繪制,否則屬性的變化并不能真正體現(xiàn)在視圖上。
public void setColor(int color){
initPaint();
mPaint.setColor(color);
invalidate();
}
public void setRadio(int radio){
if (radio > 0) {
mRadio = dp2px(radio);
}
requestLayout();
}
結(jié)束語:其實項目里許多簡單的小東西可以通過代碼實現(xiàn),比如返回的小箭頭、關(guān)閉彈窗的 X 等等。也許現(xiàn)在還不能做出什么大東西,但是小玩意兒也會讓人覺得很有成就感啊。