自定义view之QQ运动步数

116 阅读2分钟
1.在res下的values下面新建attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="QView">
        <attr name="outerColor" format="color"/>
        <attr name="innerColor" format="color"/>
        <attr name="borderWidth" format="dimension"/>
        <attr name="stepTextSize" format="dimension"/>
        <attr name="stepTextColor" format="color"/>
    </declare-styleable>
</resources>
2.创建自定义的view
package com.example.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import androidx.annotation.Nullable;

public class QView extends View {

    private int mOuterColor = Color.parseColor("#2196F3");
    private int mInnerColor = Color.parseColor("#F44336");
    private int mBorderWidth = 20;   // PX
    private int mStepTextSize = 18;  //PX
    private int mStepTextColor = Color.parseColor("#EC407A");
    private int mSeptMax = 10000;   // 总共的步数
    private int mSeptCurrent = 1000;  // 当前步数

    private Paint mOutPaint;
    private Paint mInnerPaint;
    private Paint mTextPaint;


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

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

    public QView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.QView);
        mOuterColor = array.getColor(R.styleable.QView_outerColor, mOuterColor);
        mInnerColor = array.getColor(R.styleable.QView_innerColor, mInnerColor);
        mBorderWidth = (int) array.getDimension(R.styleable.QView_borderWidth, mBorderWidth);
        mStepTextSize = array.getDimensionPixelSize(R.styleable.QView_stepTextSize, mStepTextSize);
        mStepTextColor = array.getColor(R.styleable.QView_stepTextColor, mStepTextColor);
        array.recycle();
        mOutPaint = new Paint();
        mOutPaint.setAntiAlias(true);
        mOutPaint.setStrokeWidth(mBorderWidth);
        mOutPaint.setColor(mOuterColor);
        mOutPaint.setStyle(Paint.Style.STROKE);
        mOutPaint.setStrokeCap(Paint.Cap.ROUND);

        mInnerPaint = new Paint();
        mInnerPaint.setAntiAlias(true);
        mInnerPaint.setStrokeWidth(mBorderWidth);
        mInnerPaint.setColor(mInnerColor);
        mInnerPaint.setStyle(Paint.Style.STROKE);//实心
        mInnerPaint.setStrokeCap(Paint.Cap.ROUND);//圆角

        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setStyle(Paint.Style.STROKE);
        mTextPaint.setColor(mStepTextColor);
        mTextPaint.setTextSize(mStepTextSize);
    }

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

        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
            //用户设置的是wrap_content,此时设置一个默认宽高100
            width = height = MeasureUtils.dp2px(200, this);
        }
        setMeasuredDimension(width > height ? height : width, width > height ? height : width);

        //最后设置宽高
        setMeasuredDimension(width > height ? height : width, width > height ? height : width);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int center = getWidth() / 2;
        int radius = getWidth() / 2 - mBorderWidth;
        RectF rectF = new RectF(mBorderWidth, mBorderWidth, center + radius, center + radius);
        canvas.drawArc(rectF, 135, 270, false, mOutPaint);

        if (mSeptMax == 0) return;  // 如果是0步 就返回

        float sweepAngle = (float) mSeptCurrent / mSeptMax;
        canvas.drawArc(rectF, 135, 270 * sweepAngle, false, mInnerPaint);

        String stepText = mSeptCurrent + "";
        Rect textBounds = new Rect();
        mTextPaint.getTextBounds(stepText, 0, stepText.length(), textBounds);
        int dx = getWidth() / 2 - textBounds.width() / 2;
        int baseLine = MeasureUtils.measureBaseLine(mTextPaint, stepText, this);
        canvas.drawText(stepText, dx, baseLine, mTextPaint);

    }

    private int sp2px(int sp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
                getResources().getDisplayMetrics());
    }

    public void setmSeptMax(int mSeptMax) {
        this.mSeptMax = mSeptMax;
    }

    public synchronized void setmSeptCurrent(int mSeptCurrent) {
        this.mSeptCurrent = mSeptCurrent;
        //重绘
        invalidate();
    }
}

3,在xml中使用

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    
  <com.example.view.QView
      android:id="@+id/qq"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:outerColor="@color/purple_200"
      app:innerColor="@color/purple_700"
       app:borderWidth="20dp"
       app:stepTextColor="@color/purple_500"
      app:stepTextSize="30sp"
      />

</LinearLayout>

工具类

import android.graphics.Paint;
import android.graphics.Rect;
import android.util.TypedValue;
import android.view.View;

public class MeasureUtils {
    /**
     * drawText获取基线
     *
     * @param textPaint
     * @param text
     * @param view
     * @return
     */
    public static int measureBaseLine(Paint textPaint, String text, View view) {
        Rect textBounds = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), textBounds);
        Paint.FontMetricsInt fontMetricsInt = textPaint.getFontMetricsInt();
        int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
        int baseLine = view.getHeight() / 2 + dy;
        return baseLine;
    }

    public static int sp2px(int sp, View view) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, view.getResources().getDisplayMetrics());
    }

    public static int dp2px(int dp, View view) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, view.getResources().getDisplayMetrics());
    }
}

4,在Activity中添加动画

import androidx.appcompat.app.AppCompatActivity;

import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.os.Bundle;
import android.view.animation.DecelerateInterpolator;

public class MainActivity extends AppCompatActivity {

    QView customQQStep;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        customQQStep = findViewById(R.id.qq);
        customQQStep.setmSeptMax(4000);


        //属性动画
        ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, 4000);
        valueAnimator.setDuration(5000);
        valueAnimator.setInterpolator(new DecelerateInterpolator());//插值器
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float currentStep = (Float) animation.getAnimatedValue();
                customQQStep.setmSeptCurrent((int) currentStep);
            }
        });
        valueAnimator.start();
    }
}

5效果图

image.png

引用文章: blog.csdn.net/weixin_3034…

www.bilibili.com/video/BV15K…