Android自定義View-漸變的溫度指示器

廢話少說并扇,先上圖


ED9EF9312D01ADABDFADF481CF32A26C.jpg

1、自定義View的分類

image.png

2穷蛹、自定義View要點

  1. View需要支持wrap_content
  2. View需要支持padding
  3. 盡量不要再View中使用Handler,View已經有post系列方法
  4. View如果有線程或者動畫旺坠,需要及時停止(onDetachedFromWindow會在View被remove時調用)——避免內存泄露
  5. View如果有滑動嵌套情形扮超,需要處理好滑動沖突

3、直接繼承自View的實現(xiàn)步驟和方法

  1. 重寫onDraw璧疗,在onDraw中處理padding
  2. 重寫onMeasure馁龟,額外處理wrap_content的情況
  3. 設定自定義屬性attrs(屬性相關xml文件,以及在onDraw中進行處理)

4却音、實現(xiàn)效果圖步驟

  1. 重寫onMeasure矢炼,額外處理wrap_content的情況
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        mHeight = mDefaultTextSize + mDefaultTempHeight + mDefaultIndicatorHeight + textSpace;

        if (widthSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(mWidth, mHeight);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(mWidth, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize, mHeight);
        }
    }
  1. 重寫onDraw句灌,在onDraw中處理padding胰锌,并畫出溫度計及指針
@Override
protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        final int paddingLeft = getPaddingLeft();
        final int paddingTop = getPaddingTop();
        final int paddingRight = getPaddingRight();
        final int paddingBottom = getPaddingBottom();

        //確定圓角矩形的范圍,在TmepView的最底部,top位置為總高度-圓角矩形的高度
        rectProgressBg = new RectF();
        rectProgressBg.left = 0 + paddingLeft;
        rectProgressBg.top = mHeight - mDefaultTempHeight;
        rectProgressBg.right = mWidth - paddingRight;
        rectProgressBg.bottom = mHeight - paddingBottom;

        shader = new LinearGradient(0, mHeight - mDefaultTempHeight, mWidth, mHeight, SECTION_COLORS, null, Shader.TileMode.MIRROR);
        mPaint.setShader(shader);
        //繪制圓角矩形 mDefaultTempHeight / 2確定圓角的圓心位置
        canvas.drawRoundRect(rectProgressBg, mDefaultTempHeight / 2, mDefaultTempHeight / 2, mPaint);

        //當前位置占比
        selction = currentCount / maxCount;
        //繪制指針 指針的位置在當前溫度的位置 也就是三角形的頂點落在當前溫度的位置
        //定義三角形的左邊點的坐標 x= tempView的寬度*當前位置占比-三角形的寬度/2  y=tempView的高度-圓角矩形的高度
        indexPath.moveTo(mWidth * selction - mDefaultIndicatorWidth / 2, mHeight - mDefaultTempHeight);
        //定義三角形的右邊點的坐標 = tempView的寬度*當前位置占比+三角形的寬度/2  y=tempView的高度-圓角矩形的高度
        indexPath.lineTo(mWidth * selction + mDefaultIndicatorWidth / 2, mHeight - mDefaultTempHeight);
        //定義三角形的左邊點的坐標 x= tempView的寬度*當前位置占比  y=tempView的高度-圓角矩形的高度-三角形的高度
        indexPath.lineTo(mWidth * selction, mHeight - mDefaultTempHeight - mDefaultIndicatorHeight + paddingTop);
        indexPath.close();
        indexPaint.setShader(shader);
        canvas.drawPath(indexPath, indexPaint);

        //繪制文本
        String text = (int) currentCount + "°c";
        //確定文本的位置 x=tempViwe的寬度*當前位置占比 y=tempView的高度-圓角矩形的高度-三角形的高度-文本的間隙
        canvas.drawText(text, mWidth * selction, mHeight - mDefaultTempHeight - mDefaultIndicatorHeight - textSpace, textPaint);
    }

ps:繪制三角形指針,由于位置會變 所以要確定繪制的位置如圖


image

代碼還算比較好理解酬土,詳細代碼如下:

