Android自定义View之仿QQ运动步数进度效果

本文详细介绍了如何在Android中通过自定义View实现类似QQ运动的步数展示效果,包括自定义属性设置、圆弧绘制、文字显示以及动画效果的实现。通过分析需求、编写attrs.xml、在布局中使用、获取自定义属性、onMeasure方法确定大小、onDraw方法绘制图形等步骤,逐步展示了完整的代码实现过程。
摘要由CSDN通过智能技术生成


前言

关于自定义View,仿QQ运动步数,


提示:以下是本篇文章正文内容,下面案例可供参考

先看效果图 在这里插入图片描述

一、实现思路

由图片可以看出,主要分为三个部分,总进度条,已走进度条,已走步数。但是又有许多可自行设置的属性,如线条宽度,总进度条颜色,已走进度条颜色,字体颜色等等。所以需要自定义属性Attrs.xml。下来可以慢慢实现效果。

二、实现步骤

1、分析效果

代码如下(示例):

2、确定自定义属性,编写attrs.xml

 <declare-styleable name="QQStepView">
        <attr name="outerColor" format="color"/>
        <attr name="innerColor" format="color"/>
        <attr name="borderWidget" format="dimension"/>
        <attr name="stepText" format="string"/>
        <attr name="stepTextSize" format="dimension"/>
        <attr name="stepTextColor" format="color"/>
 </declare-styleable>

3、在布局中使用

 <com.joekai.customview.views.QQStepView
        android:id="@+id/step_view"
        android:layout_width="150dp"
        android:layout_height="150dp"
        app:outerColor="@color/colorPrimary"
        app:innerColor="@color/colorAccent"
        app:textColor="@color/colorPrimaryDark"
        app:borderWidget="5dp"
        app:textSize="18sp"
        android:background="@color/red"
       />

4、在自定义View中获取自定义属性

 		//获取attrs.xml中设置的属性
 		TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.QQStepView);
        mStepText = ta.getString(R.styleable.QQStepView_stepText);
        mOuterColor = ta.getColor(R.styleable.QQStepView_outerColor, mOuterColor);
        mInnerColor = ta.getColor(R.styleable.QQStepView_innerColor, mInnerColor);
        mBorderWidget = ta.getDimension(R.styleable.QQStepView_borderWidget, mBorderWidget);
        mTextSize = ta.getDimensionPixelSize(R.styleable.QQStepView_stepTextSize, sp2px(mTextSize));
        mTextColor = ta.getColor(R.styleable.QQStepView_stepTextColor, mTextColor);
        //回收
        ta.recycle();
        ///在此处将所有画笔进行初始化
         //创建画笔
        mOuterPaint = new Paint();
        //取消锯齿
        mOuterPaint.setAntiAlias(true);
        //设置颜色
        mOuterPaint.setColor(mOuterColor);
        //设置圆弧宽度
        mOuterPaint.setStrokeWidth(mBorderWidget);
        //设置圆弧Style
        mOuterPaint.setStyle(Paint.Style.STROKE);
        //设置起始,结束圆角
        mOuterPaint.setStrokeCap(Paint.Cap.ROUND);

        mInnerPaint = new Paint();
        //取消锯齿
        mOuterPaint.setAntiAlias(true);
        //设置颜色
        mInnerPaint.setColor(mInnerColor);
        //设置圆弧宽度
        mInnerPaint.setStrokeWidth(mBorderWidget);
        //设置圆弧Style
        mInnerPaint.setStyle(Paint.Style.STROKE);
        //设置起始,结束圆角
        mInnerPaint.setStrokeCap(Paint.Cap.ROUND);
        
        mTextPaint = new Paint();
        //抗锯齿
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setColor(mTextColor);

5、onMeasure 确定大小

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //在布局文件中,可能是wrap_content ,也可能宽高不一致

        //宽高不一致时,取最小值,确保是个正方形

        //获取宽高的模式
        int widgetMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        //获取宽高的大小
        int widgetSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        switch (MeasureSpec.getMode(widthMeasureSpec)) {

        }
        setMeasuredDimension(widgetSize > heightSize ? heightSize : widgetSize, widgetSize > heightSize ? heightSize : widgetSize);
    }

