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

news2024/11/29 14:54:41

效果图:

直接上代码:

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.coloradmin.cn/o/1902527.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SQL注入【1】——通用漏洞/SQL注入/mysql跨库/ACCESS偏移

一、知识点: 1、脚本代码与数据库前置知识 2、Access数据库注入-简易&偏移 3、MYSQL数据库注入-简易:权限跨库 二、前置知识: &#xff08;一&#xff09;SQL注入漏洞产生原理分析 SQL注入产生条件&#xff1a;根本条件&#xff1a;可控变量、特定函数。 脚本代码在实现…

一气之下,关闭成都400多人的游戏公司

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 最近&#xff0c;多益网络宣布关闭成都公司&#xff0c;在未来三年内&#xff0c;关闭成都所有的相关公司。原因竟然是输掉了劳动仲裁&#xff0c;赔偿员工38万多&#xff0c;然后一气之下要退出成都&#xff0c;…

windows下编译ffmpeg 最详细教程

1 Ffmpeg下载地址&#xff1a;FFmpeg 使用命令下载 git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg 下载完成后会发现如下目录&#xff1a; 2 msys2下载地址&#xff1a;MSYS2 解压好后&#xff0c;选择一个非空路径安装&#xff0c;安装好后路径如下&#xff1a; 为…

基于STM32的通用红外遥控器设计: 解码、学习与发射(代码示例)

摘要&#xff1a; 本文将带你使用STM32打造一款功能强大的万能红外遥控器&#xff0c;它可以学习和复制多种红外信号&#xff0c;并通过OLED屏幕和按键实现便捷操作。我们将深入探讨红外通信原理、STM32编程、OLED显示和EEPROM数据存储等关键技术&#xff0c;并提供完整的代码示…

【Qt】day3 自定义控件、框架、定时器、QPainter、QFile

文章目录 自定义控件封装自定义框架定时器第一种方式第二种方式 &#xff08;推荐&#xff09; 事件分发器QPainter基本操作高级设置抗锯齿移动坐标原点 画家画资源图片&#xff0c;并实现手动移动 作业QPaintDevice绘图设备QPixmapQimageQPicture QFile文件读写操作QFileInfo文…

FPGA_GTX:简要版

1. GTX介绍 Xilinx FPGA的GT意思是Gigabyte Transceiver。通常称呼为Serdes、高速收发器。GT在xilinx不同系列有着不同的产品&#xff0c;从7系列到UltraScale系列分别有GTP、GTX、GTZ、GTH、GTY和GTM。不同GT整体结构上类似&#xff0c;为了支持越来越高的line rate&#xff…

virtualbox窗口和win10窗口的切换

1、问题&#xff1a; 从windows切换到虚拟机可以用快捷键 ALTTAB&#xff0c;但是从虚拟机到windows使用 ALTTAB 无法成功切换 2、解决方法&#xff1a; 注意&#xff1a;发现设置为ctrlAlt会导致打开终端快捷键&#xff08;CtrlAltT&#xff09;失效&#xff0c;建议这里设置…

Lua语言入门

目录 Lua语言1 搭建Lua开发环境1.1 安装Lua解释器WindowsLinux 1.2 IntelliJ安装Lua插件在线安装本地安装 2 Lua语法2.1 数据类型2.2 变量全局变量局部变量命名规范局部变量作用域 2.3 注释单行注释多行注释 2.4 赋值2.5 操作符数学操作符比较操作符逻辑操作符连接操作符取长度…

HACCP体系认证:守护食品安全的黄金标准

在食品生产过程中&#xff0c;食品安全始终是重中之重。为了确保食品的安全性和质量&#xff0c;越来越多的企业开始采用HACCP&#xff08;危害分析关键控制点&#xff09;体系认证。这个体系不仅能帮助企业预防食品安全问题&#xff0c;还能显著提升产品质量和市场竞争力。 HA…

深入探索C语言中的结构体:定义、特性与应用

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 目录 结构体的介绍结构体定义结构成员的类型结构体变量的定义和初始化结构体成员的访问结构体传参 结构体的介绍 在C语言中&#xff0c;结构体是一种用户自定义的数据类型&#xff0c;它允许开发者将不同类型的变量组合在一起…

