Android开发中自定义圆盘中的加速器的表盘

话不多说先上最终的效果图:

本篇文章主要介绍怎么自定义一个带刻度的圆盘,主要包含绘制内外圆环以及刻度的绘制。具体的实现如下:

package com.dz.common.view;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RadialGradient;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import androidx.annotation.Nullable;

import com.dz.common.utils.LogeUtils;

/**
 * @content:注意:因为是在固定开发板用,没怎么考虑适配情况.
 *  * 1.表盘大小是根据控件xml布局文件的width决定
 *  * 2.控件宽度太小可能刻度文字显示不全,可以看情况调整下文字大小.
 */
public class DashboardView extends View {
    private static final String TAG = "DashboardView";
    private Paint arcPaint;
    //圆环的角度
    private int SWEEPANGLE = 280;
    //刻度画笔
    private Paint pointerPaint;

    private Context mContext;

    //圆环半径
    private int mRadius;
    //圆环的宽度
    int arcW = 30;
    //发光的宽度
    int gleamyArcW = arcW * 3;
    //刻度宽度
    int minScalew = 5;
    int maxScalew = 5;
    //刻度的长度
    int maxScaleLength =5;
    int minScaleLength = 30;
    private Paint gleamyArcPaint;
    //发光圆环的半径
    private int mRadiusG;
    //阴影宽度
    private int shade_w = 40;

    private Path pointerPath;

    private float currentDegree = 0;
    //指针当前的角度.
    private int startAngele = 90 + (360 - SWEEPANGLE) / 2;
    //仪表盘显示的数字
    private String speed = "0";
    private Paint mTextPaint;
    private Paint mPaint;
    private ValueAnimator mAnim;

    public DashboardView(Context context) {
        super(context);
        init(context);
    }


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

    private void init(Context context) {
        mContext = context;
        // 关闭硬件加速
        setLayerType(LAYER_TYPE_SOFTWARE, null);

        //外层动态圆环画笔
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);//画线模式
        mPaint.setStrokeWidth(arcW);//线宽度
        mPaint.setAntiAlias(true);

        //圆环画笔
        arcPaint = new Paint();
        arcPaint.setStyle(Paint.Style.STROKE);//画线模式
        arcPaint.setStrokeWidth(arcW);//线宽度
        arcPaint.setColor(Color.parseColor("#518CFF"));
        arcPaint.setAlpha(80);
        arcPaint.setAntiAlias(true);
        //刻度
        pointerPaint = new Paint();
        pointerPaint.setAntiAlias(true);
        pointerPaint.setColor(Color.parseColor("#26396F"));
        pointerPaint.setTextSize(40);
        pointerPaint.setTextAlign(Paint.Align.RIGHT);

        pointerPath = new Path();

        //发光圆环
        gleamyArcPaint = new Paint();
        gleamyArcPaint.setAntiAlias(true);
        gleamyArcPaint.setStyle(Paint.Style.STROKE);
        gleamyArcPaint.setStrokeWidth(gleamyArcW);
        //文字
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(Color.WHITE);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        mTextPaint.setTextSize(60);
    }

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

        mRadius = (int) (getMeasuredWidth() / 2)-30;
        mRadiusG = mRadius - gleamyArcW / 2;
        shade_w = (int) (mRadius * 0.4);
        //Log.i(TAG, "onDraw: mRadius"+mRadius+"mRadiusG:"+mRadiusG+"shade_w:"+shade_w);

        //Log.i(TAG, "onDraw1: w:"+getMeasuredWidth()+"h:"+getMeasuredHeight());
        //Log.i(TAG, "onDraw2: w:"+getWidth()+"h:"+getHeight());
        //最外层白色动态圆环
        drawDynamicArcs(canvas);
        //圆环
        drawArcs(canvas);
        //刻度
        drawDegree(canvas);
    }

    private void drawDynamicArcs(Canvas canvas) {
        //半径
//        int dyRaduis = (int) (getMeasuredWidth() / 2 * 0.88);
        int dyRaduis = mRadius;
        int w = 0;
        int[] colorSweep = new int[]{Color.parseColor("#518CFF"), Color.parseColor("#518CFF")};
//        int[] colorSweep = new int[]{Color.parseColor("#518CFF"), Color.parseColor("#518CFF")};
        float[] position = new float[]{0f, 0.5f};
        SweepGradient mShader = new SweepGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, colorSweep, position);

        //旋转渐变
        Matrix matrix = new Matrix();
        matrix.setRotate(startAngele, canvas.getWidth() / 2, canvas.getHeight() / 2);
        mShader.setLocalMatrix(matrix);
        mPaint.setShader(mShader);
        //arcPaint.setStyle(Paint.Style.STROKE);
