欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

QQ运动的记步进度条实现

程序员文章站 2022-05-29 15:19:48
...

今天突然看到QQ计步器样式更新了,变的还挺快

QQ运动的记步进度条实现

我自己构思实现  和QQ还是有差别的但是基本样式实现了。

QQ运动的记步进度条实现

下来看代码  代码里面基本都全是注释。

package com.zyf.customview.view;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;

import com.zyf.customview.R;

/**
 * 作者:小飞
 * 创建于 2018年4月16日 17:46:36
 */

public class QQCustomView extends View {
    /**
     * 画笔
     * */
    private Paint mPaint;
    /**
     * 文字画笔
     * */
    private Paint mTextPaint;
    /**
     * 文字画笔
     * */
    private Paint mSmallTextPaint;
    /**
     * 填充进度颜色
     * */
    private int insideColor;
    /**
     * 背景颜色
     * */
    private int outsideColor;
    /**
     * 文字颜色
     * */
    private int textColor;
    /**
     * 宽
     * */
    private int width;
    /**
     * 高
     * */
    private int height;
    /**
     * 当前进度条
     * */
    private int currentProgress = 0;
    /**
     * 进度的百分比
     * */
    private int progress;
    /**
     * 文字的高度
     * */
    private int textheight;
    /**
     * 步数
     * */
    private int steps;
    /**
     * 最大步数
     * */
    private int stepsCount = 5000;
    /**
     * 单位
     * */
    private String company;
    /**
     * 弧线的宽度
     * */
    private int arcStrokeWidth = 20;

    /**
     * 弧线的间距
     * */
    private float spacingWidth = 2;
    /**
     * 进度监听
     * */
    private OnViewProgressListener listener;
    public QQCustomView(Context context) {
        this(context,null);
    }

