美文网首页
计步器UI实现

计步器UI实现

作者: Lost_Robot | 来源:发表于2018-09-13 15:42 被阅读46次
目标UI
UI说明:

1.浅色的表示总共是100%
2.深蓝色的表示执行了75%
3.数字描述
4.单位描述

技术知识:

在Canvas中有一个画圆弧的方法

drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//画弧

参数一:是RectF对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧,
参数二:是起始角(度)在电弧的开始,圆弧起始角度,单位为度。
参数三:圆弧扫过的角度,顺时针方向,单位为度,从右中间开始为零度。
参数四:是如果是true(真)的话,在绘制圆弧时将圆心包括在内,通常用来绘制扇形;如果是false(假)这将是一个弧线。
参数五:是Paint对象;

![Canvas中的绘制方法](https://img.haomeiwen.com/i6317847/cf1a1bf6a18ccb19.png?
imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

实现步骤:

1.继承自View
在ActionRateView extends View中onMeasure确定View的宽高:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        final int minimumWidth = getSuggestedMinimumWidth();
//        final int minimumHeight = getSuggestedMinimumHeight();
        int width = getDefaultSize(minimumWidth, widthMeasureSpec);
        int height = width * 46 / 100;  //取宽度的46%作为高度
        setMeasuredDimension(widthMeasureSpec, height);
    }

2.onDraw绘制

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        float width = getWidth();
        float centerX = width / 2;
        float left = width * mMarginLeftRadius;
        float top = width * mMarginTopRadius;
        float right = width * (1 - mMarginLeftRadius);
        float bottom = top + (right - left);
        mCircularDiameter = right - left;
        mUnitOfDescriptionSize = width * mUnitOfDescriptionSizeRate;
        mDescriptionSize = width * mDescriptionSizeRate;

        RectF rectF = new RectF(left, top, right, bottom);  //计算绘制图形的区域大小
        /**【第一步】绘制小圆*/
        drawArcMiniCircle(canvas, rectF);
        /**【第二步】绘制当前进度的粗圆弧*/
        drawArcMaxRate(canvas, rectF);
        /**【第四步】绘制单位文字*/
        drawTextStepString(canvas, centerX);
        /**【第三步】绘制核卡率数字*/
        drawTextNumber(canvas, centerX);

    }

3.画小圆

 private void drawArcMiniCircle(Canvas canvas, RectF rectF) {

        Paint paint = new Paint();
        paint.setColor(getResources().getColor(R.color.colorMiniRate)); /** 默认画笔颜色 */
        /** 结合处为圆弧*/
//        paint.setStrokeJoin(Paint.Join.ROUND);
        /** 设置画笔的样式 Paint.Cap.Round ,Cap.SQUARE等分别为圆形、方形*/
//        paint.setStrokeCap(Paint.Cap.ROUND);
        /** 设置画笔的填充样式 Paint.Style.FILL  :填充内部;Paint.Style.FILL_AND_STROKE  :填充内部和描边;  Paint.Style.STROKE  :仅描边*/
        paint.setStyle(Paint.Style.STROKE);
        paint.setAntiAlias(true);/**抗锯齿功能*/
        paint.setStrokeWidth(mMiniRateWidth);/**设置画笔宽度*/
//        canvas.drawArc(rectF, mStartAngle, mAngleLength, false, paint);
        float radius = (rectF.bottom - rectF.top) / 2; //小圆半径
        canvas.drawCircle(rectF.centerX(), rectF.centerY(), radius, paint);//画圆

    }

4.画大圆弧

 private void drawArcMaxRate(Canvas canvas, RectF rectF) {
        Paint paintCurrent = new Paint();
        paintCurrent.setStrokeJoin(Paint.Join.ROUND);
        paintCurrent.setStrokeCap(Paint.Cap.ROUND);//圆角弧度
        paintCurrent.setStyle(Paint.Style.STROKE);//设置填充样式
        paintCurrent.setAntiAlias(true);//抗锯齿功能
        paintCurrent.setStrokeWidth(mMaxRateWidth);//设置画笔宽度
        paintCurrent.setColor(getResources().getColor(R.color.colorMaxRate));//设置画笔颜色

        /**绘制圆弧的方法
         * drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//画弧,
         参数一是RectF对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧,
         参数二是起始角(度)在电弧的开始,圆弧起始角度,单位为度。
         参数三圆弧扫过的角度,顺时针方向,单位为度,从右中间开始为零度。
         参数四是如果这是true(真)的话,在绘制圆弧时将圆心包括在内,通常用来绘制扇形;如果它是false(假)这将是一个弧线,
         参数五是Paint对象;
         */
        canvas.drawArc(rectF, mStartAngle, mCurrentAngleLength, false, paintCurrent);
    }