6、 画外圆弧,内圆弧,文字

6.1 画外圆弧(总进度)

遇到的问题: 边缘显示不全
原因: 描边的时候设置了画笔宽度mBorderWidget,所以导致边缘超出限制。
设置画布区域:

//因为区域超出画布,所以将画笔起始点从新设置。
 RectF rectF = new RectF(mBorderWidget / 2, mBorderWidget / 2, getWidth() - mBorderWidget / 2, getHeight() - mBorderWidget / 2);
//画圆弧 
//1、画布区域,2、起始角度,3、结束角度 4、是否填充 5、画笔
canvas.drawArc(rectF, mStartAngle, mEndAngle, false, mOuterPaint);

6.2. 画内圆弧(已走进度条)

//未设置最大值时,返回,不进行绘画。
if (mMaxStep == 0) return;
//计算百分比,用于绘画已走进度条;
float sweepAngle = (float) mStep / mMaxStep;
canvas.drawArc(rectF, mStartAngle, sweepAngle * mEndAngle, false, mInnerPaint);

6.3 画文本

		mStepText = mStep + "";
        //计算的宽度 与 字体的长度有关  与字体的大小  用画笔来测量
        //获取文本的Rect
		//mTextPaint.getTextBounds(mStepText, 0, mStepText.length(), mTextBounds);
		//获取文本宽度
		//mTextBounds.width();
        //获取文本宽度
        float textWidget =  mTextPaint.measureText(mStepText);
		//计算X起始位置
        int dx = (int) (getWidth() / 2 - textWidget / 2);
        //计算基线位置
        //dy代表的是:高度的一半到baseLine的距离
        //获取文字排版信息
        Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
        int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
        //基线
        int baseLine = getHeight() / 2 + dy;
        canvas.drawText(mStepText, dx, baseLine, mTextPaint);

7、其他 设置方法

7.1 设置最大值

 public synchronized void setMaxStep(int maxStep) {
        this.mMaxStep = maxStep;
    }

7.2 设置已走进度条

public synchronized void setStep(int step){
        this.mStep = step;
        //不断绘制
        invalidate();
    }

7.1 设置已走进度条,实现动画效果

 public synchronized void setAnimatorStep(int step){
        //属性动画
        ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, step);
        valueAnimator.setDuration(1000);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                float step = (float) valueAnimator.getAnimatedValue();
                setStep((int) step);
            }
        });
        valueAnimator.start();
    }

完整代码

/**
 * 仿QQ运动步数
 */
