• 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%时不向右边移动,并且文字和进度重叠部分动态变色

  • 相关阅读:
    学WPF (1 of n)干啥都有第一次
    程序启动时显示Flash窗体(C#)
    对象序列化后直接获取byte[]的方法
    工程管理(1 of n): 建立用于管理代码开发的注释标记
    发现Visual Studio隐含的大礼包漂亮的Visual Studio图像库
    C# Hello World
    更人性化地控制用户输入(1 of n)
    快手导航 计算机软件网址导航 时空地图TimeGIS
    中国图书馆图书分类法(Chinese Library Classification CLC)的XML文档生成 时空地图TimeGIS
    快手软件 v2.5 发布 时空地图TimeGIS
  • 原文地址:https://www.cnblogs.com/chenyangqi/p/9489115.html
Copyright © 2020-2023  润新知