博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android自定义View仿QQ计步器
阅读量:4078 次
发布时间:2019-05-25

本文共 8405 字,大约阅读时间需要 28 分钟。

自定义View仿QQ计步器

自定义计步器

Android自定义View是Android开发中比较重要的一项,也是很多开发者比较怕的一个东西。其实只要认真去学习,自定义View其实没有那么可怕;相反的,我们还能从自定义View中找到很多乐趣。

自定义一个计步器分析:

1、首先需要画一个固定不动的大圆弧
2、其次需要画一个跟着步数变化的小圆弧
3、最后画圆弧中间的步数显示的文字

实现效果图

在这里插入图片描述

自定义属性

首先我们在values目录下新建attrs.xml文件;

然后在里面自定义属性:

创建StepView

创建我们的StepView,继承自View并重写构造方法。

public class StepView extends View {    public StepView(Context context) {        this(context, null);    }    public StepView(Context context, @Nullable AttributeSet attrs) {        this(context, attrs, 0);    }    public StepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    } }

这里在前两个构造方法中调this,保证使用每个构造方法初始化都能走到第三个构造方法。

在View中获取属性

创建好View,重写构造方法以后就要在View中获取我们的自定义属性:

//获取自定义属性        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.StepView);        mOutColor = array.getColor(R.styleable.StepView_outColor, Color.GREEN);        mInnerColor = array.getColor(R.styleable.StepView_innerColor, Color.BLUE);        mBorderWidth = (int) array.getDimension(R.styleable.StepView_borderWidth, 10);        mStepTextSize = array.getDimensionPixelSize(R.styleable.StepView_stepTextSize, 20);        mStepTextColor = array.getColor(R.styleable.StepView_stepTextColor, Color.RED);        array.recycle();

重写onMeasure()方法

然后需要重写我们的onMeasure方法,这个方法是测量并设置View的宽高,因为我们这个View比较简单,只需要保证是宽高一致,所以只需要获取我们的宽高,然后设置就行。

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

重写onDraw()方法

onDraw方法就是绘制View的内容,这里我们要分三步:

1、绘制大圆弧

首先绘制固定的大圆弧:

//画外圆弧        int center = getWidth() / 2;        int radius = getWidth() / 2 - mBorderWidth;        int left = center - radius;        int top = center - radius;        int right = center + radius;        int bottom = center + radius;        mRectF.set(left, top, right, bottom);        canvas.drawArc(mRectF, START_ANGLE, TOTAL_ANGLE, false, mPaintOuter);

首先要定义一个矩形区域mRectF,然后设置四个边界值。center就是圆心位置,radius就是半径,这里要减去圆弧的宽度。

然后调用drawArc(),这个方法主要有四个参数:
oval : 生成椭圆的矩形
startAngle : 弧开始的角度 (X轴正方向为0度,顺时针弧度增大)
sweepAngle : 绘制多少弧度 (注意不是结束弧度)
useCenter : 是否有弧的两边 true有两边 false无两边

2、绘制小圆弧

小圆弧是会跟着当前的步数动态变化的,所以我们要根据步数计算:

//画内圆弧        float sweepAngle = (float) mCurrentStepCount / mMaxStepCount;        canvas.drawArc(mRectF, START_ANGLE, sweepAngle * TOTAL_ANGLE, false, mPaintInner);

计算完成同样是调用drawArc()绘制圆弧。

3、绘制文字

接下来就是绘制显示在圆弧中心的文字就是我们的当前步数:

//画文字        if (mCurrentStepCount == 0) {            return;        }        String currentStepText = String.valueOf(mCurrentStepCount);        mPaintText.getTextBounds(currentStepText, 0, currentStepText.length(), mBounds);        int startX = getWidth() / 2 - mBounds.width() / 2;        Paint.FontMetricsInt fm = mPaintText.getFontMetricsInt();        int dy = (fm.bottom - fm.top) / 2 - fm.descent;        int baseline = getHeight() / 2 + dy;        canvas.drawText(currentStepText, startX, baseline, mPaintText);

首先根据画笔去测量文字的边界,然后计算开始的x坐标和baseline,最后调用drawText()绘制;

这里不知道怎么计算的可以转到另一篇博客,,里面有详细介绍计算推理过程。

在xml中使用

最后就是在xml中使用我们的自定义View,要让它动起来,还需要借助我们的属性动画,动态去改变当前的步数值,然后去重绘。

这里贴一下xml中使用代码:

然后是在activity使用:

public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        StepView stepView = findViewById(R.id.stepView);        stepView.setMaxStep(3000);        ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0,1800);        valueAnimator.setDuration(2000);        valueAnimator.setInterpolator(new DecelerateInterpolator());        valueAnimator.addUpdateListener(animation -> {            float animatedValue = (float) animation.getAnimatedValue();            stepView.setStep((int) animatedValue);        });        valueAnimator.start();    }}

StepView类全部代码