5.绘制文字单位

 private void drawTextStepString(Canvas canvas, float centerX) {
        Paint vTextPaint = new Paint();
//        vTextPaint.setTextSize(dipToPx(16));
        vTextPaint.setTextSize(mUnitOfDescriptionSize);
        vTextPaint.setTextAlign(Paint.Align.CENTER);
        vTextPaint.setAntiAlias(true);//抗锯齿功能
        vTextPaint.setColor(getResources().getColor(R.color.colorMaxRate));
        Rect bounds = new Rect();
        vTextPaint.getTextBounds(mUnitOfDescription, 0, mUnitOfDescription.length(), bounds);
        Rect bounds_Number = new Rect();
        vTextPaint.getTextBounds(mDescription, 0, mDescription.length(), bounds_Number);
        canvas.drawText(mUnitOfDescription, centerX, getHeight() / 2 + bounds.height() + bounds.height() / 2, vTextPaint);

    }

5.绘制数字


    private void drawTextNumber(Canvas canvas, float centerX) {

        Paint vTextPaint = new Paint();
        vTextPaint.setTextAlign(Paint.Align.CENTER);
        vTextPaint.setAntiAlias(true);//抗锯齿功能
        vTextPaint.setTextSize(mDescriptionSize);
        Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
        vTextPaint.setTypeface(font);//字体风格
        vTextPaint.setColor(getResources().getColor(R.color.colorMaxRate));
        Rect bounds_Number = new Rect();
        vTextPaint.getTextBounds(mDescription, 0, mDescription.length(), bounds_Number);
        canvas.drawText(mDescription, centerX, getHeight() / 2, vTextPaint);

    }

6.添加动画

public void setCurrentCount(int totalStepNum, int currentCounts) {
        mDescription = currentCounts + "";
        mDescriptionTemp = currentCounts;
        if (currentCounts > totalStepNum) {
            currentCounts = totalStepNum;
        }

        float scale = (float) currentCounts / totalStepNum;
//        /**换算成弧度最后要到达的角度的长度-->弧长*/
        float currentAngleLength = scale * mAngleLength;
        /**开始执行动画*/
        setAnimation(0, currentAngleLength, mAnimationTime);
    }

  private void setAnimation(float last, final float current, int length) {
        ValueAnimator progressAnimator = ValueAnimator.ofFloat(last, current);
        progressAnimator.setDuration(length);
        progressAnimator.setTarget(mCurrentAngleLength);
        progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mCurrentAngleLength = (float) animation.getAnimatedValue();
                float temp = mDescriptionTemp * (mCurrentAngleLength / current);
                mDescription = temp >= 10 ? temp >= 100 ? (temp + "").substring(0, 3) : (temp + "").substring(0, 2) : (temp + "").substring(0, 1);//String.valueOf(mCurrentAngleLength);
                invalidate();//重新绘制页面
            }

        });
        progressAnimator.start();
    }

7.在xml中添加自定义VIew

        <com.cmbc.creditcard.cmms.view.ActionRateView
            android:id="@+id/achievement_arv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toBottomOf="@+id/achievement_date_iv" />

8.代码中调用

 achievement_arv.setCurrentCount(100, 68)

完整代码:

package com.cmbc.creditcard.cmms.view;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import com.cmbc.creditcard.cmms.R;

/**
 * Created by Aqua on 2018-09-12.
 *
 * @author Isan
 * @version 3.2.1
 * @date 2018-09-12    09:47
 * @api 6
 */

public class ActionRateView extends View {


    private float mMiniRateWidth = 5f;  //细圆的宽度
    private float mMaxRateWidth = 20f;  //大圆的宽度
    private float mMaxTextSize = 12f;  //大字体的大小
    private float mMiniTextSize = 10f; //小字体的大小
    private float mCircularDiameter = 0.0f; //圆的直径
    private float mMarginTopRadius = 0.03f;//圆距离顶端和底端的距离3%
    private float mMarginLeftRadius = 0.3f;//圆距离顶端和底端的距离30%

    private float mStartAngle = 270; //圆开始的角度
    private float mAngleLength = 360; //整个原型的的夹角度
    private float mCurrentAngleLength = 0.0f;//外层圆的夹角

    private int mAnimationTime = 3000;//动画的时常


    private String mDescription = "0";
    private int mDescriptionTemp = 0;
    private String mUnitOfDescription = "核卡率(%)";
//    private String mUnitOfDescription = "XXXXX(%)";
    private float mUnitOfDescriptionSize = 24.0f;  //字体
    private float mUnitOfDescriptionSizeRate = 0.04f;  //字体与屏幕宽度的计算率
    private float mDescriptionSize = 32.0f;  //字体
    private float mDescriptionSizeRate = 0.12f;  //字体