适用于Mac和Windows的最佳iPhone恢复软件

本文将指导您选择一款出色的iPhone数据恢复软件来检索您的宝贵数据。 市场上有许多所谓的iPhone恢复程序。各种程序很难选择并选择其中之一。一旦您做出了错误的选择&#xff0c;您的数据就会有风险。 最好的iPhone数据恢复软件应包含以下功能。 1.安全可靠。 2.恢复成功率高…

郭明錤:苹果将为Vision Pro推出红外摄像头款AirPods

在科技界,苹果公司的每一次创新都备受瞩目。近日,著名苹果分析师郭明錤透露了一个令人振奋的消息:苹果计划在2026年推出配备红外摄像头的新款AirPods,这款耳机将特别优化与Apple Vision Pro头显的空间体验。这一消息不仅预示着苹果在音频设备领域的又一次技术飞跃,也进一步…

One day for Chinese families

周围生活中的普通家庭的一天流程&#xff1a; 【上班的一天】 【放假的一天】 有家庭的人&#xff0c;上班流程&#xff1a; 01&#xff09;准备早餐&#xff0c;牛奶&#xff0c;面包 02&#xff09;叫娃娃起床&#xff0c;一般要蛮久的&#xff1b;沟通交流 -- 哄娃娃 -- 生气…

【Linux进阶】文件系统8——硬链接和符号连接:ln

在Linux下面的链接文件有两种&#xff0c; 一种是类似Windows的快捷方式功能的文件&#xff0c;可以让你快速地链接到目标文件&#xff08;或目录)&#xff1b;另一种则是通过文件系统的inode 链接来产生新文件名&#xff0c;而不是产生新文件&#xff0c;这种称为硬链接&…

html+css+js图片手动轮播

源代码在界面图片后面 轮播演示用的几张图片是Bing上的&#xff0c;直接用的几张图片的URL&#xff0c;谁加载可能需要等一下&#xff0c;现实中替换成自己的图片即可 关注一下点个赞吧&#x1f604; 谢谢大佬 界面图片 源代码 <!DOCTYPE html> <html lang&quo…

前端面试题9(JavaScript数组去重)

1. 使用Set ES6引入了Set数据结构&#xff0c;它只存储唯一的值&#xff0c;因此可以用来快速去重。 function uniqueWithSet(arr) {return [...new Set(arr)]; }// 示例 console.log(uniqueWithSet([1, 2, 2, 3, 4, 4, 5])); // 输出: [1, 2, 3, 4, 5]2. 使用indexOf或inclu…

Poker Game, Run Fast

Poker Game, Run Fast 扑克&#xff1a;跑得快 分门别类&#xff1a; 单张从小到大默认 A < 2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 < J < Q < K 跑得快&#xff1a;单张从小到大 3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 &…

第二次练习

目录 一、student表的增删改查 1.向student表中添加一条新记录 2. 向student表中添加多条新记录 3.向student表中添加一条新记录 4.更新表&#xff0c;grade 大于90的加0.5 5.删除成绩为空的记录 二、用户权限部分 1、创建一个用户test1使他只能本地登录拥有查询student表的权…

比赛获奖的武林秘籍:03 好的创意选取-获得国奖的最必要前提

比赛获奖的武林秘籍&#xff1a;03 好的创意选取-获得国奖的最必要前提 摘要 本文主要介绍了大学生电子计算机类比赛和创新创业类比赛创意选取的重要性&#xff0c;并列举了好的创意选取和坏的创意选取的例子&#xff0c;同时说明了好的创意选取具有哪些特点&#xff0c;同时…

当需要对大量数据进行排序操作时,怎样优化内存使用和性能?

文章目录 一、选择合适的排序算法1. 快速排序2. 归并排序3. 堆排序 二、数据结构优化1. 使用索引2. 压缩数据3. 分块排序 三、外部排序1. 多路归并排序 四、利用多核和并行计算1. 多线程排序2. 使用并行流 五、性能调优技巧1. 避免不必要的内存复制2. 缓存友好性3. 基准测试和性…