//        mPaint.setStrokeWidth(w);
        RectF rectF = new RectF();
        rectF.left = (float) (getMeasuredWidth() / 2 - (dyRaduis - w / 2));
        rectF.top = (float) (getMeasuredHeight() / 2 - (dyRaduis - w / 2));
        rectF.right = (float) (getMeasuredWidth() / 2 + (dyRaduis - w / 2));
        rectF.bottom = (float) (getMeasuredHeight() / 2 + (dyRaduis - w / 2));
        canvas.drawArc(rectF, 90 + (360 - SWEEPANGLE) / 2, currentDegree, false, mPaint);
    }
    int clockPointNum = 40;
    private void drawDegree(Canvas canvas) {
        pointerPaint.setColor(Color.parseColor("#ffffff"));
        pointerPaint.setTextSize(40);
        pointerPaint.clearShadowLayer();
        pointerPaint.setTextAlign(Paint.Align.RIGHT);

        canvas.save();
        //原点移到空间中心点
        canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);

        canvas.rotate((360 - SWEEPANGLE) / 2 + 90);//(360-240)/2+90;
        //设置刻度文字颜色大小.
        mTextPaint.reset();
        mTextPaint.setColor(Color.parseColor("#ffffff"));
        mTextPaint.setTextSize(20);
        mTextPaint.clearShadowLayer();
        mTextPaint.setTextAlign(Paint.Align.RIGHT);
        mTextPaint.setAntiAlias(true);
        for (int i = 0; i < clockPointNum; i++) {
            if (i % 4 == 0) {     //长表针
                pointerPaint.setStrokeWidth(maxScalew);
                canvas.drawLine(mRadiusG + arcW, maxScalew / 2, mRadiusG+maxScaleLength, maxScalew / 2, pointerPaint);

                drawPointerText(canvas, mRadiusG - arcW, maxScalew / 2, i);

            } else {    //短表针
                pointerPaint.setStrokeWidth(minScalew);
//                canvas.drawLine(mRadiusG - arcW, maxScalew / 2, mRadiusG - minScaleLength, maxScalew / 2, pointerPaint);
            }

            canvas.rotate((float) SWEEPANGLE / (float) clockPointNum);
            // Log.i(TAG, "onDraw: "+i+"---"+(float) SWEEPANGLE/ (float) clockPointNum);
        }
        //最后一根
        canvas.drawLine(mRadiusG +arcW, -maxScalew / 2, mRadiusG+ maxScaleLength, -maxScalew / 2, pointerPaint);

        drawPointerText(canvas, mRadiusG - arcW, maxScalew / 2, 40);
        canvas.restore();
        count = 0;
    }

    int count = 0;

    private void drawPointerText(Canvas canvas, int x, int y, int i) {
        //动态设置刻度文字颜色。
        float a = (float) SWEEPANGLE / (float) clockPointNum;

        if (currentDegree > a * i)
            mTextPaint.setColor(Color.WHITE);
        else
            mTextPaint.setColor(Color.parseColor("#ffffff"));

        if (i != 0)
            count += 10;
        //canvas.drawText(String.valueOf(count), x - maxScaleLength, y, mTextPaint);

        //保存状态
        canvas.save();

        canvas.translate(mRadiusG - maxScaleLength, y);
        // 90 + (360 - SWEEPANGLE) / 2 = 130
        canvas.rotate(-(130f + a * i));

        String str = String.valueOf(count);

        //文字宽
        float text_w = mTextPaint.measureText(str);
        //文字baseline在y轴方向的位置
        float baseLineY = Math.abs(mTextPaint.ascent() + mTextPaint.descent()) / 2;
        if (i >= 0 && i <= 8) {
            mTextPaint.setTextAlign(Paint.Align.LEFT);
        } else if (i > 8 && i <= 20) {
            mTextPaint.setTextAlign(Paint.Align.CENTER);

            baseLineY = baseLineY;
        } else if (i > 20) {
            mTextPaint.setTextAlign(Paint.Align.RIGHT);
        }
        mTextPaint.setTextSize(30);
        canvas.drawText(String.valueOf(count/10), 5, baseLineY, mTextPaint);
        //恢复对canvas操作
        canvas.restore();

 /*刻度数字 水平改弧形 替换canvas.save() -- canvas.restore()之间的内容;
       //保存状态
        canvas.save();

        canvas.translate(mRadiusG - maxScaleLength, y);
        canvas.rotate(90);

        mTextPaint.setTextAlign(Paint.Align.CENTER);
        canvas.drawText(String.valueOf(count), 0, 40, mTextPaint);
        //恢复对canvas操作
        canvas.restore();*/
    }

    private void drawArcs(Canvas canvas) {
//        RectF rectF = new RectF(getMeasuredWidth() / 2 - mRadius, getMeasuredHeight() / 2 - mRadius,
//                getMeasuredWidth() / 2 + mRadius, getMeasuredHeight() / 2 + mRadius);
        RectF rectF = new RectF(getMeasuredWidth() / 2 - mRadius, getMeasuredHeight() / 2 - mRadius,
                getMeasuredWidth() / 2 + mRadius, getMeasuredHeight() / 2 + mRadius);

        canvas.drawArc(rectF, 90 + (360 - SWEEPANGLE) / 2, SWEEPANGLE, false, arcPaint);
    }


    /**
     * 外部更新刻度.
     *
     * @param speedssss .
     */
    public void udDataSpeed(int speedssss) {
        float a = SWEEPANGLE / 180f;
        if (speedssss < 0) throw new IllegalArgumentException("----speed不能小于0----");
        speed = String.valueOf(speedssss);
        //Log.i(TAG, "udDataSpeed: \ncurrentDegree:"+currentDegree+"\na:"+a+"\nspeedssss:"+speedssss);
        startAnimation(currentDegree, (float) speedssss * a);
    }

    //指针+阴影偏移动画.
    private void startAnimation(float start, float end) {
        if (mAnim != null) {
            if (mAnim.isRunning() || mAnim.isStarted()) {
                mAnim.cancel();
                mAnim.removeAllUpdateListeners();
            }
            boolean running = mAnim.isRunning();
            boolean started = mAnim.isStarted();
            Log.i(TAG, "startAnimation: running:" + running + "--started" + started);
        }
        mAnim = ValueAnimator.ofFloat(start, end);
        //anim.setRepeatCount(ValueAnimator.INFINITE);//设置无限重复
        //anim.setRepeatMode(ValueAnimator.REVERSE);//设置重复模式
        mAnim.setDuration(500);
        mAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                float value = (float) mAnim.getAnimatedValue();
                //Log.i(TAG, "onAnimationUpdate: " + value);
                currentDegree = value;
                invalidate();
            }
        });
        mAnim.start();
    }

    /**
     * 退出动画.
     */
    public void closeAnimation() {
        if (mAnim != null) {
            mAnim.cancel();
            mAnim.removeAllUpdateListeners();
        }
    }
}

