如圖:
自定義屬性
values文件下添加 attrs.xml文件
<?xml version="1.0" encoding="utf-8"?> <resources> <!-- 儀表盤自定義屬性 --> <attr name="color_dial_lower" format="color"/> <attr name="color_dial_middle" format="color"/> <attr name="color_dial_high" format="color"/> <attr name="text_size_dial" format="dimension"/> <attr name="stroke_width_dial" format="dimension"/> <attr name="radius_circle_dial" format="dimension"/> <attr name="text_title_dial" format="string"/> <attr name="text_title_size" format="dimension"/> <attr name="text_title_color" format="color"/> <attr name="text_size_value" format="dimension"/> <attr name="animator_play_time" format="integer"/> <declare-styleable name="ClockView"> <attr name="color_dial_lower"/> <attr name="color_dial_middle"/> <attr name="color_dial_high"/> <attr name="text_size_dial"/> <attr name="stroke_width_dial"/> <attr name="radius_circle_dial"/> <attr name="text_title_dial"/> <attr name="text_title_size"/> <attr name="text_title_color"/> <attr name="text_size_value"/> <attr name="animator_play_time"/> </declare-styleable> </resources>
自定義view
package com.chuanye.yibiaopan; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; public class ClockView2 extends View { private static final int DEFAULT_COLOR_LOWER = Color.parseColor("#1d953f");//下游顏色 private static final int DEFAULT_COLOR_MIDDLE = Color.parseColor("#228fbd");//中間顏色 private static final int DEFAULT_COLOR_HIGH = Color.RED;//高的顏色 private static final int DEAFAULT_COLOR_TITLE = Color.BLACK;//標題顏色 private static final int DEFAULT_TEXT_SIZE_DIAL = 11;//轉盤 字體大小 private static final int DEFAULT_STROKE_WIDTH = 8;//線的寬度 private static final int DEFAULT_RADIUS_DIAL = 128;//轉盤半徑 private static final int DEAFAULT_TITLE_SIZE = 16;//標題大小 private static final int DEFAULT_VALUE_SIZE = 28;//值的大小 private static final int DEFAULT_ANIM_PLAY_TIME = 2000;//動畫時間 private int colorDialLower;//轉盤下游顏色 private int colorDialMiddle;//轉盤中游顏色 private int colorDialHigh;//轉盤上游顏色 private int textSizeDial;//轉盤文字大小 private int strokeWidthDial;//轉盤中風寬度 private String titleDial;//轉盤標題 private int titleDialSize;//轉盤標題大小 private int titleDialColor;//轉盤標題顏色 private int valueTextSize;//值的大小 private int animPlayTime;//動畫時間 private int radiusDial;//轉盤半徑 private int mRealRadius;//實際半徑 private float currentValue;//當前值 private Paint arcPaint;//弧的畫筆 private RectF mRect;//矩形 private Paint pointerPaint;//指針 private Paint.FontMetrics fontMetrics;//字體度量 private Paint titlePaint;//標題畫筆 private Path pointerPath;//指示器路徑 public ClockView2(Context context) { this(context, null); } public ClockView2(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public ClockView2(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //初始化屬性 initAttrs(context, attrs); initPaint(); } private void initAttrs(Context context, AttributeSet attrs){ //獲得樣式屬性 TypedArray attributes = context.obtainStyledAttributes(attrs,R.styleable.ClockView); colorDialLower = attributes.getColor(R.styleable.ClockView_color_dial_lower, DEFAULT_COLOR_LOWER);//轉盤下游顏色 colorDialMiddle = attributes.getColor(R.styleable.ClockView_color_dial_middle, DEFAULT_COLOR_MIDDLE);//轉盤中游顏色 colorDialHigh = attributes.getColor(R.styleable.ClockView_color_dial_high, DEFAULT_COLOR_HIGH);//轉盤上游顏色 textSizeDial = (int) attributes.getDimension(R.styleable.ClockView_text_size_dial, sp2px(DEFAULT_TEXT_SIZE_DIAL));//文字大小 strokeWidthDial = (int) attributes.getDimension(R.styleable.ClockView_stroke_width_dial, dp2px(DEFAULT_STROKE_WIDTH));//線條寬度 radiusDial = (int) attributes.getDimension(R.styleable.ClockView_radius_circle_dial, dp2px(DEFAULT_RADIUS_DIAL));//轉盤半徑周期 titleDial = attributes.getString(R.styleable.ClockView_text_title_dial);//轉盤標題 titleDialSize = (int) attributes.getDimension(R.styleable.ClockView_text_title_size, dp2px(DEAFAULT_TITLE_SIZE));//轉盤標題大小 titleDialColor = attributes.getColor(R.styleable.ClockView_text_title_color, DEAFAULT_COLOR_TITLE);//轉盤標題顏色 valueTextSize = (int) attributes.getDimension(R.styleable.ClockView_text_size_value, dp2px(DEFAULT_VALUE_SIZE));//轉盤值 animPlayTime = attributes.getInt(R.styleable.ClockView_animator_play_time, DEFAULT_ANIM_PLAY_TIME);//動畫時間 } private void initPaint(){ //圓弧畫筆 arcPaint = new Paint(); arcPaint.setAntiAlias(true);//抗鋸齒 arcPaint.setStyle(Paint.Style.STROKE);//風格 arcPaint.setStrokeWidth(strokeWidthDial);//轉盤中風寬度 //指針畫筆 pointerPaint = new Paint(); pointerPaint.setAntiAlias(true);//抗鋸齒 pointerPaint.setTextSize(textSizeDial);//文字大小 pointerPaint.setTextAlign(Paint.Align.CENTER);//排成一行 居中 fontMetrics = pointerPaint.getFontMetrics();//獲得字體度量 //標題畫筆 titlePaint = new Paint(); titlePaint.setAntiAlias(true);//抗鋸齒 titlePaint.setTextAlign(Paint.Align.CENTER);//排成一行 居中 titlePaint.setFakeBoldText(true);//設置黑體 //指針條 pointerPath = new Path(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec);//獲得測量寬的模式 int widthSize = MeasureSpec.getSize(widthMeasureSpec);//獲得測量寬的大小 int heightMode = MeasureSpec.getMode(heightMeasureSpec);//獲得測量高的模式 int heightSize = MeasureSpec.getSize(heightMeasureSpec);//獲得測量高的大小 int mWidth, mHeight; if (widthMode == MeasureSpec.EXACTLY){//精確的 mWidth = widthSize; }else { mWidth = getPaddingLeft() + radiusDial * 2 + getPaddingRight(); if (widthMode == MeasureSpec.AT_MOST){//大概 mWidth = Math.min(mWidth, widthSize); } } if (heightMode == MeasureSpec.EXACTLY){//精確的 mHeight = heightSize; }else { mHeight = getPaddingTop() + radiusDial * 2 + getPaddingBottom(); if (heightMode == MeasureSpec.AT_MOST){//大概 mHeight = Math.min(mHeight, heightSize); } } //設置測量的大小 setMeasuredDimension(mWidth, mHeight); radiusDial = Math.min((getMeasuredWidth() - getPaddingLeft() - getPaddingRight()), (getMeasuredHeight() - getPaddingTop() - getPaddingBottom())) / 2; mRealRadius = radiusDial - strokeWidthDial / 2;//真實的半徑 mRect = new RectF(-mRealRadius, -mRealRadius, mRealRadius, mRealRadius);//矩形 左上右下 } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawArc(canvas);//畫弧 drawPointerLine(canvas);//畫指針線 drawTitleDial(canvas);//畫標題 drawPointer(canvas);//畫指針 } //畫弧 private void drawArc(Canvas canvas){ //畫布轉換 canvas.translate(getPaddingLeft() + radiusDial, getPaddingTop() + radiusDial); arcPaint.setColor(colorDialLower);//轉盤下游顏色 canvas.drawArc(mRect, 135, 54, false, arcPaint); arcPaint.setColor(colorDialMiddle);//轉盤中游顏色 canvas.drawArc(mRect, 189, 162, false, arcPaint); arcPaint.setColor(colorDialHigh);//轉盤高游顏色 canvas.drawArc(mRect, 351, 54, false, arcPaint); } //畫指針線 private void drawPointerLine(Canvas canvas){ //畫布旋轉 canvas.rotate(135); for (int i=0; i<101; i++){ //一共需要繪制101個表針 if (i <= 20){ pointerPaint.setColor(colorDialLower); }else if (i<= 80){ pointerPaint.setColor(colorDialMiddle); }else { pointerPaint.setColor(colorDialHigh); } if (i % 10 == 0){ //長表針 pointerPaint.setStrokeWidth(6); canvas.drawLine(radiusDial, 0, radiusDial - strokeWidthDial - dp2px(15), 0, pointerPaint); drawPointerText(canvas, i); }else { //短表針 pointerPaint.setStrokeWidth(3); canvas.drawLine(radiusDial, 0, radiusDial - strokeWidthDial - dp2px(5), 0, pointerPaint); } canvas.rotate(2.7f); } } //畫指針文字 private void drawPointerText(Canvas canvas, int i){ canvas.save(); int currentCenterX = (int) (radiusDial - strokeWidthDial - dp2px(21) - pointerPaint.measureText(String.valueOf(i)) / 2); canvas.translate(currentCenterX, 0); canvas.rotate(360 - 135 - 2.7f * i); //坐標系總旋轉角度為360度 int textBaseLine = (int) (0 + (fontMetrics.bottom - fontMetrics.top) /2 - fontMetrics.bottom); canvas.drawText(String.valueOf(i), 0, textBaseLine, pointerPaint); canvas.restore(); } //畫標題的值 private void drawTitleDial(Canvas canvas){ titlePaint.setColor(titleDialColor); titlePaint.setTextSize(titleDialSize); canvas.rotate( -47.7f); //恢復坐標系為起始中心位置 canvas.drawText(titleDial, 0, -radiusDial / 3, titlePaint); if (currentValue <= 20){ titlePaint.setColor(colorDialLower); }else if (currentValue <= 80){ titlePaint.setColor(colorDialMiddle); }else { titlePaint.setColor(colorDialHigh); } titlePaint.setTextSize(valueTextSize); canvas.drawText(currentValue + "%", 0, radiusDial * 2/3, titlePaint); } //畫旋轉的指針 private void drawPointer(Canvas canvas){ int currentDegree = (int) (currentValue * 2.7 + 135); canvas.rotate(currentDegree); pointerPath.moveTo(radiusDial - strokeWidthDial - dp2px(12), 0); pointerPath.lineTo(0, -dp2px(5)); pointerPath.lineTo(-12, 0); pointerPath.lineTo(0, dp2px(5)); pointerPath.close(); canvas.drawPath(pointerPath,titlePaint); } //設置完成程度 public void setCompleteDegree(float degree){ ValueAnimator animator = ValueAnimator.ofFloat(0, degree); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { currentValue = (float)(Math.round((float) animation.getAnimatedValue() * 100)) / 100; invalidate(); } }); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.setDuration(animPlayTime); animator.start(); } protected int dp2px(int dpVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, getResources().getDisplayMetrics()); } protected int sp2px(int spVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, getResources().getDisplayMetrics()); } public void setNum(float degree){ currentValue = degree; invalidate(); } public void setNumAnimator(float degree){ ValueAnimator animator = ValueAnimator.ofFloat(currentValue, degree); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { currentValue = (float)(Math.round((float) animation.getAnimatedValue() * 100)) / 100; invalidate(); } }); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.setDuration(1000); animator.start(); } }
布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:gravity="center" tools:context=".Main2Activity"> <com.chuanye.yibiaopan.ClockView2 android:id="@+id/clock_view2" android:layout_width="wrap_content" android:layout_height="wrap_content" app:text_title_dial="完成率"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/et_main2" android:hint="輸入值" android:layout_marginTop="20dp"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btn_main2" android:text="設置值" android:layout_marginTop="20dp"/> </LinearLayout>
MainActivity
package com.chuanye.yibiaopan; import android.content.Context; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.text.TextUtils; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; public class Main2Activity extends AppCompatActivity { private ClockView2 clock_view2; private Context mContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); mContext = this; clock_view2 = (ClockView2) findViewById(R.id.clock_view2); clock_view2.setCompleteDegree(32.25f); //設置指針最終位置,附帶動畫效果 final EditText et_main2 = findViewById(R.id.et_main2); Button btn_main2 = findViewById(R.id.btn_main2); btn_main2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String num = et_main2.getText().toString().trim(); if (TextUtils.isEmpty(num)){ Toast.makeText(mContext,"不能為空",Toast.LENGTH_SHORT).show(); return; }else { float num1 = Float.valueOf(num); //clock_view2.setNum(num1); //設置指針最終位置,附帶動畫效果 clock_view2.setNumAnimator(num1); } } }); } }
完成
參考來自:https://www.jianshu.com/p/f36bb920e6b3