仿汽車速度儀表盤(pán)

問(wèn)題

  • 如何線性顏色變化?
  Shader mShader = new LinearGradient(pointX - raduis, pointY, pointX + raduis, pointY,
                new int[]{0xFF445EED, 0xFF072AE9, 0xFF0625CE}, null, Shader.TileMode.CLAMP);

完整的自定義View

public class SpeedControlView extends View implements Runnable {
    //畫(huà)筆
    private Paint mPaint, textPaint, speedAreaPaint;
    private Context mContext;
    //屏幕寬高
    private int screenWidth, screenHeight;
    //儀表盤(pán)圓的半徑
    private float raduis, sRaduis;
    //圓心
    private int pointX, pointY;
    //文字的偏移量
    private float textScale;
    //速度指針變化的位置
    private float linePointerX, linePointerY;
    //速度
    private int speed;
    //速度范圍的2個(gè)扇形外切矩形
    private RectF speedRectF, speedRectFInner;
    //速度控制模式  1 加速  2 減速  3 手剎
    private int type;

    // 速度文字 繪制的XY坐標(biāo)
    private int baseX, baseY;

    //屏幕密度
    private float mDensityDpi;

    //設(shè)置速度控制模式
    public void setType(int type) {
        this.type = type;
    }

    //開(kāi)始重繪
    private boolean start = true;

    public void setStart(boolean start) {
        this.start = start;
    }

    // 設(shè)置速度 并重繪視圖
    public void setSpeed(int speed) {
        this.speed = speed;
        if (speed > 0) {
            postInvalidate();
        }
    }


    public SpeedControlView(Context context) {
        this(context, null);
    }

    public SpeedControlView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SpeedControlView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;

        //獲取屏幕寬高
//        screenWidth = ((Activity) context).getWindowManager().getDefaultDisplay().getWidth();
//        screenHeight = ((Activity) context).getWindowManager().getDefaultDisplay().getHeight();

        //獲取屏幕寬高 和 屏幕密度dpi
        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
        screenWidth = displayMetrics.widthPixels;
        screenHeight = displayMetrics.heightPixels;
        mDensityDpi = displayMetrics.densityDpi / 320;  //320為我的測(cè)試機(jī)dpi密度油猫,以次繪制視圖
        //關(guān)閉硬件加速
        setLayerType(LAYER_TYPE_SOFTWARE, null);
        //設(shè)置抗鋸齒
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setAntiAlias(true);
        //設(shè)置畫(huà)筆樣式
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(5 * mDensityDpi);

        //初始化  圓心左邊 和 半徑
        raduis = screenWidth / 3;
        pointX = pointY = screenWidth / 2;
//        pointY = screenHeight / 4;