在项目中具体的使用如下:

在xml文件中的布局
<com.dz.common.view.DashboardView
    android:id="@+id/scal_blu"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:layout_marginTop="20dp"
    android:layout_gravity="center">

在应用的类里面具体使用如下:

scal_blu.udDataSpeed(50);       //设置指针最终位置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/140296.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在报错中学python something

这里写目录标题 动手学深度学习pandas完整代码数据处理TypeError: can only concatenate str (not "int") to str&#xff08;fillna填补缺失值&#xff09; 创建文件夹学习这个数据分组get_dummies实现one hot encode 动手学深度学习pandas完整代码 import osimpor…

Camtasia2024全新中文版电脑录屏工具

在这个视频的大舞台上&#xff0c;每一帧都是你炫耀的机会&#xff0c;每一秒都是让观众瞪大眼睛的瞬间。现在&#xff0c;让我们一起飞跃时空&#xff0c;用更少的时间创作更多的惊喜吧&#xff01; 就算你是个小白&#xff0c;毫无经验&#xff0c;别担心&#xff0c;Camtas…

记录C# WinForm项目调用Rust生成的dll库

一、开发环境 1.RustRover (version&#xff1a;2023.3 EAP) 2.Visual Studio 2019 (version&#xff1a;16.11.30) 3.Windows 10 64位 OS 4.WinR&#xff1a;控制台程序&#xff0c;cmd.exe 二、使用RustRover编译Rust脚本为dll 1.下载安装Rust&#xff0c;https://www.…

Vue3封装自定义指令+h()

官方install介绍 directive/myDir/index.js 定义指令 import { h, render, ref } from "vue"; const vMyDir {mounted(el, binding) {renderElement(el, binding);}, }; // inserted是Vue2的生命周期钩子&#xff0c;所以在Vue3项目中要使用mounted const renderEl…

Android Rxjava架构原理与使用的详解解答

简单介绍 Rxjava这个名字&#xff0c;其中java代表java语言&#xff0c;而Rx是什么意思呢&#xff1f;Rx是Reactive Extensions的简写&#xff0c;翻译过来就是&#xff0c;响应式拓展。所以Rxjava的名字的含义就是&#xff0c;对java语言的拓展&#xff0c;让其可以实现对数据…

从头开始的卷积神经网络

VGG-16 卷积神经网络。来源&#xff1a;LearnOpenCV 参考资料&#xff1a;这篇文章可以在 Kaggle Notebook &#x1f9e0; Convolutional Neural Network From Scratch上更好地阅读。路易斯费尔南多托雷斯 一、说明 本文详细介绍在tf2.0上&#xff0c;使用ceras实现基本的神经…

「NLP+网安」相关顶级会议期刊 投稿注意事项+会议等级+DDL+提交格式

「NLP网安」相关顶级会议&期刊投稿注意事项 写在最前面一、会议ACL (The Annual Meeting of the Association for Computational Linguistics)IH&MMSec (The ACM Workshop on Information Hiding, Multimedia and Security)CCS (The ACM Conference on Computer and Co…

16 _ 二分查找(下):如何快速定位IP对应的省份地址?

通过IP地址来查找IP归属地的功能,不知道你有没有用过?没用过也没关系,你现在可以打开百度,在搜索框里随便输一个IP地址,就会看到它的归属地。 这个功能并不复杂,它是通过维护一个很大的IP地址库来实现的。地址库中包括IP地址范围和归属地的对应关系。 当我们想要查询202…

Golang源码分析 | 程序引导过程

环境说明 CentOS Linux release 7.2 (Final&#xff09; go version go1.16.3 linux/amd64 GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7使用gdb查看程序入口 编写一个简单的go程序 // main.go package mainfunc main() {print("Hello world") } 编译go …

Python大神用的贼溜的九个技巧,超级实用~

文章目录 一、整理字符串输入二、迭代器&#xff08;切片&#xff09;三、跳过可对对象的开头四、只包含关键字参数的函数 (kwargs)五、创建支持「with」语句的对象六、用「slots」节省内存七、限制「CPU」和内存使用量八、控制可以/不可以导入什么九、实现比较运算符的简单方法…

js获取当前日期与7天后的日期

调用 console.log(this.getSectionData(7))结果 函数 getSectionData(section) {const now new Date()const nowYear now.getFullYear()const nowMonth now.getMonth() 1 < 10 ? (0 (now.getMonth() 1)) : (now.getMonth() 1)const nowDay now.getDate() < 1…

Git 分支设计规范

开篇 这篇文章分享 Git 分支设计规范&#xff0c;目的是提供给研发人员做参考。 规范是死的&#xff0c;人是活的&#xff0c;希望自己定的规范&#xff0c;不要被打脸。 在说 Git 分支规范之前&#xff0c;先说下在系统开发过程中常用的环境。 DEV 环境&#xff1a;用于开发…

高可用架构设计

1. 引言 软件系统有三个追求&#xff1a;高性能、高并发、高可用&#xff0c;俗称三高。三者既有区别也有联系&#xff0c;门门道道很多&#xff0c;本篇讨论高可用 高可用技术的重要性在于保证系统的连续可用性&#xff0c;提高系统的稳定性和可靠性。它可以应对高并发和大规…

vue2按需导入Element(vite打包)

1.安装element 说明&#xff1a;-S是生产依赖。 npm install element-ui2 -S 2.安装babel-plugin-component 说明&#xff1a;-D是开发模式使用。 npm install babel-plugin-component -D 3. vite.config.js 说明&#xff1a;借助 babel-plugin-component &#xff0c;我们可…

华为的干部管理和人才管理实践精髓(深度好文,收藏)

&#xff08;本文摘自谢宁专著《华为战略管理法&#xff1a;DSTE实战体系》&#xff0c;欢迎购买&#xff09; 1997年&#xff0c;在《华为基本法》的起草过程中&#xff0c;起草小组的一位人大教授问任正非:“任总&#xff0c;人才是不是华为的核心竞争力?”任正非的回答出人…

在Spring Boot中使用进程内缓存和Cache注解

在Spring Boot中使用内缓存的时候需要预先知道什么是内缓存&#xff0c;使用内缓存的好处。 什么是内缓存 内缓存&#xff08;也称为进程内缓存或本地缓存&#xff09;是指将数据存储在应用程序的内存中&#xff0c;以便在需要时快速访问和检索数据&#xff0c;而无需每次都从…

记录--让我们来深入了解一下前端“三清”是什么

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 前端“三清” 在前端开发中&#xff0c;我们经常听到关于“三清”的说法&#xff0c;即 window、document、Object。这三者分别代表了 BOM(浏览器对象模型)、DOM(文档对象模型)以及 JS 的顶层对象。在…

C/C++轻量级并发TCP服务器框架Zinx-游戏服务器开发006:基于redis查找玩家姓名+游戏业务实现总结

文章目录 1 Redis的安装与API的使用1.1 安装目录及环境变量1.2 设置远程客户端连接和守护进程1.3 启动redis1.4 Hiredis API的使用1.5 我的动态库和头文件 2 Redis的使用2.1 初始化时候2.2 结束的时候 3 测试4 Makefile5 游戏业务总结 1 Redis的安装与API的使用 1.1 安装目录及…

为什么UI自动化难做?—— 关于Selenium UI自动化的思考

在快速迭代的产品、团队中&#xff0c;UI自动化通常是一件看似美好&#xff0c;实际“鸡肋”&#xff08;甚至绝大部分连鸡肋都算不上&#xff09;的工具。原因不外乎以下几点&#xff1a; 1 效果有限 通常只是听说过&#xff0c;就想去搞UI自动化的团队&#xff0c;心里都认…

【数据分享】2021-2023年我国主要城市逐月轨道交通运营数据

以地铁为代表的轨道交通是大城市居民的主要交通出行方式之一&#xff0c;轨道交通的建设和运营情况也是一个城市发展水平的重要体现。本次我们为大家带来的是2021-2023年我国主要城市的逐月的轨道交通运营数据&#xff01; 数据指标包括&#xff1a;运营线路条数&#xff08;条…