package com.px.test.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.util.AttributeSet;import android.view.View;import com.px.test.R;import androidx.annotation.Nullable;public class StepView extends View {    private static final String TAG = "StepView";    private static final int START_ANGLE = 135;    private static final int TOTAL_ANGLE = 270;    private int mOutColor;    private int mInnerColor;    private int mBorderWidth;    private int mStepTextSize;    private int mStepTextColor;    private Paint mPaintOuter;    private RectF mRectF;    private Paint mPaintInner;    private Paint mPaintText;    private Rect mBounds;    private int mMaxStepCount;    private int mCurrentStepCount;    public StepView(Context context) {        this(context, null);    }    public StepView(Context context, @Nullable AttributeSet attrs) {        this(context, attrs, 0);    }    public StepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        //获取自定义属性        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.StepView);        mOutColor = array.getColor(R.styleable.StepView_outColor, Color.GREEN);        mInnerColor = array.getColor(R.styleable.StepView_innerColor, Color.BLUE);        mBorderWidth = (int) array.getDimension(R.styleable.StepView_borderWidth, 10);        mStepTextSize = array.getDimensionPixelSize(R.styleable.StepView_stepTextSize, 20);        mStepTextColor = array.getColor(R.styleable.StepView_stepTextColor, Color.RED);        array.recycle();        mPaintOuter = new Paint();        mPaintOuter.setAntiAlias(true);        mPaintOuter.setColor(mOutColor);        mPaintOuter.setStrokeWidth(mBorderWidth);        mPaintOuter.setStrokeCap(Paint.Cap.ROUND);        mPaintOuter.setStyle(Paint.Style.STROKE);        mPaintInner = new Paint();        mPaintInner.setAntiAlias(true);        mPaintInner.setColor(mInnerColor);        mPaintInner.setStrokeWidth(mBorderWidth);        mPaintInner.setStrokeCap(Paint.Cap.ROUND);        mPaintInner.setStyle(Paint.Style.STROKE);        mPaintText = new Paint();        mPaintText.setAntiAlias(true);        mPaintText.setColor(mStepTextColor);        mPaintText.setTextSize(mStepTextSize);        mBounds = new Rect();        mRectF = new RectF();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int width = MeasureSpec.getSize(widthMeasureSpec);        int height = MeasureSpec.getSize(heightMeasureSpec);        setMeasuredDimension(Math.min(width, height), Math.min(width, height));    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        //画外圆弧        int center = getWidth() / 2;        int radius = getWidth() / 2 - mBorderWidth;        int left = center - radius;        int top = center - radius;        int right = center + radius;        int bottom = center + radius;        mRectF.set(left, top, right, bottom);        canvas.drawArc(mRectF, START_ANGLE, TOTAL_ANGLE, false, mPaintOuter);        //画内圆弧        float sweepAngle = (float) mCurrentStepCount / mMaxStepCount;        canvas.drawArc(mRectF, START_ANGLE, sweepAngle * TOTAL_ANGLE, false, mPaintInner);        //画文字        if (mCurrentStepCount == 0) {            return;        }        String currentStepText = String.valueOf(mCurrentStepCount);        mPaintText.getTextBounds(currentStepText, 0, currentStepText.length(), mBounds);        int startX = getWidth() / 2 - mBounds.width() / 2;        Paint.FontMetricsInt fm = mPaintText.getFontMetricsInt();        int dy = (fm.bottom - fm.top) / 2 - fm.descent;        int baseline = getHeight() / 2 + dy;        canvas.drawText(currentStepText, startX, baseline, mPaintText);    }    public void setMaxStep(int maxStepCount) {        mMaxStepCount = maxStepCount;    }    public void setStep(int currentStepCount) {        mCurrentStepCount = currentStepCount;        invalidate();    }}

整个实现过程并不复杂,有时候有些事情并没有我们想的那么难,只要我们去做了,就会发现其实就那样。

完整工程文件下载

转载地址:http://jzsni.baihongyu.com/

你可能感兴趣的文章
Ubuntu修改host遇到unable to resolve host
查看>>
路由选择算法
查看>>
Objective-C 基础入门(一)
查看>>
Objective-C 基础入门(三) 读写文件与回调
查看>>
C++ STL标准库与泛型编程(一)概述
查看>>
C++ STL标准库与泛型编程(四)Deque、Queue、Stack 深度探索
查看>>
C++ STL标准库 算法
查看>>
JVM内存模型_Minor GC笔记
查看>>
SpringCloud学习之PassCloud——(一)PassCloud源代码下载
查看>>
Linux下安装Python环境并部署NLP项目
查看>>
Nginx篇-springCloud配置Gateway+Nginx进行反向代理和负载均衡
查看>>
Nginx篇-Nginx配置动静分离
查看>>
缓存篇-Redis缓存失效以及解决方案
查看>>
缓存篇-使用Redis进行分布式锁应用
查看>>
缓存篇-Redisson的使用
查看>>
phpquery抓取网站内容简单介绍
查看>>
找工作准备的方向(4月22日写的)
查看>>
关于fwrite写入文件后打开查看是乱码的问题
查看>>
用结构体指针前必须要用malloc,不然会出现段错误
查看>>
Linux系统中的美
查看>>