        //設(shè)置抗鋸齒
        textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setAntiAlias(true);
        //設(shè)置畫(huà)筆顏色
        textPaint.setColor(Color.WHITE);
        // 獲取字體并設(shè)置畫(huà)筆字體
        Typeface typeface = Typeface.createFromAsset(mContext.getAssets(), "kt.ttf");
        textPaint.setTypeface(typeface);
        //設(shè)置抗鋸齒
        speedAreaPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        speedAreaPaint.setAntiAlias(true);
        //設(shè)置畫(huà)筆樣式
        speedAreaPaint.setStyle(Paint.Style.FILL);
        // 設(shè)置速度范圍扇形的漸變顏色
        Shader mShader = new LinearGradient(pointX - raduis, pointY, pointX + raduis, pointY,
                new int[]{0xFF445EED, 0xFF072AE9, 0xFF0625CE}, null, Shader.TileMode.CLAMP);
        speedAreaPaint.setShader(mShader);
        // 初始化速度范圍的2個(gè)扇形外切矩形
        speedRectF = new RectF(pointX - raduis + 10 * mDensityDpi, pointY - raduis + 10 * mDensityDpi,
                pointX + raduis - 10 * mDensityDpi, pointY + raduis - 10 * mDensityDpi);
        speedRectFInner = new RectF(pointX - raduis / 2, pointY - raduis / 2,
                pointX + raduis / 2, pointY + raduis / 2);
    }

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

        //繪制外層圓奔滑,兩個(gè)外圈祟牲,兩個(gè)內(nèi)圈
        drawCicle(canvas);

        //繪制速度范圍扇形區(qū)域
        speedAreaPaint.setColor(0x7E3F51B5);
        drawSpeedArea(canvas);

        //變換畫(huà)筆顏色 繪制刻度
        mPaint.setColor(0xBF3F6AB5);
        drawScale(canvas);

        //變換畫(huà)筆顏色 繪制速度標(biāo)識(shí)文字
        textPaint.setTextSize(25 * mDensityDpi);
        mPaint.setColor(Color.WHITE);
        sRaduis = raduis - 50 * mDensityDpi;
        textScale = Math.abs(textPaint.descent() + textPaint.ascent()) / 2;
        Log.e("textScale", textScale + "");

        drawText(canvas);

        //繪制中間文字內(nèi)容
        drawCenter(canvas);
    }

    /**
     * 繪制外層圓
     */
    private void drawCicle(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(0xFF343434);
        canvas.drawCircle(pointX, pointY, raduis, mPaint);

        //外圈2個(gè)圓
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(0xBF3F6AB5);
        mPaint.setStrokeWidth(4 * mDensityDpi);
        canvas.drawCircle(pointX, pointY, raduis, mPaint);
        mPaint.setStrokeWidth(3 * mDensityDpi);
        canvas.drawCircle(pointX, pointY, raduis - 10 * mDensityDpi, mPaint);

        //內(nèi)圈2個(gè)圓
        mPaint.setStrokeWidth(5 * mDensityDpi);
        mPaint.setColor(0xE73F51B5);
        canvas.drawCircle(pointX, pointY, raduis / 2, mPaint);
        mPaint.setColor(0x7E3F51B5);
        canvas.drawCircle(pointX, pointY, raduis / 2 + 5 * mDensityDpi, mPaint);
        mPaint.setStrokeWidth(3 * mDensityDpi);

    }

    /**
     * 繪制速度區(qū)域扇形
     */
    private void drawSpeedArea(Canvas canvas) {
        int degree;
        if (speed < 210) {
            degree = speed * 36 / 30;
        } else {
            degree = 210 * 36 / 30;
        }

        canvas.drawArc(speedRectF, 144, degree, true, speedAreaPaint);

        // TODO: 2016/5/12
        //不顯示中間的內(nèi)圈的扇形區(qū)域
        mPaint.setColor(0xFF343434);
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawArc(speedRectFInner, 144, degree, true, mPaint);
        mPaint.setStyle(Paint.Style.STROKE);
    }


    /**
     * 繪制刻度
     */
    private void drawScale(Canvas canvas) {
        for (int i = 0; i < 60; i++) {
            if (i % 6 == 0) {
                canvas.drawLine(pointX - raduis + 10 * mDensityDpi, pointY, pointX - raduis + 50 * mDensityDpi, pointY, mPaint);
            } else {
                canvas.drawLine(pointX - raduis + 10 * mDensityDpi, pointY, pointX - raduis + 30 * mDensityDpi, pointY, mPaint);
            }
            canvas.rotate(6, pointX, pointY);
        }
    }

    /**
     * 繪制速度標(biāo)識(shí)文字
     */
    private void drawText(Canvas canvas) {
//        canvas.save();
//        canvas.rotate(-36, pointX, pointY);
        for (int i = 0; i < 8; i++) {
            int value = 30 * i;
            String TEXT = String.valueOf(value);
            switch (value) {
                case 0:
                    // 計(jì)算Baseline繪制的起點(diǎn)X軸坐標(biāo)
                    baseX = (int) (pointX - sRaduis * Math.cos(Math.PI / 5) + textPaint.measureText(TEXT) / 2 + textScale / 2);
                    // 計(jì)算Baseline繪制的Y坐標(biāo)
                    baseY = (int) (pointY + sRaduis * Math.sin(Math.PI / 5) + textScale / 2);
                    break;
                case 30:
                    baseX = (int) (pointX - raduis + 50 * mDensityDpi + textPaint.measureText(TEXT) / 2);
                    baseY = (int) (pointY + textScale);
                    break;
                case 60:
                    baseX = (int) (pointX - sRaduis * Math.cos(Math.PI / 5) + textScale);
                    baseY = (int) (pointY - sRaduis * Math.sin(Math.PI / 5) + textScale * 2);
                    break;
                case 90:
                    baseX = (int) (pointX - sRaduis * Math.cos(2 * Math.PI / 5) - textScale / 2);
                    baseY = (int) (pointY - sRaduis * Math.sin(2 * Math.PI / 5) + 2 * textScale);
                    break;
                case 120:
                    baseX = (int) (pointX + sRaduis * Math.sin(Math.PI / 10) - textPaint.measureText(TEXT) / 2);
                    baseY = (int) (pointY - sRaduis * Math.cos(Math.PI / 10) + 2 * textScale);
                    break;
                case 150:
                    baseX = (int) (pointX + sRaduis * Math.cos(Math.PI / 5) - textPaint.measureText(TEXT) - textScale / 2);
                    baseY = (int) (pointY - sRaduis * Math.sin(Math.PI / 5) + textScale * 2);
                    break;
                case 180:
                    baseX = (int) (pointX + sRaduis - textPaint.measureText(TEXT) - textScale / 2);
                    baseY = (int) (pointY + textScale);
                    break;
                case 210:
                    baseX = (int) (pointX + sRaduis * Math.cos(Math.PI / 5) - textPaint.measureText(TEXT) - textScale / 2);
                    baseY = (int) (pointY + sRaduis * Math.sin(Math.PI / 5) - textScale / 2);
                    break;
            }
//            baseX = (int) (pointX - raduis + 50 * mDensityDpi + textPaint.measureText(TEXT) / 2);
//            baseY = (int) (pointY + textScale);
            canvas.drawText(TEXT, baseX, baseY, textPaint);
//            canvas.rotate(36, pointX, pointY);
        }
//        canvas.restore();
    }

    /**
     * 繪制中間文字內(nèi)容
     */
    private void drawCenter(Canvas canvas) {
        //速度
        textPaint.setTextSize(60 * mDensityDpi);
        float tw = textPaint.measureText(String.valueOf(speed));
        baseX = (int) (pointX - tw / 2);
        baseY = (int) (pointY + Math.abs(textPaint.descent() + textPaint.ascent()) / 4);
        canvas.drawText(String.valueOf(speed), baseX, baseY, textPaint);

        //單位
        textPaint.setTextSize(20 * mDensityDpi);
        tw = textPaint.measureText("km/h");
        baseX = (int) (pointX - tw / 2);
        baseY = (int) (pointY + raduis / 4 + Math.abs(textPaint.descent() + textPaint.ascent()) / 4);
        canvas.drawText("km/h", baseX, baseY, textPaint);
    }


    @Override
    public void run() {
        int speedChange;
        while (start) {
            switch (type) {
                case 1://油門(mén)
                    speedChange = 3;
                    break;
                case 2://剎車
                    speedChange = -5;
                    break;
                case 3://手剎
                    speed = 0;
                default:
                    speedChange = -1;
                    break;
            }
            speed += speedChange;
            if (speed < 1) {
                speed = 0;
            }
            try {
                Thread.sleep(50);
                setSpeed(speed);
            } catch (InterruptedException e) {
                e.printStackTrace();
                break;
            }
        }
    }
}