public class QQStepView extends View {
    private int mOuterColor = Color.RED;//总进度条颜色
    private int mInnerColor = Color.BLUE;//已走颜色
    private float mBorderWidget = 20;//圆弧宽度
    private int mTextSize = 20;//字体大小
    private String mStepText;
    private int mTextColor = Color.GREEN;//字体颜色
    private Paint mOuterPaint;//总画笔
    private Paint mInnerPaint;//已走画笔
    private Paint mTextPaint;//已走画笔
    private int mStartAngle = 135;
    private int mEndAngle = 270;
    private int mMaxStep = 2000;
    private int mStep = 1000;
    private Rect mTextBounds ;


    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);
        // 1 分析效果
        // 2 确定自定义属性,编写attrs.xml
        // 3 在布局中使用
        // 4 在自定义View中获取自定义属性


        // 7 其他
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.QQStepView);
        mStepText = ta.getString(R.styleable.QQStepView_stepText);
        mOuterColor = ta.getColor(R.styleable.QQStepView_outerColor, mOuterColor);
        mInnerColor = ta.getColor(R.styleable.QQStepView_innerColor, mInnerColor);
        mBorderWidget = ta.getDimension(R.styleable.QQStepView_borderWidget, mBorderWidget);
        mTextSize = ta.getDimensionPixelSize(R.styleable.QQStepView_stepTextSize, sp2px(mTextSize));
        mTextColor = ta.getColor(R.styleable.QQStepView_stepTextColor, mTextColor);
        //回收
        ta.recycle();

        //创建画笔
        mOuterPaint = new Paint();
        //取消锯齿
        mOuterPaint.setAntiAlias(true);
        //设置颜色
        mOuterPaint.setColor(mOuterColor);
        //设置圆弧宽度
        mOuterPaint.setStrokeWidth(mBorderWidget);
        //设置圆弧Style
        mOuterPaint.setStyle(Paint.Style.STROKE);
        //设置起始,结束圆角
        mOuterPaint.setStrokeCap(Paint.Cap.ROUND);

        mInnerPaint = new Paint();
        //取消锯齿
        mOuterPaint.setAntiAlias(true);
        //设置颜色
        mInnerPaint.setColor(mInnerColor);
        //设置圆弧宽度
        mInnerPaint.setStrokeWidth(mBorderWidget);
        //设置圆弧Style
        mInnerPaint.setStyle(Paint.Style.STROKE);
        //设置起始,结束圆角
        mInnerPaint.setStrokeCap(Paint.Cap.ROUND);


        mTextPaint = new Paint();
        //抗锯齿
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setColor(mTextColor);

        mTextBounds = new Rect();
    }

    /**
     * 将sp值转换为px值,保证文字大小不变
     *
     * @param spValue
     * @return
     */
    public static int sp2px(int spValue) {
        final float fontScale = BaseApplication.getInstance().getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }

    // 5 onMeasure
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //在布局文件中,可能是wrap_content ,也可能宽高不一致

        //高度不一致,取最大值,确保是个正方形

        //获取宽高的模式
        int widgetMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        //获取宽高的大小
        int widgetSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        switch (MeasureSpec.getMode(widthMeasureSpec)) {

        }
        setMeasuredDimension(widgetSize > heightSize ? heightSize : widgetSize, widgetSize > heightSize ? heightSize : widgetSize);
    }

    // 6 画外圆弧,内圆弧,文字
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //6.1 画外圆弧
        //问题:边缘显示不全   描边有宽度  mBorderWidget
        //获取圆点
        int center = getWidth() / 2;
        //获取半径
        int radius = (int) (center - mBorderWidget / 2);
        RectF rectF = new RectF(mBorderWidget / 2, mBorderWidget / 2, getWidth() - mBorderWidget / 2, getHeight() - mBorderWidget / 2);
        canvas.drawArc(rectF, mStartAngle, mEndAngle, false, mOuterPaint);
        //6.2 画内圆弧
        if (mMaxStep == 0) return;
        float sweepAngle = (float) mStep / mMaxStep;
        canvas.drawArc(rectF, mStartAngle, sweepAngle * mEndAngle, false, mInnerPaint);


        //6.3 画文本
        mStepText = mStep + "";
        //计算的宽度 与 字体的长度有关  与字体的大小  用画笔来测量
        //获取文本的Rect
//        mTextPaint.getTextBounds(mStepText, 0, mStepText.length(), mTextBounds);

        //获取文本宽度
        float textWidget =  mTextPaint.measureText(mStepText);

        int dx = (int) (getWidth() / 2 - textWidget / 2);

        //dy代表的是:高度的一半到baseLine的距离
        //获取文字排版信息
        Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
        int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
        //基线
        int baseLine = getHeight() / 2 + dy;
        canvas.drawText(mStepText, dx, baseLine, mTextPaint);
    }
    // 7 其他

    public synchronized void setMaxStep(int maxStep) {
        this.mMaxStep = maxStep;
    }
    public synchronized void setStep(int step){
        this.mStep = step;
        //不断绘制
        invalidate();
    }

    public synchronized void setAnimatorStep(int step){
        //属性动画
        ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, step);
        valueAnimator.setDuration(1000);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                float step = (float) valueAnimator.getAnimatedValue();
                setStep((int) step);
            }
        });
        valueAnimator.start();
    }
}


总结

提示:这里对文章进行总结:
以上就是今天要讲的内容,本文仅仅简单介绍了自定义仿QQ运动步数,扩展效果可自己再去实现。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值