QQ运动步数&自定义ProgressBar

效果如下

gif图展示效果不好,实际体验无卡顿

1.自定义属性

早Values目录下New-values resource file,命名为attrs.xml(命名随意,但规范命名为attrs.xml)
自定义属性如下,注意format不要与Android自带的命名重复。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="QQStepView">
        <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>

    <declare-styleable name="MyProgressBar">
        <attr name="leftColor" format="color" />
        <attr name="rightColor" format="color" />
        <attr name="progressTextColor" format="color" />
        <attr name="progressTextSize" format="dimension" />
        <attr name="progressBounds" format="dimension" />
    </declare-styleable>
</resources>
2.编写自定义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.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;

import com.cyq.customview2.R;
import com.cyq.customview2.utils.MeasureUtils;

@SuppressWarnings("all")
public class QQStepView extends View {
    private int mOuterColor = Color.parseColor("#2196F3");
    private int mInnerColor = Color.parseColor("#F44336");
    private int mStepTextColor = Color.parseColor("#EC407A");
    private int mBorderWidth = 20;//px
    private int mStepTextSize = 18;//px

    private int mSeptMax = 10000;
    private int mSeptCurrent = 0;

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

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

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

    public QQStepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.QQStepView);
        mOuterColor = array.getColor(R.styleable.QQStepView_outerColor, mOuterColor);
        mInnerColor = array.getColor(R.styleable.QQStepView_innerColor, mInnerColor);
        mStepTextColor = array.getColor(R.styleable.QQStepView_stepTextColor, mStepTextColor);
        mBorderWidth = (int) array.getDimension(R.styleable.QQStepView_borderWidth, MeasureUtils.dp2px(mBorderWidth, this));
        mStepTextSize = array.getDimensionPixelSize(R.styleable.QQStepView_stepTextSize, MeasureUtils.sp2px(mStepTextSize, this));
        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) {
        super.onMeasure(widthMeasureSpec, 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);
    }

    @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;
        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);
    }

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

    public synchronized void setmSeptCurrent(int mSeptCurrent) {
        this.mSeptCurrent = mSeptCurrent;
        //重绘
        invalidate();
    }
}
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.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;

import com.cyq.customview2.R;
import com.cyq.customview2.utils.MeasureUtils;

@SuppressWarnings("all")
public class MyProgressBar extends View {
    private int mLiftColor = Color.parseColor("#F44336");
    private int mRightColor = Color.parseColor("#E0E0E0");
    private int mProgressTextColor = Color.parseColor("#616161");
    private int mProgressTextSize = 12;//px 后续再考虑需不需要转换成sp
    private int mProgressBounds = 1;//px

    private int mCurrentProgress, mMaxProgress = 100;//默认最大刻度为100

    private Paint mLeftPaint, mRightPaint, mTextPaint;

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

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

    public MyProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyProgressBar);
        mLiftColor = array.getColor(R.styleable.MyProgressBar_leftColor, mLiftColor);
        mRightColor = array.getColor(R.styleable.MyProgressBar_rightColor, mRightColor);
        mProgressTextColor = array.getColor(R.styleable.MyProgressBar_progressTextColor, mProgressTextColor);
        mProgressTextSize = array.getDimensionPixelSize(R.styleable.MyProgressBar_progressTextSize, mProgressTextSize);
        array.recycle();


        mLeftPaint = new Paint();
        mLeftPaint.setAntiAlias(true);
        mLeftPaint.setColor(mLiftColor);

        mRightPaint = new Paint();
        mRightPaint.setAntiAlias(true);
        mRightPaint.setColor(mRightColor);

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widht = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(widht, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mRightPaint.setStrokeWidth(getHeight());
        RectF rightRect = new RectF(0, 0, getWidth(), getHeight());
        canvas.drawRoundRect(rightRect, getHeight() / 2, getHeight() / 2, mRightPaint);

        mLeftPaint.setStrokeWidth(getHeight());
        float progress = (float) mCurrentProgress / (mMaxProgress * 10);
        int radius = getHeight() / 2;
        RectF rectF = new RectF(0, 0, progress * getWidth(), getHeight());
        canvas.drawRoundRect(rectF, radius, radius, mLeftPaint);

        //画文字随着进度条右移
        String text = (float) mCurrentProgress / 10 + "%";
        int dx = getHeight() / 2;
        Rect textBounds = new Rect();
        mTextPaint.getTextBounds(text, 0, text.length(), textBounds);
        int baseLine = MeasureUtils.measureBaseLine(mTextPaint, text, this);
        canvas.drawText(text, progress * getWidth() + 10, baseLine, mTextPaint);
    }

    public void setProgress(int mCurrentProgress) {
        this.mCurrentProgress = mCurrentProgress;
        //重绘
        invalidate();
    }

    public void setMaxProgress(int mMaxProgress) {
        this.mMaxProgress = mMaxProgress;
    }

    public int getProgress() {
        return mCurrentProgress;
    }
}
3.为自定义View添加动画

首先在xml中使用我们的自定义布局和自定义属性

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".page3.QQSportActivity">

    <com.cyq.customview2.page3.QQStepView
        android:id="@+id/custom_QQ_step"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="50dp"
        app:borderWidth="6dp"
        app:innerColor="@color/innerColor"
        app:outerColor="@color/outerColor"
        app:stepTextSize="30sp"
        android:layout_marginBottom="100dp"/>

    <com.cyq.customview2.page3.MyProgressBar
        android:id="@+id/custom_progressbar"
        android:layout_width="match_parent"
        android:layout_height="20dp"
        android:layout_margin="50dp"
        app:leftColor="@color/innerColor"
        app:progressTextColor="@color/stepTextColor"
        app:progressTextSize="16sp"
        app:rightColor="@color/greyColor" />
</LinearLayout>

通过属性动画动态增加进度

  
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.animation.DecelerateInterpolator;

import com.cyq.customview2.R;

import butterknife.BindView;
import butterknife.ButterKnife;

public class QQSportActivity extends AppCompatActivity {
    @BindView(R.id.custom_QQ_step)
    QQStepView customQQStep;
    @BindView(R.id.custom_progressbar)
    MyProgressBar customProgressbar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_qqsport);
        ButterKnife.bind(this);

        customQQStep.setmSeptMax(10000);

        //属性动画
        ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, 8765);
        valueAnimator.setDuration(2000);
        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();

        //属性动画
        ValueAnimator valueAnimator2 = ObjectAnimator.ofFloat(0, 780);
        valueAnimator2.setDuration(2000);
        valueAnimator2.setInterpolator(new DecelerateInterpolator());//插值器
        valueAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float currentStep = (Float) animation.getAnimatedValue();
                customProgressbar.setProgress((int) currentStep);
            }
        });
        valueAnimator2.start();
    }
}

获取文字基线和sp,dp转xp的工具类如下;

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());
    }
}

后续待改进

1.sp,dp,xp的转换
2.进度文字接近100%时不向右边移动,并且文字和进度重叠部分动态变色

原文地址:https://www.cnblogs.com/chenyangqi/p/9489115.html