Android 简单快速实现 下弧形刻度尺(滑动事件)

效果图:

直接上代码:

package com.my.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;

/**
 * 弧形可滑动刻度控件
 */
public class ArcRulerView extends View {
    //弧形开始的角度
    private float startAngle = 180f;
    //弧面所跨的弧度
    private final float sweepAngle = 180f;

    //outer总刻度
    private final int outerTotalDial = 80;
    //inner总刻度
    private final int innerTotalDial = (int) (outerTotalDial * 2.5f);
    //每次滑动角度
    private final float moveAnglePre = sweepAngle / outerTotalDial * 0.5f;

    //inner半圆的半径
    private int innerRadius;
    //outer半圆的半径
    private int outerRadius;

    private String TAG = ArcRulerView.class.getSimpleName();

    private int dp2px(int i) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, i, getResources().getDisplayMetrics());
    }

    //inner刻度线的宽度
    private int innerLineWidth = 1;
    //inner刻度线的高度
    private int innerLineHeight = dp2px(18);

    //刻度线的宽度
    private int outerLineWidth = dp2px(2);
    //outer刻度线的高度
    private int outerLineHeight = dp2px(36);

    private Bitmap mRulerPoint;

    private int sp2px(int i) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, i, getResources().getDisplayMetrics());
    }

    private Paint outerLinePaint, innerLinePaint, innerBgPaint, outerBgPaint;

    public ArcRulerView(Context context) {
        this(context, null);
    }

    public ArcRulerView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ArcRulerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

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

    private void init() {
        outerLinePaint = new Paint();
        outerLinePaint.setColor(Color.WHITE);
        outerLinePaint.setAntiAlias(true);

        innerLinePaint = new Paint();
        innerLinePaint.setColor(Color.parseColor("#D6D6D6"));
        innerLinePaint.setAntiAlias(true);

        outerBgPaint = new Paint();
        outerBgPaint.setColor(Color.parseColor("#1E1E1E"));
        outerBgPaint.setAlpha(30);
        outerBgPaint.setAntiAlias(true);

        innerBgPaint = new Paint();
        innerBgPaint.setColor(Color.parseColor("#D6D6D6"));
        innerBgPaint.setAlpha(40);
        innerBgPaint.setAntiAlias(true);

        mRulerPoint = BitmapFactory.decodeResource(getResources(), R.drawable.ic_ruler_point);
    }


    private float dx = 0, dy = 0, mx = 0, my = 0;
    private boolean mIsDrawDial = false;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                dx = event.getX();
                dy = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (!mIsDrawDial) {
                    mx = event.getX();
                    my = event.getY();

                    float md = mx - dx;//x轴滑动距离

                    if (md > 0 && startAngle < sweepAngle + 90) {
                        if (md > 5) {
                            startAngle += moveAnglePre;
                            invalidate();
                            dx = mx;
                            dy = my;
                        }
                    } else if (md < 0 && startAngle > sweepAngle - 90) {
                        if (Math.abs(md) > 5) {
                            startAngle -= moveAnglePre;
                            invalidate();
                            dx = mx;
                            dy = my;
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return true;
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);

        outerRadius = (int) (getWidth() * 0.68f);//outer半径
        innerRadius = (int) (outerRadius - outerLineHeight - innerLineHeight * 0.8f);//inner半径

        if (!mIsDrawDial) {
            mIsDrawDial = true;
            //outer背景
            float outerBgRadius = Math.max(getHeight(), outerRadius);
            float outerWidthDiff = outerBgRadius - getWidth() * 0.5f;
            canvas.drawArc(-outerWidthDiff, 0, getWidth() + outerWidthDiff, outerBgRadius * 2, startAngle, sweepAngle, true, outerBgPaint);

            //inner背景
            float innerWidthDiff = innerRadius - getWidth() * 0.5f;
            canvas.drawArc(-innerWidthDiff, getHeight() - innerRadius, getWidth() + innerWidthDiff, innerRadius * 2f + getHeight() - innerRadius, startAngle, sweepAngle, true, innerBgPaint);

            drawDial(startAngle, sweepAngle, outerTotalDial, outerLineWidth, outerLineHeight, outerRadius, outerLinePaint, canvas);//outer刻度
            drawDial(startAngle, sweepAngle, innerTotalDial, innerLineWidth, innerLineHeight, innerRadius, innerLinePaint, canvas);//inner刻度
            mIsDrawDial = false;
        }

        canvas.drawBitmap(mRulerPoint, getWidth() * 0.5f - mRulerPoint.getWidth() * 0.5f, 0, null);//指针
    }

    /**
     * 画刻度盘
     */
    private void drawDial(float startAngle, float sweepAngle, int dialCount, int lineWidth, int lineHeight, int radius, Paint paint, Canvas canvas) {
        paint.setStrokeWidth(lineWidth);
        float length;
        float angle;
        //根据需要显示的刻度总个数遍历
        for (int i = 0; i <= dialCount; i++) {
            //每一个刻度对应的起始角度为180度+(总度数/个数)*对应刻度的位置
            angle = ((sweepAngle) / (dialCount * 1f) * i) + startAngle;
            //线条的起始点位置
            float[] startP;
            //线条的end点的位置
            float[] endP;
            //短刻度条的长度为长刻度条的一半
            length = lineHeight;

            startP = getPointFromAngleAndRadius(angle, radius);
            endP = getPointFromAngleAndRadius(angle, radius - length);

            //高亮计算
            int alpha = 255;
            int centerAngle = 270;

            if (angle != centerAngle) {
                int angleDiff = (int) Math.abs(angle - centerAngle);
                int anglePer = 255 / 45;
                alpha = 255 - angleDiff * anglePer;
            }
            LibLogUtil.i(TAG, "alpha=" + alpha);
            alpha = Math.max(alpha, 0);
            paint.setAlpha(alpha);
            //画出对应的刻度条
            canvas.drawLine(startP[0], startP[1], endP[0], endP[1], paint);
        }
    }

    /**
     * 根据刻度条相应的角度算出点位置
     *
     * @param angle
     * @param radius
     * @return
     */
    private float[] getPointFromAngleAndRadius(float angle, float radius) {
        //根据三角函数公式可以知道,横坐标值为(刻度条+innnerradius)也就是刻度条对应圆的半径
        //乘以一个cos(angle),因为我们是以(getWidth() / 2,控件的高度)位置建的坐标系
        //而真正的坐标系的位置为控件左上角,所以算出的值后需要+getWidth() / 2或者getHeight()
        double x = radius * Math.cos(angle * Math.PI / 180) + getWidth() / 2;
        double y = radius * Math.sin(angle * Math.PI / 180) + getHeight();
        return new float[]{(float) x, (float) y};
    }

}

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

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

