Android自定义简单仿QQ运动步数进展圆环

实现效果主要效果分为三个部分:
1.固定蓝色的大圆弧 color borderWidth
2.可以变化的小圆弧(红色) color borderWidth
3.中间的步数文字 color textSize
在这里插入图片描述

drawArc方法
startAngle 确定角度的起始位置
sweepAngle 确定扫过的角度
useCenter 是否使用中心:true,连接矩形中心及弧;false不显示,(是否显示半径连线,true表示显示圆弧与圆心的半径连线,false表示不显示)(Paint.Style.FILL时)连接弧的起点终点

    public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
            @NonNull Paint paint) {
        super.drawArc(oval, startAngle, sweepAngle, useCenter, paint);
    }

在这里插入图片描述
顺时针,起点从135度开始扫了270度的距离到终点

在这里插入图片描述
左边距离=控件宽度的一半减去文字宽度的一半
具体代码

public class QQStepView extends View {

    private int mOuterColor = Color.RED;
    private int mInnerColor = Color.BLUE;
    private int mBorderWidth = 20;//20px
    private int mStepTextSize;
    private int mStepTextColor;
    private Paint mOutPaint, mInnerPaint, mTextPaint;

    //总共的,当前的步数
    private int mStepMax = 100;//默认值
    private int mCurrentStep = 50;

    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中获取自定义属性
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.QQStepView);
        mOuterColor = array.getColor(R.styleable.QQStepView_outerColor, mOuterColor);
        mInnerColor = array.getColor(R.styleable.QQStepView_innerColor, mInnerColor);
        mBorderWidth = (int) array.getDimension(R.styleable.QQStepView_borderWidth, mBorderWidth);
        mStepTextSize = array.getDimensionPixelSize(R.styleable.QQStepView_stepTextSize, mStepTextSize);
        mStepTextColor = array.getColor(R.styleable.QQStepView_stepTextColor, mStepTextColor);
        array.recycle();
        mOutPaint = new Paint();
        mOutPaint.setAntiAlias(true);
        mOutPaint.setStrokeWidth(mBorderWidth);
        mOutPaint.setColor(mOuterColor);
        mOutPaint.setStrokeCap(Paint.Cap.ROUND);//设置头部和尾部为圆形
        mOutPaint.setStyle(Paint.Style.STROKE);//空心

        mInnerPaint = new Paint();
        mInnerPaint.setAntiAlias(true);
        mInnerPaint.setStrokeWidth(mBorderWidth);
        mInnerPaint.setColor(mInnerColor);
        mInnerPaint.setStrokeCap(Paint.Cap.ROUND);//设置头部和尾部为圆形
        mInnerPaint.setStyle(Paint.Style.STROKE);//空心

        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(mStepTextColor);
        mTextPaint.setTextSize(mStepTextSize);
        //7.其他
    }

    //5.onMeasure
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //调用者在布局文件中可能 wrap_content,这个时候你可以去判断模式如果是AT-MOST去设置一个默认值,或者抛出异常让用户不可以设置wrap_content
        //这里我默认设置为100dp
        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) {
            width = dip2px(getContext(), 100);
        }
        if (heightMode == MeasureSpec.AT_MOST) {
            height = dip2px(getContext(),100);
        }
        //获取模式 AT_MOST 40dp
        //宽高不一致 取最小值 确保是个正方形,正方向才好画圆
        setMeasuredDimension(width > height ? height : width, width > height ? height : width);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //6.画外圆弧,
        //区域
        //getWidth和getHeight是获取自定义View的宽和高在onMeasure执行完后才能调用
        int center = getWidth() / 2;
        int radius = center - mBorderWidth / 2;//mBorderWidth如果不除与2的话也行
        //center - radius和center + radius是考虑描边宽度的问题,如果直接是用右边getWidth和下边getHeight,左边和上边为0的话,这样画出来的圆弧会出边界
        //因此我们要考虑描边宽度的问题,让他小一点,画的时候不要出界。
        RectF rectF = new RectF(center - radius, center - radius, center + radius, center + radius);
        //起始角度135到270顺时针扫
        canvas.drawArc(rectF, 135, 270, false, mOutPaint);
        //7.画内圆弧,
        if (mStepMax == 0) {
            //如果没设置总步数就不让他往下执行
            return;
        }
        //不能向外圆弧一样写死  百分比 是使用者设置的从外面传
        float sweepAngle = (float) mCurrentStep / mStepMax;
        canvas.drawArc(rectF, 135, 270 * sweepAngle, false, mInnerPaint);

        //8.画文字
        String stepText = mCurrentStep + "";
        Rect textBounds = new Rect();
        //文字的宽度需要画笔去测量
        mTextPaint.getTextBounds(stepText, 0, stepText.length(), textBounds);
        int dx = getWidth() / 2 - textBounds.width() / 2;//左边距离=控件宽度的一半减去文字宽度的一半
        //基线
        Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
        int dy = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
        int baseLine = getHeight() / 2 + dy;
        canvas.drawText(stepText, dx, baseLine, mTextPaint);
    }

    //7.其他 写几个方法动起来
    public void setStepMax(int stepMax) {
        this.mStepMax = stepMax;
    }

    public void setCurrentStep(int currentStep) {
        this.mCurrentStep = currentStep;
        //不断绘制  调用onDraw
        invalidate();
    }
}

布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="16dp"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    xmlns:yiran="http://schemas.android.com/apk/res-auto">

<com.example.customview.customview.qqstep.QQStepView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/step_view"
    yiran:outerColor="@color/mainColor"
    yiran:innerColor="@color/purple_700"
    yiran:borderWidth="6dp"
    yiran:stepTextColor="@color/purple_700"
    yiran:stepTextSize="16sp"
    />
</LinearLayout>

使用

public class ViewActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_qqstepview);
        QQStepView qqStepView=(QQStepView) findViewById(R.id.step_view);
        qqStepView.setStepMax(4000);//设置最大值4000
        //属性动画
        //1秒钟从0变化到3000
        ValueAnimator valueAnimator= ObjectAnimator.ofFloat(0,3000);
        valueAnimator.setDuration(1000);
        valueAnimator.setInterpolator(new DecelerateInterpolator());//设置动画效果前面快后面慢一点
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float currentStep=(float) animation.getAnimatedValue();
                qqStepView.setCurrentStep((int) currentStep);
            }
        });
        valueAnimator.start();
    }
}

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

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值