Android實(shí)現(xiàn)儀表盤(pán)效果
本文實(shí)例為大家分享了Android實(shí)現(xiàn)儀表盤(pán)效果的具體代碼,供大家參考,具體內(nèi)容如下
儀表盤(pán)效果,圓弧可變色,效果圖如下:
通過(guò)自定義view實(shí)現(xiàn),代碼如下:
public class DashboardView extends View { private int mRadius; // 畫(huà)布邊緣半徑(去除padding后的半徑) private int mStartAngle = 150; // 起始角度 private int mSweepAngle = 240; // 繪制角度 private int mMin = 1; // 最小值 private int mMax = 8; // 最大值 private int mSection = 8; // 值域(mMax-mMin)等分份數(shù) private int mPortion = 3; // 一個(gè)mSection等分份數(shù) private String mHeaderText = ""; // 表頭 private int mCreditValue = (int) 8.12; // 信用分 private int mSolidCreditValue = mCreditValue; // 信用分(設(shè)定后不變) private int mSparkleWidth; // 亮點(diǎn)寬度 private int mProgressWidth; // 進(jìn)度圓弧寬度 private float mLength1; // 刻度頂部相對(duì)邊緣的長(zhǎng)度 private int mCalibrationWidth; // 刻度圓弧寬度 private float mLength2; // 刻度讀數(shù)頂部相對(duì)邊緣的長(zhǎng)度 private int mPadding; private float mCenterX, mCenterY; // 圓心坐標(biāo) private Paint mPaint; private RectF mRectFProgressArc; private RectF mRectFCalibrationFArc; private RectF mRectFTextArc; private Path mPath; private Rect mRectText; private String[] mTexts; private int mBackgroundColor; private int[] mBgColors; private boolean isAnimFinish = true; private float mAngleWhenAnim; public DashboardView(Context context) { this(context, null); } public DashboardView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DashboardView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mSparkleWidth = dp2px(10); mProgressWidth = dp2px(3); mCalibrationWidth = dp2px(10); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStrokeCap(Paint.Cap.ROUND); mRectFProgressArc = new RectF(); mRectFCalibrationFArc = new RectF(); mRectFTextArc = new RectF(); mPath = new Path(); mRectText = new Rect(); mTexts = new String[]{"", "Ⅳ", "", "Ⅲ", "", "Ⅱ", "", "Ⅰ", ""}; mBgColors = new int[]{ContextCompat.getColor(getContext(), R.color.color_red), ContextCompat.getColor(getContext(), R.color.color_orange), ContextCompat.getColor(getContext(), R.color.color_yellow), ContextCompat.getColor(getContext(), R.color.color_green), ContextCompat.getColor(getContext(), R.color.color_blue)}; mBackgroundColor = mBgColors[0]; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mPadding = Math.max( Math.max(getPaddingLeft(), getPaddingTop()), Math.max(getPaddingRight(), getPaddingBottom()) ); setPadding(mPadding, mPadding, mPadding, mPadding); mLength1 = mPadding + mSparkleWidth / 2f + dp2px(8); mLength2 = mLength1 + mCalibrationWidth + dp2px(1) + dp2px(5); int width = resolveSize(dp2px(220), widthMeasureSpec); mRadius = (width - mPadding * 2) / 2; setMeasuredDimension(width, width - dp2px(30)); mCenterX = mCenterY = getMeasuredWidth() / 2f; mRectFProgressArc.set( mPadding + mSparkleWidth / 2f, mPadding + mSparkleWidth / 2f, getMeasuredWidth() - mPadding - mSparkleWidth / 2f, getMeasuredWidth() - mPadding - mSparkleWidth / 2f ); mRectFCalibrationFArc.set( mLength1 + mCalibrationWidth / 2f, mLength1 + mCalibrationWidth / 2f, getMeasuredWidth() - mLength1 - mCalibrationWidth / 2f, getMeasuredWidth() - mLength1 - mCalibrationWidth / 2f ); mPaint.setTextSize(sp2px(10)); mPaint.getTextBounds("0", 0, "0".length(), mRectText); mRectFTextArc.set( mLength2 + mRectText.height(), mLength2 + mRectText.height(), getMeasuredWidth() - mLength2 - mRectText.height(), getMeasuredWidth() - mLength2 - mRectText.height() ); } @SuppressLint("ResourceAsColor") @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // canvas.drawColor(mBackgroundColor); // canvas.drawColor(R.color.color_blue); /** * 畫(huà)進(jìn)度圓弧背景 */ mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(mProgressWidth); mPaint.setAlpha(80); mPaint.setColor(mBackgroundColor); canvas.drawArc(mRectFProgressArc, mStartAngle + 1, mSweepAngle - 2, false, mPaint); mPaint.setAlpha(255); if (isAnimFinish) { /** * 畫(huà)進(jìn)度圓弧(起始到信用值) */ mPaint.setShader(generateSweepGradient()); mPaint.setColor(mBackgroundColor); canvas.drawArc(mRectFProgressArc, mStartAngle + 1, calculateRelativeAngleWithValue(mCreditValue) - 2, false, mPaint); /** * 畫(huà)指示亮點(diǎn) */ float[] point = getCoordinatePoint( mRadius - mSparkleWidth / 2f, mStartAngle + calculateRelativeAngleWithValue(mCreditValue) ); mPaint.setStyle(Paint.Style.FILL); mPaint.setShader(generateRadialGradient(point[0], point[1])); mPaint.setColor(mBackgroundColor); canvas.drawCircle(point[0], point[1], mSparkleWidth / 2f, mPaint); } else { /** * 畫(huà)進(jìn)度圓弧(起始到信用值) */ mPaint.setShader(generateSweepGradient()); mPaint.setColor(mBackgroundColor); canvas.drawArc(mRectFProgressArc, mStartAngle + 1, mAngleWhenAnim - mStartAngle - 2, false, mPaint); /** * 畫(huà)指示亮點(diǎn) */ float[] point = getCoordinatePoint( mRadius - mSparkleWidth / 2f, mAngleWhenAnim ); mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(mBackgroundColor); mPaint.setShader(generateRadialGradient(point[0], point[1])); canvas.drawCircle(point[0], point[1], mSparkleWidth / 2f, mPaint); } /** * 畫(huà)刻度圓弧 */ mPaint.setShader(null); mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(Color.WHITE); mPaint.setAlpha(80); mPaint.setStrokeCap(Paint.Cap.SQUARE); mPaint.setStrokeWidth(mCalibrationWidth); mPaint.setColor(mBackgroundColor); canvas.drawArc(mRectFCalibrationFArc, mStartAngle + 3, mSweepAngle - 6, false, mPaint); /** * 畫(huà)長(zhǎng)刻度 * 畫(huà)好起始角度的一條刻度后通過(guò)canvas繞著原點(diǎn)旋轉(zhuǎn)來(lái)畫(huà)剩下的長(zhǎng)刻度 */ mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setStrokeWidth(dp2px(2)); mPaint.setAlpha(120); mPaint.setColor(mBackgroundColor); float x0 = mCenterX; float y0 = mPadding + mLength1 + dp2px(1); float x1 = mCenterX; float y1 = y0 + mCalibrationWidth; // 逆時(shí)針到開(kāi)始處 canvas.save(); canvas.drawLine(x0, y0, x1, y1, mPaint); float degree = mSweepAngle / mSection; for (int i = 0; i < mSection / 2; i++) { canvas.rotate(-degree, mCenterX, mCenterY); canvas.drawLine(x0, y0, x1, y1, mPaint); } canvas.restore(); // 順時(shí)針到結(jié)尾處 canvas.save(); for (int i = 0; i < mSection / 2; i++) { canvas.rotate(degree, mCenterX, mCenterY); canvas.drawLine(x0, y0, x1, y1, mPaint); } canvas.restore(); /** * 畫(huà)短刻度 * 同樣采用canvas的旋轉(zhuǎn)原理 */ mPaint.setStrokeWidth(dp2px(1)); mPaint.setAlpha(80); mPaint.setColor(mBackgroundColor); float x2 = mCenterX; float y2 = y0 + mCalibrationWidth - dp2px(2); // 逆時(shí)針到開(kāi)始處 canvas.save(); canvas.drawLine(x0, y0, x2, y2, mPaint); degree = mSweepAngle / (mSection * mPortion); for (int i = 0; i < (mSection * mPortion) / 2; i++) { canvas.rotate(-degree, mCenterX, mCenterY); canvas.drawLine(x0, y0, x2, y2, mPaint); } canvas.restore(); // 順時(shí)針到結(jié)尾處 canvas.save(); for (int i = 0; i < (mSection * mPortion) / 2; i++) { canvas.rotate(degree, mCenterX, mCenterY); canvas.drawLine(x0, y0, x2, y2, mPaint); } canvas.restore(); /** * 畫(huà)長(zhǎng)刻度讀數(shù) * 添加一個(gè)圓弧path,文字沿著path繪制 */ mPaint.setTextSize(sp2px(10)); mPaint.setTextAlign(Paint.Align.LEFT); mPaint.setStyle(Paint.Style.FILL); mPaint.setAlpha(160); mPaint.setColor(mBackgroundColor); for (int i = 0; i < mTexts.length; i++) { mPaint.getTextBounds(mTexts[i], 0, mTexts[i].length(), mRectText); // 粗略把文字的寬度視為圓心角2*θ對(duì)應(yīng)的弧長(zhǎng),利用弧長(zhǎng)公式得到θ,下面用于修正角度 float θ = (float) (180 * mRectText.width() / 2 / (Math.PI * (mRadius - mLength2 - mRectText.height()))); mPath.reset(); mPath.addArc( mRectFTextArc, mStartAngle + i * (mSweepAngle / mSection) - θ, // 正起始角度減去θ使文字居中對(duì)準(zhǔn)長(zhǎng)刻度 mSweepAngle ); canvas.drawTextOnPath(mTexts[i], mPath, 0, 0, mPaint); } /** * 畫(huà)實(shí)時(shí)度數(shù)值 */ mPaint.setAlpha(255); mPaint.setTextSize(sp2px(50)); mPaint.setTextAlign(Paint.Align.CENTER); mPaint.setColor(mBackgroundColor); // String value = String.valueOf(mSolidCreditValue); String value = ""; if (mSolidCreditValue > 6) { //"Ⅳ", "", "Ⅲ", "", "Ⅱ", "", "Ⅰ", value = "Ⅰ"; } else if (5 < mSolidCreditValue && mSolidCreditValue <= 6) { value = "Ⅱ"; } else if (4 < mSolidCreditValue && mSolidCreditValue <= 5) { value = "Ⅲ"; } else if (3 < mSolidCreditValue && mSolidCreditValue <= 4) { value = "Ⅳ"; } canvas.drawText(value, mCenterX, mCenterY + dp2px(30), mPaint); /** * 畫(huà)表頭 */ mPaint.setAlpha(160); mPaint.setTextSize(sp2px(12)); mPaint.setColor(Color.BLACK); canvas.drawText(mHeaderText, mCenterX, mCenterY - dp2px(20), mPaint); /** * 畫(huà)描述 */ mPaint.setAlpha(255); mPaint.setTextSize(sp2px(20)); mPaint.setColor(mBackgroundColor); canvas.drawText(calculateCreditDescription(), mCenterX, mCenterY + dp2px(55), mPaint); /** * 畫(huà)評(píng)估時(shí)間 */ mPaint.setAlpha(160); mPaint.setTextSize(sp2px(10)); mPaint.setColor(Color.BLACK); canvas.drawText(getFormatTimeStr(), mCenterX, mCenterY + dp2px(70), mPaint); } private int dp2px(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics()); } private int sp2px(int sp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, Resources.getSystem().getDisplayMetrics()); } private SweepGradient generateSweepGradient() { SweepGradient sweepGradient = new SweepGradient(mCenterX, mCenterY, new int[]{Color.argb(0, 255, 255, 255), Color.argb(200, 255, 255, 255)}, new float[]{0, calculateRelativeAngleWithValue(mCreditValue) / 360} ); Matrix matrix = new Matrix(); matrix.setRotate(mStartAngle - 1, mCenterX, mCenterY); sweepGradient.setLocalMatrix(matrix); return sweepGradient; } private RadialGradient generateRadialGradient(float x, float y) { return new RadialGradient(x, y, mSparkleWidth / 2f, new int[]{Color.argb(255, 255, 255, 255), Color.argb(80, 255, 255, 255)}, new float[]{0.4f, 1}, Shader.TileMode.CLAMP ); } private float[] getCoordinatePoint(float radius, float angle) { float[] point = new float[2]; double arcAngle = Math.toRadians(angle); //將角度轉(zhuǎn)換為弧度 if (angle < 90) { point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius); point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius); } else if (angle == 90) { point[0] = mCenterX; point[1] = mCenterY + radius; } else if (angle > 90 && angle < 180) { arcAngle = Math.PI * (180 - angle) / 180.0; point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius); point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius); } else if (angle == 180) { point[0] = mCenterX - radius; point[1] = mCenterY; } else if (angle > 180 && angle < 270) { arcAngle = Math.PI * (angle - 180) / 180.0; point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius); point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius); } else if (angle == 270) { point[0] = mCenterX; point[1] = mCenterY - radius; } else { arcAngle = Math.PI * (360 - angle) / 180.0; point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius); point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius); } return point; } /** * 相對(duì)起始角度計(jì)算所對(duì)應(yīng)的角度大小 */ private float calculateRelativeAngleWithValue(int value) { float degreePerSection = 1f * mSweepAngle / mSection; if (value > 6) { return 8 * degreePerSection + 2 * degreePerSection / 250 * (value - 6); } else if (value > 5) { return 6 * degreePerSection + 2 * degreePerSection / 50 * (value - 5); } else if (value > 4) { return 4 * degreePerSection + 2 * degreePerSection / 50 * (value - 4); } else if (value > 3) { return 2 * degreePerSection + 2 * degreePerSection / 50 * (value - 3); } else { return 2 * degreePerSection / 200 * (value - 1); } } /** * 信用分對(duì)應(yīng)描述 */ private String calculateCreditDescription() { if (mSolidCreditValue > 6) { return ""; } else if (mSolidCreditValue > 5) { return ""; } else if (mSolidCreditValue > 4) { return ""; } else if (mSolidCreditValue > 3) { return ""; } return ""; } private SimpleDateFormat mDateFormat; private String getFormatTimeStr() { if (mDateFormat == null) { mDateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm", Locale.CHINA); } return String.format("", mDateFormat.format(new Date())); // return String.format("監(jiān)測(cè)日期:%s", "2020-05-04"); } public int getCreditValue() { return mCreditValue; } /** * 設(shè)置值 * * @param creditValue */ public void setCreditValue(int creditValue) { if (mSolidCreditValue == creditValue || creditValue < mMin || creditValue > mMax) { return; } mSolidCreditValue = creditValue; mCreditValue = creditValue; postInvalidate(); } /** * 設(shè)置并播放動(dòng)畫(huà) * * @param creditValue 值 */ public void setCreditValueWithAnim(int creditValue) { if (creditValue < mMin || creditValue > mMax || !isAnimFinish) { return; } mSolidCreditValue = creditValue; ValueAnimator creditValueAnimator = ValueAnimator.ofInt(1, mSolidCreditValue); creditValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCreditValue = (int) animation.getAnimatedValue(); postInvalidate(); } }); // 計(jì)算最終值對(duì)應(yīng)的角度,以掃過(guò)的角度的線性變化來(lái)播放動(dòng)畫(huà) float degree = calculateRelativeAngleWithValue(mSolidCreditValue); ValueAnimator degreeValueAnimator = ValueAnimator.ofFloat(mStartAngle, mStartAngle + degree); degreeValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mAngleWhenAnim = (float) animation.getAnimatedValue(); } }); @SuppressLint("ObjectAnimatorBinding") ObjectAnimator colorAnimator = ObjectAnimator.ofInt(this, "mBackgroundColor", mBgColors[0], mBgColors[0]); // 實(shí)時(shí)信用值對(duì)應(yīng)的背景色的變化 long delay = 1000; if (mSolidCreditValue > 6) { colorAnimator.setIntValues(mBgColors[0], mBgColors[1], mBgColors[2], mBgColors[3], mBgColors[4]); delay = 3000; } else if (mSolidCreditValue > 5) { colorAnimator.setIntValues(mBgColors[0], mBgColors[1], mBgColors[2], mBgColors[3]); delay = 2500; } else if (mSolidCreditValue > 4) { colorAnimator.setIntValues(mBgColors[0], mBgColors[1], mBgColors[2]); delay = 2000; } else if (mSolidCreditValue > 3) { colorAnimator.setIntValues(mBgColors[0], mBgColors[1]); delay = 1500; } colorAnimator.setEvaluator(new ArgbEvaluator()); colorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mBackgroundColor = (int) animation.getAnimatedValue(); } }); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.setDuration(delay) .playTogether(creditValueAnimator, degreeValueAnimator, colorAnimator); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); isAnimFinish = false; } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); isAnimFinish = true; } @Override public void onAnimationCancel(Animator animation) { super.onAnimationCancel(animation); isAnimFinish = true; } }); animatorSet.start(); } }
在color中添加顏色值:
<color name="color_red">#FF5722</color> <color name="color_orange">#FF9800</color> <color name="color_yellow">#FFC107</color> <color name="color_green">#3FB830</color> <color name="color_blue">#00BAED</color>
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
android中對(duì)文件加密解密的實(shí)現(xiàn)
本文主要介紹了android中對(duì)文件加密解密的實(shí)現(xiàn),文件加密現(xiàn)在的應(yīng)用很廣,有需要的朋友可以了解一下。2016-10-10Android開(kāi)發(fā)實(shí)現(xiàn)可拖動(dòng)排序的ListView功能【附源碼下載】
這篇文章主要介紹了Android開(kāi)發(fā)實(shí)現(xiàn)可拖動(dòng)排序的ListView功能,結(jié)合實(shí)例形式分析了Android列表拖動(dòng)排序原理與相關(guān)操作技巧,并附帶完整源碼供讀者下載參考,需要的朋友可以參考下2017-11-11Android 修改Preferences默認(rèn)樣式的步驟
這篇文章主要介紹了Android 修改Preferences默認(rèn)樣式的步驟,幫助大家更好的理解和學(xué)習(xí)使用Android開(kāi)發(fā),感興趣的朋友可以了解下2021-04-04Android在kts中使用navigation及Args的方法
在Android項(xiàng)目中使用Kotlin腳本(kts)替代Groovy配置navigation和Args,需添加相關(guān)依賴(lài),并在build.gradle中進(jìn)行配置,文章詳細(xì)介紹了如何在kts中使用navigation進(jìn)行頁(yè)面導(dǎo)航和參數(shù)傳遞,介紹了使用Bundle和Safe Args兩種方式安全傳遞參數(shù)2024-10-10Android自定義view實(shí)現(xiàn)輸入框效果
這篇文章主要為大家詳細(xì)介紹了Android自定義view實(shí)現(xiàn)輸入框效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03Android判斷軟鍵盤(pán)彈出并隱藏的簡(jiǎn)單完美解決方法(推薦)
下面小編就為大家?guī)?lái)一篇Android判斷軟鍵盤(pán)彈出并隱藏的簡(jiǎn)單完美解決方法(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10Android視頻懸浮窗口實(shí)現(xiàn)的示例代碼
這篇文章主要介紹了Android視頻懸浮窗口實(shí)現(xiàn)的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04用Android?studio實(shí)現(xiàn)簡(jiǎn)易計(jì)算器功能
這篇文章主要為大家詳細(xì)介紹了用Android?studio實(shí)現(xiàn)簡(jiǎn)易計(jì)算器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05android利用ContentResolver訪問(wèn)者獲取手機(jī)聯(lián)系人信息
這篇文章主要介紹了android利用ContentResolver訪問(wèn)者獲取手機(jī)聯(lián)系人信息,非常具有實(shí)用價(jià)值,需要的朋友可以參考下。2017-02-02