相关文章

iptables实现端口转发ssh

iptables实现端口转发 实现使用防火墙9898端口访问内网front主机的22端口&#xff08;ssh连接&#xff09; 1. 防火墙配置(lb01) # 配置iptables # 这条命令的作用是将所有目的地为192.168.100.155且目标端口为19898的TCP数据包的目标IP地址改为10.0.0.148&#xff0c;并将目标…

基于Java+SpringMvc+Vue技术的在线学习交流平台的设计与实现---60页论文参考

博主介绍&#xff1a;硕士研究生&#xff0c;专注于Java技术领域开发与管理&#xff0c;以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年&#xff0c;拥有近12年的管理工作经验&#xff0c;拥有较丰富的技术架构思想、较扎实的技术功底和资深的项目管理经…

【PB案例学习笔记】-29制作一个调用帮助文档的小功能

写在前面 这是PB案例学习笔记系列文章的第29篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

【Spring Boot】关系映射开发(三):多对多映射

《JPA 从入门到精通》系列包含以下文章&#xff1a; Java 持久层 API&#xff1a;JPA认识 JPA 的接口JPA 的查询方式基于 JPA 开发的文章管理系统&#xff08;CRUD&#xff09;关系映射开发&#xff08;一&#xff09;&#xff1a;一对一映射关系映射开发&#xff08;二&#…

Java_网络编程

网络通信的关键三要素 IP、端口号、协议 IP地址 IP地址&#xff08;Internet Protocol&#xff09;&#xff1a;全程“互联网协议地址”&#xff0c;是分配给上网设备的唯一标志。 IP地址有两种形式&#xff1a;IPv4、IPv6 InetAddress 代表IP地址 InetAddress 的常用方法…

【算法训练记录——Day42】

Day42——动态规划Ⅳ 1.leetcode_1049最后一块石头的重量II2.leetcode_494目标和3.leetcode_474一和零 1.leetcode_1049最后一块石头的重量II 思路&#xff1a;石头只能用一次。。。怎么才能让碰撞后重量最小呢&#xff0c;还要转换成动态规划&#xff0c;难以理解。。 看题解&…

J024_打印电影的全部信息