    public QQCustomView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public QQCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray ty = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomView,0,0);
        try {
            insideColor = ty.getColor(R.styleable.CustomView_inside_color,0xFF40B3FF);
            outsideColor = ty.getColor(R.styleable.CustomView_outside_color,0xFF737171);
            textColor = ty.getColor(R.styleable.CustomView_text_color,0xFF40B3FF);
            steps = ty.getInt(R.styleable.CustomView_step,0);
            stepsCount = ty.getInt(R.styleable.CustomView_step_count,5000);
            company = ty.getString(R.styleable.CustomView_company);
            arcStrokeWidth = ty.getInt(R.styleable.CustomView_arc_stroke_width,20);
            if(TextUtils.isEmpty(company)){
                company = "步";
            }
        }finally {
            ty.recycle();
        }
        init();
    }
    /**
     * 初始化属性
     *  <attr name="inside_color" format="color"></attr>
     *  <attr name="outside_color" format="color"></attr>
     *  <attr name="text_color" format="color"></attr>
     * */
    private void init() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(5);
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(100);
        mTextPaint.setColor(textColor);
        textheight = (int)(mTextPaint.ascent()+mTextPaint.descent());
        mSmallTextPaint = new Paint();
        mSmallTextPaint.setAntiAlias(true);
        mSmallTextPaint.setTextSize(50);
        mSmallTextPaint.setColor(textColor);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        width = measureSize(widthMeasureSpec);
        height = measureSize(heightMeasureSpec);
        setMeasuredDimension(width,height);
    }
    private int measureSize(int measureSpec) {
        int length;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        if(mode == MeasureSpec.EXACTLY){
            length = size;
        }else{
            length = 500;
            if(mode == MeasureSpec.AT_MOST){
                length = Math.min(length,size);
            }
        }
        return length;
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(listener!=null){
            listener.onProgress(currentProgress);
        }
        drawInside(canvas);
        drawOutside(canvas,currentProgress);
        drawText(canvas,currentProgress);
    }
    /**
     * 画字
     * */
    private void drawText(Canvas canvas,int steps) {
        String str = steps+"";
        int textWidth = (int)mTextPaint.measureText(str);
        int textsWidth = (int)mSmallTextPaint.measureText(company);
        int countWidth = textsWidth +textWidth;
        canvas.drawText(str,0,str.length(),(width-countWidth)/2,(height-textheight)/2,mTextPaint);
        canvas.drawText(company,0,1,(width+textWidth-textsWidth)/2,(height-textheight)/2,mSmallTextPaint);
    }

    /**
     * 画背景
     * */
    private void drawInside(Canvas canvas) {
        mPaint.setColor(outsideColor);
        canvas.save();
        //这里270的意思是旋转270的弧线的意思
        //(float) (width/2 + width/2 * Math.cos(135f*Math.PI/180))
        // 计算的是 135度的情况下 圆上的点位置  计算两个同一半径直线上的两个点画线 旋转画布角度  重复画  就达到了所有的刻度
        for (float i = 0; i < 270/spacingWidth; i++) {
            canvas.drawLine((float) (width/2 + width/2 * Math.cos(135f*Math.PI/180)),(float) (height/2 + (width/2) * Math.sin(135f*Math.PI/180)),
                    (float) (width/2 + (width/2-arcStrokeWidth) * Math.cos(135f*Math.PI/180)),(float) (height/2 + (width/2-arcStrokeWidth) * Math.sin(135f*Math.PI/180)), mPaint);
            canvas.rotate(spacingWidth, getWidth() / 2, getHeight() / 2);
        }
        canvas.restore();
    }

    /**
     * 画进度
     * */
    private void drawOutside(Canvas canvas,int progress) {
        float angle;
        if((progress/(stepsCount*1f))*270>270){
            angle = 270;
        }else {
            angle =  (progress/(stepsCount*1f))*270;
        }
        mPaint.setColor(insideColor);
        mPaint.setStyle(Paint.Style.STROKE);
        canvas.save();
        for (float i = 0; i < angle/spacingWidth; i++) {
            canvas.drawLine((float) (width/2 + width/2 * Math.cos(135f*Math.PI/180)),(float) (height/2 + (width/2) * Math.sin(135f*Math.PI/180)),
                    (float) (width/2 + (width/2-arcStrokeWidth) * Math.cos(135f*Math.PI/180)),(float) (height/2 + (width/2-arcStrokeWidth) * Math.sin(135f*Math.PI/180)), mPaint);
            canvas.rotate(spacingWidth, getWidth() / 2, getHeight() / 2);
        }
        canvas.restore();
    }
    /**
     * 设置当前进度
     * */
    public void setCurrentProgress(int currentProgress) {
        this.currentProgress = currentProgress;
        invalidate();
    }
    /**
     * 属性动画
     * */
    ValueAnimator anim;
    private void animatorMethod(){
        if(anim!=null&&anim.isRunning()){
            return;
        }
        anim = ValueAnimator.ofInt(0,steps);
        anim.setDuration(3000);
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                currentProgress = (int)animation.getAnimatedValue();
                invalidate();
            }
        });
        anim.start();

    }

    public void setSteps(int steps) {
        this.steps = steps;
        animatorMethod();
    }
    /**
     * 设置线条宽度
     * */
    public void setArcStrokeWidth(int arcStrokeWidth) {
        if(arcStrokeWidth<10){
            return;
        }
        this.arcStrokeWidth = arcStrokeWidth;
        invalidate();
    }
    /**
     * 设置线条间距
     * */
    public void setSpacingWidth(float spacingWidth) {
        if(spacingWidth<1){
            return;
        }
        this.spacingWidth = spacingWidth;
        invalidate();
    }

    public interface OnViewProgressListener{
        void onProgress(int progress);
    }
    /**
     * 设置监听
     * */
    public void setListener(OnViewProgressListener listener) {
        this.listener = listener;
    }
}

里面的自定义属性不用关注 上面有属性注释  自己也可以直接初始化即可

源码地址

相关标签: 自定义控件