[Android] 你可能要用到的自定義View——碼表

自定義View——IndicatorView,源碼地址:ttdevs github

0x00 main

進步、創新都是在不斷變化的需求中誕生的。
-- By ttdevs

這不,新的需求又來了。廢話不多說,先上設計稿:

boohee design

簡單分析上圖包括三部分:最上面的類Progressbar,中間兩個指示盤和最下面的指示盤。第一個類Progressbar我們項目之前有實現過,但是和這個需求有一些差異,因此決定重新實現一遍。另外兩個圓形指示盤本想通過在一張背景圖片上放一張指針圖,控制指針圖的旋轉來實現,但是考慮到這樣不夠靈活,因此也決定自己來畫。So,我們接下來實現這三個View。

正式開始之前,先看看我們最終效果圖(當然,指針是可以動的):

result one
result two

0x01 分析

  • LineIndicator (第一個,類Progressbar)

    主要包括三部分:左側的提示和內容,右側的提示和內容,中間的類Progressbar。兩側的文字比較容易處理,掌握了文字的基本繪制,畫起來是很容易的。中間的類Progressbar由于打算自己畫,所以不會直接貼個Progressbar在上面。我的思路是畫一條長的直線作為背景,中間的指示也是直線,比背景直線粗,直線的Paint設置 StrokeCapPaint.Cap.ROUNDpaint.setStrokeCap(Paint.Cap.ROUND);,這樣就可以有兩頭半圓的效果;中間的指示數字直接畫在粗的指示直線上面即可。這里可能遇到的問題有下面兩個:

    • StrokeCap 設置 為 Paint.Cap.ROUND時的位置關系

      StrokeCap 設置 為 Paint.Cap.ROUND,直線兩側的半圓是不算在直線的長度里的。簡單來說,如果view的長度和我畫的直線長度一致,那么這個直線就是矩形而不是期待的兩側都是半圓的橢圓。

    • 當進度為0或者為100%時的展示

      如果我們背景直線和指示直線起點終點位置一致,那么最終效果就是當進度為0或者100%的時候,指示直線是顯示不全的,因此我們需要對這兩個位置進行矯正。我的矯正方法比較簡單,在0~x和y~100%進度的時候分別顯示x和y(本以為自己的思路很屌,后來發現別人也是這么干的)。

  • CircleIndicator (中間的多種顏色環)

    這個View我將其分解為四部分,從上層到下層:中間指針,刻度環,顯示的內容,圓環背景和外層指示文字。每一部分再做分解:

    • 中間指針

      這個又分解為六個部分:最下層大圓,兩個圓形半圓,兩個三角形和上層小圓。

    • 刻度環

      由于是圓環,所以必須畫弧線了。但是這個弧線又有點特殊,兩側帶圓角,中間圓環直角,這個沒想到啥好辦法,用了很一般的思路:先用 Paint.Cap.ROUND 畫兩側的弧,然后 Paint.Style.STROKE 畫中間的部分。

    • 顯示的內容

      這個就比較簡單了,主要在于計算文字的顯示位置。

    • 圓環背景和外層文字

      最簡單的一個圓,加一個以圓為路徑畫的文字。

最后我們畫的順序正好與上面所述順序相反。
  • ProgressIndicator (最下面的兩種顏色環)

    這個相較 CircleIndicator 就簡單了一些。在畫圓環的時候,我們只需要先畫一個灰色背景,然后再畫一個綠色圓弧即可。

0x02 實現

View的繪制我們應該都比較熟悉,主要有下面三個過程: onMeasure(測量)、onLayout(布局)、onDraw(繪制)。針對上述三個View:LineIndicator,我們根據實際的內容來計算View的高度,寬度用戶設定;CircleIndicator和ProgressIndicator的寬度用戶設定,高度自定計算與寬度相同。onLayout我們不需要。最后的根據實際的展示內容來繪制。

另外,由于CircleIndicator和ProgressIndicator和相似度高,很多代碼可以拿來重用,因此,我寫了一個基類來完成公共的部分,特殊部分每個 子類自己完成。最后再子類地onDraw方法中按照順序調用即可。

由于主要是計算各種坐標位置,代碼還是不少的,這里就不貼代碼。最后的實現請移步我的 github。三個View的代碼已經整理完LineIndicator,其它的兩個還需要點時間(2016-06-19)。

0x03 知識點

  • 自定義View的思路

    • 創建類,繼承自View或者ViewGroup或者其它ViewGroup
    • 定義View的可配置參數,如果你需要的話
    • 實現View的具體邏輯

    這個太粗略了,各位可以參考具體源碼。

  • 文字位置的計算

    如果你沒有自己畫過文字,那你肯定不可能一下子明白文字的畫法,或多或少的出現偏移。這里直接給大家推薦一篇講的非常詳細的文章:http://blog.csdn.net/aigestudio/article/details/41447349

  • 位置、半徑、弧長等的計算

    這里會涉及到簡單的三角函數和圓的周長弧度的計算,可能要說的就是三角函數的參數是弧度制度。

  • ObjectAnimator

    /**
     * 設置內容的顏色值(非resource的id)
     *
     * @param contentColor 內容的顏色值
     * @param unitColor    單位的顏色值
     */
    public void setContentColor(int contentColor, int unitColor) {
        mContentColor = contentColor;
        mUnitColor = unitColor;
    }
    
    /**
     * 設置進度
     *
     * @param indicator 進度值
     */
    public void setIndicator(float indicator) {
        if (indicator <= mStartIndicator) {
            mIndicator = mStartIndicator;
        } else if (indicator > mEndIndicator) {
            mIndicator = mEndIndicator;
        } else {
            mIndicator = indicator;
        }
        postInvalidate();
    }
    
    /**
     * 獲取進度
     *
     * @return 當前進度值
     */
    public float getIndicator() {
        return mStartIndicator;
    }
    
    public void animateIndicator(float indicator) {
        Interpolator interpolator = new AnticipateOvershootInterpolator(1.8f);
        ObjectAnimator animation = ObjectAnimator.ofFloat(this, "indicator", indicator);
        animation.setDuration(2000);
        animation.setInterpolator(interpolator);
        animation.start();
    }
    
  • 處理屏幕的旋轉

    由于時間比較緊,這個我還沒做。主要在View的以下兩個方法中實現:

    
    @Override
    protected Parcelable onSaveInstanceState() {
        return super.onSaveInstanceState();
    }
    
    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        super.onRestoreInstanceState(state);
    }
    
    

最后歡迎各位吐槽并提供更好的建議~~

Create by ttdevs
Create by ttdevs
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容