調(diào)用:

@Override
protected void onResume() {
    super.onResume();
    if (speedControlView != null) {
        speedControlView.setSpeed(0);
        speedControlView.setStart(true);
    }
    new Thread(speedControlView).start();
}

@Override
protected void onStop() {
    super.onStop();
    if (speedControlView != null) {
        speedControlView.setSpeed(0);
        speedControlView.setStart(false);
    }
}

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末狂魔,一起剝皮案震驚了整個(gè)濱河市椎咧,隨后出現(xiàn)的幾起案子侠讯,更是在濱河造成了極大的恐慌挖藏,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厢漩,死亡現(xiàn)場(chǎng)離奇詭異膜眠,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)溜嗜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)宵膨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人炸宵,你說(shuō)我怎么就攤上這事辟躏。” “怎么了土全?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵捎琐,是天一觀的道長(zhǎng)抑钟。 經(jīng)常有香客問(wèn)我,道長(zhǎng)野哭,這世上最難降的妖魔是什么在塔? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮拨黔,結(jié)果婚禮上蛔溃,老公的妹妹穿的比我還像新娘。我一直安慰自己篱蝇,他們只是感情好贺待,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著零截,像睡著了一般麸塞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涧衙,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天哪工,我揣著相機(jī)與錄音,去河邊找鬼弧哎。 笑死雁比,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的撤嫩。 我是一名探鬼主播偎捎,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼序攘!你這毒婦竟也來(lái)了茴她?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤程奠,失蹤者是張志新(化名)和其女友劉穎丈牢,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體梦染,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赡麦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了帕识。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片泛粹。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖肮疗,靈堂內(nèi)的尸體忽然破棺而出晶姊,到底是詐尸還是另有隱情,我是刑警寧澤伪货,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布们衙,位于F島的核電站钾怔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蒙挑。R本人自食惡果不足惜宗侦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望忆蚀。 院中可真熱鬧矾利,春花似錦、人聲如沸馋袜。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)欣鳖。三九已至察皇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間泽台,已是汗流浹背什荣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留师痕,地道東北人溃睹。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓而账,卻偏偏與公主長(zhǎng)得像胰坟,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子泞辐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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