    public ActionRateView(Context context) {
        super(context);
    }

    public ActionRateView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public ActionRateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        float width = getWidth();
        float centerX = width / 2;
        float left = width * mMarginLeftRadius;
        float top = width * mMarginTopRadius;
        float right = width * (1 - mMarginLeftRadius);
        float bottom = top + (right - left);
        mCircularDiameter = right - left;
        mUnitOfDescriptionSize = width * mUnitOfDescriptionSizeRate;
        mDescriptionSize = width * mDescriptionSizeRate;

        RectF rectF = new RectF(left, top, right, bottom);
        /**【第一步】绘制小圆*/
        drawArcMiniCircle(canvas, rectF);
        /**【第二步】绘制当前进度的粗圆弧*/
        drawArcMaxRate(canvas, rectF);
        /**【第四步】绘制单位文字*/
        drawTextStepString(canvas, centerX);
        /**【第三步】绘制XXX率数字*/
        drawTextNumber(canvas, centerX);

    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        final int minimumWidth = getSuggestedMinimumWidth();
//        final int minimumHeight = getSuggestedMinimumHeight();
        int width = getDefaultSize(minimumWidth, widthMeasureSpec);
        int height = width * 46 / 100;  //取宽度的46%作为高度
        setMeasuredDimension(widthMeasureSpec, height);
    }

    private void drawArcMiniCircle(Canvas canvas, RectF rectF) {

        Paint paint = new Paint();
        paint.setColor(getResources().getColor(R.color.colorMiniRate)); /** 默认画笔颜色 */
        /** 结合处为圆弧*/
//        paint.setStrokeJoin(Paint.Join.ROUND);
        /** 设置画笔的样式 Paint.Cap.Round ,Cap.SQUARE等分别为圆形、方形*/
//        paint.setStrokeCap(Paint.Cap.ROUND);
        /** 设置画笔的填充样式 Paint.Style.FILL  :填充内部;Paint.Style.FILL_AND_STROKE  :填充内部和描边;  Paint.Style.STROKE  :仅描边*/
        paint.setStyle(Paint.Style.STROKE);
        paint.setAntiAlias(true);/**抗锯齿功能*/
        paint.setStrokeWidth(mMiniRateWidth);/**设置画笔宽度*/
//        canvas.drawArc(rectF, mStartAngle, mAngleLength, false, paint);
        float radius = (rectF.bottom - rectF.top) / 2; //小圆半径
        canvas.drawCircle(rectF.centerX(), rectF.centerY(), radius, paint);//画圆

    }

    private void drawArcMaxRate(Canvas canvas, RectF rectF) {
        Paint paintCurrent = new Paint();
        paintCurrent.setStrokeJoin(Paint.Join.ROUND);
        paintCurrent.setStrokeCap(Paint.Cap.ROUND);//圆角弧度
        paintCurrent.setStyle(Paint.Style.STROKE);//设置填充样式
        paintCurrent.setAntiAlias(true);//抗锯齿功能
        paintCurrent.setStrokeWidth(mMaxRateWidth);//设置画笔宽度
        paintCurrent.setColor(getResources().getColor(R.color.colorMaxRate));//设置画笔颜色

        /**绘制圆弧的方法
         * drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//画弧,
         参数一是RectF对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧,
         参数二是起始角(度)在电弧的开始,圆弧起始角度,单位为度。
         参数三圆弧扫过的角度,顺时针方向,单位为度,从右中间开始为零度。
         参数四是如果这是true(真)的话,在绘制圆弧时将圆心包括在内,通常用来绘制扇形;如果它是false(假)这将是一个弧线,
         参数五是Paint对象;
         */
        canvas.drawArc(rectF, mStartAngle, mCurrentAngleLength, false, paintCurrent);
    }

    private void drawTextNumber(Canvas canvas, float centerX) {

        Paint vTextPaint = new Paint();
        vTextPaint.setTextAlign(Paint.Align.CENTER);
        vTextPaint.setAntiAlias(true);//抗锯齿功能
        vTextPaint.setTextSize(mDescriptionSize);
        Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
        vTextPaint.setTypeface(font);//字体风格
        vTextPaint.setColor(getResources().getColor(R.color.colorMaxRate));
        Rect bounds_Number = new Rect();
        vTextPaint.getTextBounds(mDescription, 0, mDescription.length(), bounds_Number);
        canvas.drawText(mDescription, centerX, getHeight() / 2, vTextPaint);

    }

    private void drawTextStepString(Canvas canvas, float centerX) {
        Paint vTextPaint = new Paint();
//        vTextPaint.setTextSize(dipToPx(16));
        vTextPaint.setTextSize(mUnitOfDescriptionSize);
        vTextPaint.setTextAlign(Paint.Align.CENTER);
        vTextPaint.setAntiAlias(true);//抗锯齿功能
        vTextPaint.setColor(getResources().getColor(R.color.colorMaxRate));
        Rect bounds = new Rect();
        vTextPaint.getTextBounds(mUnitOfDescription, 0, mUnitOfDescription.length(), bounds);
        Rect bounds_Number = new Rect();
        vTextPaint.getTextBounds(mDescription, 0, mDescription.length(), bounds_Number);
        canvas.drawText(mUnitOfDescription, centerX, getHeight() / 2 + bounds.height() + bounds.height() / 2, vTextPaint);

    }

    /**
     * 获取当前步数的数字的高度
     *
     * @param fontSize 字体大小
     * @return 字体高度
     */
    public int getFontHeight(float fontSize) {
        Paint paint = new Paint();
        paint.setTextSize(fontSize);
        Rect bounds_Number = new Rect();
        paint.getTextBounds(mDescription, 0, mDescription.length(), bounds_Number);
        return bounds_Number.height();
    }


    public void setCurrentCount(int totalNum, int currentCounts) {
        mDescription = currentCounts + "";
        mDescriptionTemp = currentCounts;
        if (currentCounts > totalNum) {
            currentCounts = totalNum;
        }

//        /**所走步数占用总共步数的百分比*/
        float scale = (float) currentCounts / totalNum;
//        /**换算成弧度最后要到达的角度的长度-->弧长*/
        float currentAngleLength = scale * mAngleLength;
        /**开始执行动画*/
        setAnimation(0, currentAngleLength, mAnimationTime);
    }

    /**
     * 为进度设置动画
     * ValueAnimator是整个属性动画机制当中最核心的一个类,属性动画的运行机制是通过不断地对值进行操作来实现的,
     * 而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。
     * 它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,
     * 我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,
     * 那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。
     *
     */
    private void setAnimation(float last, final float current, int length) {
        ValueAnimator progressAnimator = ValueAnimator.ofFloat(last, current);
        progressAnimator.setDuration(length);
        progressAnimator.setTarget(mCurrentAngleLength);
        progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mCurrentAngleLength = (float) animation.getAnimatedValue();
                float temp = mDescriptionTemp * (mCurrentAngleLength / current);
                mDescription = temp >= 10 ? temp >= 100 ? (temp + "").substring(0, 3) : (temp + "").substring(0, 2) : (temp + "").substring(0, 1);
                invalidate();//重新绘制页面
            }

        });
        progressAnimator.start();
    }

}

