Android 自定義 Drawable

weixin_34007291發表於2017-06-04

Android 中,說到 Drawable,第一個想到的時候?是不是 drawable 裡面的 shape 或者 selector 或者就是那麼一個個圖示?

Drawable 是一個抽象類,描述的是一些可以被畫出來的東西,其實 View 也是畫東西出來嘛,DrawableView 的最明顯區別就是它只能畫東西,沒法接收事件響應互動。

現在有如下的需求:

2244299-57a5f304b34bc8f4.gif
design_drawable.gif

簡單的說就是需要畫三種狀態(選中,未選中,不可選)的背景,三種狀態,這個好說,如果使用 xml 的話,就是上面說的 selector,直接使用物件的話就是 StateListDrawable ,狀態的話,就是state_enabled,state_checked就可以啦。等等,不是三個狀態嗎?不是還有一個 unable 的狀態嗎?這個等下看程式碼吧。

2244299-dbb0f88a55b6545a.png
StateListDrawable.png

接下來,就需要來畫出這三種狀態的 Drawable 了,其實很簡單的,未選中和不可選都是畫一個矩形的邊框就好了,選中也就是在矩形邊框的基礎上再加上左上方一個三角形就好了,至於畫三角形,使用 Path 就可以搞定的。這個就是基本思路了。

2244299-a667454c0f3fcfad.png
orverwrite.png

自定義一個 Drawable,抽象類嘛,必須實現四個方法,draw() 就不用講了,setAlpha() 就是這是設定 alpha 咯,setColorFilter(),就是設定顏色過濾了,getOpacity() 獲取不透明度,這個只能返回指定的半透明、透明、未指定幾種情況。其中兩個set的方法是用來讓我們設定的。綜合起來的話,我們這裡根本就不用關心這個透明度或者 colorfilter 的問題,所以其他三個方法直接空處理或者返回預設值就好了。

接下來差不多就可以擼程式碼了。

    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setColor(mainColor);
    mPaint.setStrokeWidth(6);
    //設定畫虛線,如果之後不再使用虛線,呼叫paint.setPathEffect(null);
    pathPaint = new Paint(mPaint);
    pathPaint.setStyle(Paint.Style.FILL);
    mArrow = new Path();

先定義一個 Paint,還有一個 Path ,用來畫那個三角形。這裡遇到第一個問題,strokeWidth 不能寫死畫素值啊,但是 Drawable 裡面似乎沒有Context 的引用,所以還沒法轉 dp 了,所以只有在初始化 Drawable 的時候傳一個 Context 進來吧!另外,還有一個問題呢,這個 Drawable 的寬高怎麼確定?我們畫的那個三角形肯定是需要跟著高度呈一定的比例咯,這裡就涉及到 onBoundsChange() 的方法了。

@Override
protected void onBoundsChange(Rect bounds) {
    super.onBoundsChange(bounds);
    mRect = bounds;
    int value = (int) (bounds.height() * 0.1f);
    mArrow.reset();
    mArrow.moveTo(0, 0);
    value = (int) (value * 1.5f);
    mArrow.lineTo(value, 0);
    mArrow.lineTo(0, value);
    mArrow.close();
    invalidateSelf();
}

這兩個問題解決了,那麼最後就是在 draw() 的方法中畫出來了。

  switch (currentState) {
        case STATE_CHECKED://選中狀態
            mPaint.setColor(checkedColor);
            pathPaint.setColor(checkedColor);
            canvas.drawRect(mRect, mPaint);
            canvas.drawPath(mArrow, pathPaint);
            break;
        case STATE_UNABLE://不可用狀態 畫虛線
            mPaint.setColor(borderColor);
            PathEffect effects = new DashPathEffect(new float[]{10, 10, 10, 10}, 1);
            mPaint.setPathEffect(effects);
            canvas.drawRect(mRect, mPaint);
            break;
        case STATE_NORMAL://正常狀態
                mPaint.setColor(borderColor);
                canvas.drawRect(mRect, mPaint);
            break;
    }

好了,到這裡最基本的 Drawable 就搞定了,接下來還要搞定這個狀態的改變。

接下來實現一個 StateListDrawable,將相關的狀態和上面的 Drawable
繫結起來就好了,其他的就不用我們操心了。

   addState(new int[]{android.R.attr.state_checked}, new CategoryDrawable(CategoryDrawable.STATE_CHECKED, mColor, showInner));
    //unable 就是這樣定義的
   addState(new int[]{-android.R.attr.state_enabled}, new CategoryDrawable(CategoryDrawable.STATE_UNABLE, mColor, showInner));
   addState(new int[]{}, new CategoryDrawable(CategoryDrawable.STATE_NORMAL, mColor, showInner));

最後,就是給 View 新增我們製作好的 Drawable 了,這裡需要支援CheckableTextView,那麼必須就是 CheckedTextView 了。

下載

相關文章