一、需求描述 展示多部电影的信息。 电影信息包括&#xff1a;电影名称、电影得分、电影票价格。 二、代码实现 2.1 Movie类 package com.itheima.collection;public class Movie {//电影名称private String name;//电影得分private int score;//电影票价格private double…

【Excel】输入内容自动添加边框线

1. 选中表格区域 → 新建条件规则 2. 设置公式 3. 设置格式 测试生效

[吃瓜教程]南瓜书第6章支持向量机

0.补充知识 0.1 超平面 定义&#xff1a; 超平面是指在&#x1d45b;维空间中&#xff0c;维度为 &#x1d45b;−1的子空间。它是分割空间的一个平面。 性质&#xff1a; n维空间的超平面 ( w T x b 0 , 其中 w , x ∈ R n ) (w^Tx_b0,其中w,x\in \mathbb R^n) (wTxb​0,其…

C++的set / multiset容器

一、介绍 C的set容器又被称为集合&#xff0c;所有元素在被插入后都会自动排序。 二、数据结构 set / multiset属于关联式容器&#xff0c;底层数据结构是用二叉树实现的。 其余的容器比如vector、deque和list等为序列式容器&#xff0c;因为他们底层使用线性序列结构&#xf…

Windows环境安装Redis和Redis Desktop Manager图文详解教程

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl Redis概述 Redis是一个开源的高性能键值对数据库&#xff0c;以其卓越的读写速度而著称&#xff0c;广泛用于数据库、缓存和消息代理。它主要将数据存储在内存中&#xff0…

CISC和RISC指令集

文章目录 1. 指令集 2. CISC&#xff08;复杂指令集计算&#xff09; 3. RISC&#xff08;精简指令集计算&#xff09; 4. RISC的设计初衷 5. CISC和RISC流程对比 CISC&#xff08;复杂指令集计算&#xff09;的实现 RISC&#xff08;精简指令集计算&#xff09;的实现 …

【高中数学之函数】四种幂函数图线(二次、三次、开方、开立方)

【图像】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>UNASSIGNED</title><style type"text/css">.c…

【智能算法应用】灰狼算法求解二维栅格路径规划问题

目录 1.算法原理2.二维路径规划数学模型3.结果展示4.参考文献5.代码获取 1.算法原理 【智能算法】灰狼算法&#xff08;GWO&#xff09;原理及实现 2.二维路径规划数学模型 栅格法模型最早由 W.E. Howden 于 1968 年提出&#xff0c;障碍物的栅格用黑色表示&#xff0c;可通…

基于pi控制的数字锁相环simulink建模与仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2022a 3.部分核心程序 &#xff08;完整版代码包含详细中文注释和操作步骤视频&#xff09…

基于MATLAB的PEF湍流风场生成器模拟与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于MATLAB的PEF湍流风场生成器模拟与仿真。PEF&#xff08;Primitive Equations Formulation&#xff09;湍流风场模型&#xff0c;是大气科学和气象学中用来描述大气流动和气…

咬文嚼字:词元是当今生成式人工智能失败的一个重要原因

生成式人工智能模型处理文本的方式与人类不同。了解它们基于"标记"的内部环境可能有助于解释它们的一些奇怪行为和顽固的局限性。从 Gemma 这样的小型设备上模型到 OpenAI 业界领先的 GPT-4o 模型&#xff0c;大多数模型都建立在一种称为转换器的架构上。由于转换器在…

subset使用

在R语言中&#xff0c;subset()函数用于从数据框中选择满足特定条件的观测。其语法如下&#xff1a; subset(x, subset, select, drop FALSE) 参数说明&#xff1a; x&#xff1a;数据框或矩阵。 subset&#xff1a;逻辑条件&#xff0c;用于筛选满足特定条件的行。 select…

Linux Bridge - Part 2

概览 在前一篇文章中&#xff0c;我描述了Linux 网桥&#xff08;bridge&#xff09;的配置&#xff0c;并展示了一个实验&#xff0c;其中使用Wireshark来分析流量。在本文中&#xff0c;我将讨论当创建一个网桥时会发生什么&#xff0c;以及Linux 网桥&#xff08;bridge&am…

给您介绍工控CAN总线

CAN是什么 CAN&#xff0c;全称Controller Area Network&#xff0c;即控制器局域网&#xff0c;是一种由Bosch公司在1983年开发的通信协议。它主要用于汽车和工业环境中的电子设备之间的通信。CAN协议定义了物理层和数据链路层的通信机制&#xff0c;使得不同的设备能够通过CA…