自定義View(1)--QQ運動計步器

無圖無真像:

弧度為270

弧度為360

在我們畫一個圖形之前,我們需要思考,我們先要解析他的步驟,然后根據(jù)步驟一步一步來完成。
思考:我們繪制View的一般基本流程為:

1.根據(jù)我們的需要,是需要new出來還是在布局文件里面添加,從而得到相應(yīng)的構(gòu)造方法。
2.我們需要什么屬性?
3.我們是否需要測量?
4.如果是viewGroup我們需要是否要通過onLayout方法來擺放childView的位置?
5.調(diào)用onDraw()的時候先畫什么,在畫什么?然后調(diào)用相應(yīng)的API完成相應(yīng)的步驟即可。
6.性能優(yōu)化。

我們從這個基本流程出發(fā)

  1. 我們希望這個view能夠new出來,也能夠在布局文件里面添加使用,所以構(gòu)造函數(shù)如下:
 public QQSportStepView(Context context) {
        this(context, null);
    }

    public QQSportStepView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public QQSportStepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        
    }

2.我們需要什么屬性?在這個view里面,我想實現(xiàn)的是希望其他人可以自己設(shè)置文字顏色,線條寬度,弧度大小等等,所以我們在att里面定義:
<declare-styleable name="QQSportStep">
<attr name="bottomColor" format="color" />
<attr name="topColor" format="color" />
<attr name="textColor" format="color" />
<attr name="maxStepNum" format="integer" />
<attr name="currentStepNum" format="integer" />
<attr name="textSize" format="dimension" />
<attr name="circleRadio" format="dimension" />
<attr name="circleStrokeWidth" format="dimension" />
<attr name="arcAngle" format="float" />
</declare-styleable>

并且在初始化的時候解析出來:


    private int mBottomColor;//底層圓的顏色
    private int mTopColor;//頂層圓的顏色
    private int mTextColor;//文字顏色

    private int mMaxStepNum;//最大步數(shù)
    private int mCurrentStepNum;//當前步數(shù)

    private int mTextSize;//文字大小
    private int mCircleRadio;//圓的半徑
    private int mCircleStrokeWidth;//圓線條的寬度

    private float mArcAngle;//弧度大小
    private float mStartAngle;//通過幅度計算出開始的角度位置

    private void initAttrs(Context context, AttributeSet attrs) {
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.QQSportStep);
        mBottomColor = ta.getColor(R.styleable.QQSportStep_bottomColor, Color.BLUE);
        mTopColor = ta.getColor(R.styleable.QQSportStep_topColor, Color.RED);
        mTextColor = ta.getColor(R.styleable.QQSportStep_textColor, Color.RED);
        mMaxStepNum = ta.getInteger(R.styleable.QQSportStep_maxStepNum, 0);
        mCurrentStepNum = ta.getInteger(R.styleable.QQSportStep_currentStepNum, 0);
        mTextSize = ta.getDimensionPixelSize(R.styleable.QQSportStep_textSize, DisplayUtil.sp2px(context, 17));
        mCircleRadio = ta.getDimensionPixelSize(R.styleable.QQSportStep_circleRadio, DisplayUtil.dip2px(context, 100));
        mArcAngle = ta.getFloat(R.styleable.QQSportStep_arcAngle, 270.0f);
        if (mArcAngle > 360.0f || mArcAngle < -360.0f)
            mArcAngle = 360.0f;

        mCircleStrokeWidth = ta.getDimensionPixelOffset(R.styleable.QQSportStep_circleStrokeWidth, DisplayUtil.dip2px(context, 5));
        ta.recycle();
    }

3.我們是否需要測量?因為這個view使用的時候一般是寫好的固定的大小,所以不必要測量,所以我就沒重寫onMeasure方法
4.因為這是個view,沒有childView,所以不用重寫onLayout方法
5.解析步驟:我將這個view解析為三個步驟

1.畫底部的圓弧
2.畫頂部的圓弧
3.畫文字

然后重寫onDraw
畫底部的圓弧

       if (mRectF == null) {
            int centerX = getWidth() / 2;
            int centerY = getHeight() / 2;
            mRectF = new RectF(centerX - mCircleRadio, centerY   mCircleRadio, centerX + mCircleRadio, centerY + mCircleRadio);
        }

        //1.畫底部的圓
        float gapAngle = mArcAngle - 180.0f;
        if (mArcAngle >= 0) {//大于0表示在上方
            mStartAngle = 180.0f - gapAngle / 2;
        } else {//小于0表示在下方
            mStartAngle = -gapAngle / 2;
        }
        canvas.drawArc(mRectF, mStartAngle, mArcAngle, false, mBottomPaint);

畫頂部的圓弧

      //2.畫頂部的圓弧
        float currentAngle = (float) mCurrentStepNum / mMaxStepNum * mArcAngle;
        canvas.drawArc(mRectF, mStartAngle, currentAngle, false, mTopPaint);

        if (mMaxStepNum <= 0)
            return;

畫文字

String step = String.valueOf(mCurrentStepNum);
        int dx = (getWidth() - DisplayUtil.getTextWidth(step, mTextPaint)) / 2;
        int baseLine = getHeight() / 2 + DisplayUtil.getTextBaseLine(mTextPaint);
        // 繪制步數(shù)文字
        canvas.drawText(step, dx, baseLine, mTextPaint);

5.性能優(yōu)化(這一步很重要,請不要忽略它)
我們現(xiàn)在已經(jīng)可以在手機屏幕上呈現(xiàn)一個靜態(tài)的view了,但是我們要讓他動起來,這里有兩種方式,一種是在view內(nèi)部寫實現(xiàn),一種是在view外部實現(xiàn),為了降低耦合,我們最好是將動畫實現(xiàn)的效果從外部實現(xiàn)。在動畫的時候,一定是View不停的調(diào)用onDraw方法重繪,所以我們將重復的操作提取出來,一次就行了,不用每一次都在執(zhí)行,比如:畫筆初始化、一些計算等,這樣能夠降低gpu的消耗,當然如果實現(xiàn)的效果比較復雜的畫,可以使用雙緩沖的繪圖方式來犧牲內(nèi)存來換取時間,這樣gpu就不會起伏太大。

最后我們在外部使用ValueAnimator來實現(xiàn)動畫:

        int maxStepNun = 100000;
        qqSportStepView.setMaxStepNum(maxStepNun);
        ValueAnimator valueAnimator = new ValueAnimator();
        valueAnimator.setDuration(2000);
        valueAnimator.setIntValues(0, maxStepNun);
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                qqSportStepView.setCurrentStepNum(value);
            }
        });
        valueAnimator.start();

總結(jié):我們在做自定義一個view的時候,一定要先理清楚思路,我們要實現(xiàn)什么效果?我們要達到什么目的?然后在解析相應(yīng)的步驟,最后調(diào)用相關(guān)的api一步步完成即可。
參考:自定義View - 仿QQ運動步數(shù)進度效果

本文源碼下載地址:
https://github.com/ChinaZeng/CustomView

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內(nèi)容