package androidtest.project.com.customview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Shader;
import android.support.annotation.Nullable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by liuboyu on 2018/10/15.
 */

public class TemperatureView extends View {

    private Context mContext;

    /**
     * 分段顏色
     */
    private static final int[] SECTION_COLORS = {Color.GREEN, Color.YELLOW, Color.RED};

    /**
     * 默認寬度
     */
    private int mWidth = 1000;

    /**
     * 默認高度
     */
    private int mHeight = 200;

    /**
     * 設置溫度的最大范圍
     */
    private float maxCount = 100f;

    /**
     * 設置當前溫度
     */
    private float currentCount = 50f;

    /**
     * 當前刻度位置
     */
    private float selction;

    /**
     * 主畫筆格带,畫刻度尺
     */
    private Paint mPaint;

    /**
     * 文字畫筆
     */
    private Paint textPaint;

    /**
     * 當前刻度指針
     */
    private Path indexPath;
    private Paint indexPaint;

    /**
     * 畫圓柱
     */
    private RectF rectProgressBg;
    private LinearGradient shader;

    /**
     * 指針的寬高
     */
    private int mDefaultIndicatorWidth = dipToPx(10);
    private int mDefaultIndicatorHeight = dipToPx(8);
    /**
     * 圓角矩形的高度
     */
    private int mDefaultTempHeight = dipToPx(20);
    /**
     * 默認字體大小
     */
    private int mDefaultTextSize = 30;
    private int textSpace = dipToPx(5);


    public TemperatureView(Context context) {
        super(context);
        init(context);
    }

    public TemperatureView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

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