相关文章

  • 计步器UI实现

    UI说明: 1.浅色的表示总共是100%2.深蓝色的表示执行了75%3.数字描述4.单位描述 技术知识: 在Can...

  • 计步器 APP UI 绘制

    之间我绘制了一个计步器 APP的 UI, 但效果不是很理想,特别是字体部分......这次,我再次用「Sketch...

  • iOS计步器实现

    前几天写一个关于养生和医疗的一个项目,要写一个类似微信运动的计步器功能,只好先去研究一下计步器功能实现。 之前在我...

  • iOS 实现计步器功能

    现在越来越多的人关注运动和健康,iOS系统也在很早的时候就自带了健康APP,下面详细描述一下在我们开发中,怎么实现...

  • 基于三轴传感器的计步器代码实现-iOS

    原有iOS计步器采用系统框架swift语言调用 改为三轴传感器的计步器实现 开启陀螺仪采集三轴数据 计步核心 波峰...

  • 关于 Notification 的 TransactionToo

    最近做计步器,需要每隔五秒更新通知栏的ui, 考虑的性能问题,notification不是每次都new 然后再后台...

  • android 计步器实现原理操作

    我们经常会看到微信 QQ 以及其他一些运动app里面都有一个计步功能,那它是怎么实现的呢? 今天我们就来实现一下,...

  • iOS通过CMPedometer实现计步器

    CMPedometer是iOS8以后推出的CoreMotion框架中用于获取用户运动信息的类,我们可以获取到用户活...

  • 计步器

    http://mp.weixin.qq.com/s/paZHmJp9-YP-Gn29UKicQg

  • 计步器

    微信中的运动步数统计相信大家都见过,那么这是怎么实现的呢 在iOS7之前是通过CMStepCounter来实现的 ...

网友评论

      本文标题:计步器UI实现

      本文链接:https://www.haomeiwen.com/subject/atmmgftx.html