    /**
     * 初始化各種畫筆
     *
     * @param context
     */
    private void init(Context context) {
        this.mContext = context;
        //圓角矩形paint
        mPaint = new Paint();
        //防止邊緣的鋸齒
        mPaint.setAntiAlias(true);

        //文本paint
        textPaint = new TextPaint();
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(mDefaultTextSize);
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setColor(mContext.getResources().getColor(R.color.colorAccent));

        //三角形指針paint
        indexPath = new Path();
        indexPaint = new Paint();
        indexPaint.setAntiAlias(true);
        indexPaint.setStyle(Paint.Style.FILL);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        mHeight = mDefaultTextSize + mDefaultTempHeight + mDefaultIndicatorHeight + textSpace;

        if (widthSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(mWidth, mHeight);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(mWidth, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize, mHeight);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        final int paddingLeft = getPaddingLeft();
        final int paddingTop = getPaddingTop();
        final int paddingRight = getPaddingRight();
        final int paddingBottom = getPaddingBottom();

        //確定圓角矩形的范圍,在TmepView的最底部,top位置為總高度-圓角矩形的高度
        rectProgressBg = new RectF();
        rectProgressBg.left = 0 + paddingLeft;
        rectProgressBg.top = mHeight - mDefaultTempHeight;
        rectProgressBg.right = mWidth - paddingRight;
        rectProgressBg.bottom = mHeight - paddingBottom;

        shader = new LinearGradient(0, mHeight - mDefaultTempHeight, mWidth, mHeight, SECTION_COLORS, null, Shader.TileMode.MIRROR);
        mPaint.setShader(shader);
        //繪制圓角矩形 mDefaultTempHeight / 2確定圓角的圓心位置
        canvas.drawRoundRect(rectProgressBg, mDefaultTempHeight / 2, mDefaultTempHeight / 2, mPaint);

        //當前位置占比
        selction = currentCount / maxCount;
        //繪制指針 指針的位置在當前溫度的位置 也就是三角形的頂點落在當前溫度的位置
        //定義三角形的左邊點的坐標 x= tempView的寬度*當前位置占比-三角形的寬度/2  y=tempView的高度-圓角矩形的高度
        indexPath.moveTo(mWidth * selction - mDefaultIndicatorWidth / 2, mHeight - mDefaultTempHeight);
        //定義三角形的右邊點的坐標 = tempView的寬度*當前位置占比+三角形的寬度/2  y=tempView的高度-圓角矩形的高度
        indexPath.lineTo(mWidth * selction + mDefaultIndicatorWidth / 2, mHeight - mDefaultTempHeight);
        //定義三角形的左邊點的坐標 x= tempView的寬度*當前位置占比  y=tempView的高度-圓角矩形的高度-三角形的高度
        indexPath.lineTo(mWidth * selction, mHeight - mDefaultTempHeight - mDefaultIndicatorHeight + paddingTop);
        indexPath.close();
        indexPaint.setShader(shader);
        canvas.drawPath(indexPath, indexPaint);

        //繪制文本
        String text = (int) currentCount + "°c";
        //確定文本的位置 x=tempViwe的寬度*當前位置占比 y=tempView的高度-圓角矩形的高度-三角形的高度-文本的間隙
        canvas.drawText(text, mWidth * selction, mHeight - mDefaultTempHeight - mDefaultIndicatorHeight - textSpace, textPaint);
    }


    /**
     * 單位轉換
     *
     * @param dip
     * @return
     */
    private int dipToPx(int dip) {
        float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (dip * scale + 0.5f * (dip >= 0 ? 1 : -1));
    }


    /***
     * 設置最大的溫度值
     * @param maxCount
     */
    public void setMaxCount(float maxCount) {
        this.maxCount = maxCount;
    }

    /***
     * 設置當前的溫度
     * @param currentCount
     */
    public void setCurrentCount(float currentCount) {
        if (currentCount > maxCount) {
            this.currentCount = maxCount - 5;
        } else if (currentCount < 0f) {
            currentCount = 0f + 5;
        } else {
            this.currentCount = currentCount;
        }
        invalidate();
    }

    /**
     * 設置溫度指針的大小
     *
     * @param width
     * @param height
     */
    public void setIndicatorSize(int width, int height) {
        this.mDefaultIndicatorWidth = width;
        this.mDefaultIndicatorHeight = height;
    }

    /**
     * 設置溫度計厚度
     *
     * @param height
     */
    public void setTempHeight(int height) {
        this.mDefaultTempHeight = height;
    }

    /**
     * 設置文字大小
     *
     * @param textSize
     */
    public void setTextSize(int textSize) {
        this.mDefaultTextSize = textSize;
    }

    /**
     * 獲取溫度計最大刻度
     *
     * @return
     */
    public float getMaxCount() {
        return maxCount;
    }

    /**
     * 獲取當前刻度
     *
     * @return
     */
    public float getCurrentCount() {
        return currentCount;
    }
}

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末凉袱,一起剝皮案震驚了整個濱河市侦铜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌涤躲,老刑警劉巖贡未,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俊卤,死亡現(xiàn)場離奇詭異,居然都是意外死亡消恍,警方通過查閱死者的電腦和手機狠怨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來恰矩,“玉大人茵汰,你說我怎么就攤上這事±覆颍” “怎么了豆胸?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長灵奖。 經常有香客問我,道長骡尽,這世上最難降的妖魔是什么擅编? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮谭贪,結果婚禮上锦担,老公的妹妹穿的比我還像新娘。我一直安慰自己套媚,他們只是感情好痘煤,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宙橱,像睡著了一般蘸拔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宝冕,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天邓萨,我揣著相機與錄音缔恳,去河邊找鬼。 笑死歉甚,一個胖子當著我的面吹牛,可吹牛的內容都是我干的赖钞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼弓千,長吁一口氣:“原來是場噩夢啊……” “哼献起!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤总寒,失蹤者是張志新(化名)和其女友劉穎理肺,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體年枕,經...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡乎完,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年树姨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帽揪。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡转晰,死狀恐怖,靈堂內的尸體忽然破棺而出蔗崎,到底是詐尸還是另有隱情侠坎,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布他嫡,位于F島的核電站,受9級特大地震影響钢属,放射性物質發(fā)生泄漏淆党。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一山孔、第九天 我趴在偏房一處隱蔽的房頂上張望荷憋。 院中可真熱鬧,春花似錦勒庄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铐尚。三九已至,卻和暖如春塑径,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背匆骗。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工誉简, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瓮钥。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像桨武,于是被迫代替她去往敵國和親锈津。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內容