重学 Android 自定义 View 系列(十二):环形SeekBar剖析

Android 圆弧形 SeekBar 阅读详情
前言

一个自定义的圆形 SeekBar,类似于传统的 SeekBar 但采用了圆形轨迹。最近被一个网友私信问有没有类似效果的View,因为前面做过几个环形进度条,这个不就加个触摸效果么,以为不算很难,但深入了解后,才发现事情并没有那么简单…

你需要具备的知识:三角函数正弦余弦计算、反三角函数、角度弧度区别…

该View 由 CircleSeekbar 优化而来,深入进行剖析其原理,效果图如下:

在这里插入图片描述


1. 功能介绍

  • 绘制圆形轨道(进度条):支持背景轨道和进度轨道两层绘制和背景圆环缓存。

  • 支持触摸交互:用户可以通过手指拖动控制进度。

  • 自定义进度范围:允许设置最小值、最大值以及当前进度。

  • 进度变化监听:提供回调接口,实时反馈进度变化。

  • 状态保存与恢复:支持 onSaveInstanceState() 和 onRestoreInstanceState() 以应对屏幕旋转等情况。

2. 绘制逻辑

  • 绘制背景轨道:使用 canvas.drawArc() 绘制一个完整的灰色圆弧。

  • 绘制进度条:基于当前进度计算角度,并在同一圆弧路径上绘制一个有颜色的进度部分。

  • 绘制指示点(滑块):计算当前进度对应的角度,并在圆弧末端绘制一个小圆点作为拖动滑块。

3. 关键技术点解析

3.1 初始化默认滑块位置

如果我们在xml或代码中设置了默认值,就需要给滑块和进度进行初始化,首先计算出默认进度所对应夹角的余弦值:

    private void refreshPosition() {
        //计算当前角度
        mCurAngle = (double) mCurProcess / mMaxProcess * 360.0;
        //Math.toRadians(mCurAngle) 将角度 mCurAngle 从度数转换为弧度。因为在 Java 的 Math 库中,三角函数(如 cos 方法)使用的是弧度制
        double cos = -Math.cos(Math.toRadians(mCurAngle));
        refreshWheelCurPosition(cos);
    }

通过余弦值计算 X 点坐标

    private float calcXLocationInWheel(double angle, double cos) {
        // Math.sqrt(1 - cos * cos) 根据三角函数关系,对于给定角度的余弦值 cos,通过 sin²α + cos²α = 1,
        // 这里计算出正弦值(sin = Math.sqrt(1 - cos * cos))
        if (angle < 180) {
            return (float) (getMeasuredWidth() / 2 + Math.sqrt(1 - cos * cos) * mUnreachedRadius);
        } else {
            return (float) (getMeasuredWidth() / 2 - Math.sqrt(1 - cos * cos) * mUnreachedRadius);
        }
    }

由三角函数公式sin²α + cos²α = 1 可知,Math.sqrt(1 - cos * cos) 就是开 1 - cos²α 的平方根,计算得到当前角度的正弦值,因为 angle < 180代表右半圆,大于圆心 X 坐标,使用➕,得到滑块的 X 坐标点。

通过余弦值计算 Y 点坐标

    private float calcYLocationInWheel(double cos) {
        return getMeasuredWidth() / 2 + mUnreachedRadius * (float) cos;
    }

因为cos 传进来的本来就是负值,所以这里直接用cos 计算,补充一下:(0度 至 90度)cos 是正值,(90度 至 180度)cos 是负值,(180度 至 270度)cos 是负值,(270度 至 360度)cos 是正值。

来,让我们梦回高中:
在这里插入图片描述

3.2 通过触摸点拿到倾斜角的余弦值

在这里插入图片描述
在 onTouchEvent 方法中,可以得到当前触摸点(X,Y)的坐标

    private float computeCos(float x, float y) {
        float width = x - getWidth() / 2;
        float height = y - getHeight() / 2;
        float slope = (float) Math.sqrt(width * width + height * height);
        return height / slope;
    }

由于起始点是从圆的顶点开始的,所以要计算的是顶点与slope夹角的余弦值,这一点不同于前面文章环形进度条的计算方式!

3.3 拿到余弦值后,通过反余弦函数得到角度值
            private static final double RADIAN = 180 / Math.PI;//弧度
            //....
            double angle;
            if (x < getWidth() / 2) { // 滑动超过180度  触摸点在左半边(180°~360°)
                angle = Math.PI * RADIAN + Math.acos(cos) * RADIAN;
            } else { // 没有超过180度 触摸点在右半边(0°~180°)
                angle = Math.PI * RADIAN - Math.acos(cos) * RADIAN;
            }

在单位圆中,任意点的坐标(x,y)可以表示为(cosθ, sinθ),已知点的坐标,可以通过反余弦函数(arccos)求出角度,反余弦函数返回的是弧度值,范围是[0, π](0°~180°),它无法直接区分左半圆和右半圆,可知,上面计算可简化为:180 + θ 或 180 - θ。

这样就拿到了触摸点相对于顶点的顺时针旋转角度值!

3.4 防止滑过一圈后还能滑动

正常情况下,我们希望从0度 滑到 360度,开始到结束即可,所以就要限制一下,某些角度触摸点的事件处理:

                if (mCurAngle > 270 && angle < 90) {
                    mCurAngle = 360;
                    cos = -1;// 防止跳跃
                } else if (mCurAngle < 90 && angle > 270) {
                    mCurAngle = 0;
                    cos = -1;
                } else {
                    mCurAngle = angle;
                }

看代码好像不容易理解,来看两张图吧:

强制设置角度为360

在这里插入图片描述
当当前滑块位置相对于角度是 mCurAngle > 270,即上图粉色区域时,这时如果点击红色区域即 angle < 90,就强制设置为360度,就是满进度,防止出现不正常现象。

强制设置角度为0

在这里插入图片描述
同上所述,只是反过来了而已。

3.5 限制圆内点击事件

只有当点击圆环及内部才能触发刷新事件

    private boolean isTouch(float x, float y) {
        double radius = (getWidth() - getPaddingLeft() - getPaddingRight() + getCircleWidth()) / 2;
        double centerX = getWidth() / 2;
        double centerY = getHeight() / 2;
        return Math.pow(centerX - x, 2) + Math.pow(centerY - y, 2) < radius * radius;
    }

Math.pow 是Java中 Math 类的一个静态方法,用于计算一个数的指定次幂。其完整方法签名为:

public static double pow(double a, double b)

参数说明

  • a:底数,即要进行乘方运算的基础数值。
  • b:指数,用于指定底数要相乘的次数。

直接比较 平方距离 和 半径平方, 三角型 两边平方之和大于第三边平方 就不在圆内,如下图所示:
在这里插入图片描述

3.6 Canvas缓存
            if (mCacheCanvas == null) {
                buildCache(centerX, centerY, wheelRadius);
            }
            //...
    private void buildCache(float centerX, float centerY, float wheelRadius) {
          mCacheBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
          mCacheCanvas = new Canvas(mCacheBitmap);

          //画环
          mCacheCanvas.drawCircle(centerX, centerY, wheelRadius, mWheelPaint);
    }

在此View中,需要实时更新的一直是进度圆环和滑块位置,而背景圆环始终不变,所以可以将背景圆环的画布缓存下来,防止不必要的重绘,提升性能,具体表现在:

  • 减少重复计算:背景圆环的几何计算只需要一次
  • 避免重复绘制:静态内容只需绘制一次到 Bitmap,之后直接复用
  • 降低GPU负担:减少每帧需要上传到GPU的数据量

这种缓存方式 特别适合静态内容多、动态内容少的自定义 View。

4. 自定义属性

 <declare-styleable name="CircleSeekBar">
    <!-- 进度条的最大值 -->
    <attr name="wheel_max_process" format="integer" />

    <!-- 当前进度值 -->
    <attr name="wheel_cur_process" format="integer" />

    <!-- 已完成部分的进度条颜色 -->
    <attr name="wheel_reached_color" format="color" />

    <!-- 已完成部分的进度条宽度 -->
    <attr name="wheel_reached_width" format="dimension" />

    <!-- 是否为已完成进度条的两端添加圆角 -->
    <attr name="wheel_reached_has_corner_round" format="boolean"/>

    <!-- 未完成部分的进度条颜色 -->
    <attr name="wheel_unreached_color" format="color" />

    <!-- 未完成部分的进度条宽度 -->
    <attr name="wheel_unreached_width" format="dimension" />

    <!-- 指示滑块的颜色 -->
    <attr name="wheel_pointer_color" format="color" />

    <!-- 指示滑块的半径 -->
    <attr name="wheel_pointer_radius" format="dimension" />

    <!-- 是否为指示滑块添加阴影 -->
    <attr name="wheel_has_pointer_shadow" format="boolean" />

    <!-- 是否为进度条整体添加阴影 -->
    <attr name="wheel_has_wheel_shadow" format="boolean" />

    <!-- 指示滑块阴影的半径大小 -->
    <attr name="wheel_pointer_shadow_radius" format="dimension" />

    <!-- 进度条整体阴影的半径大小 -->
    <attr name="wheel_shadow_radius" format="dimension" />

    <!-- 是否开启缓存优化(减少重复绘制,提高性能) -->
    <attr name="wheel_has_cache" format="boolean" />

    <!-- 是否允许用户手动触摸滑动进度 -->
    <attr name="wheel_can_touch" format="boolean" />

    <!-- 是否限制只能在一个完整圆周内滑动进度 -->
    <attr name="wheel_scroll_only_one_circle" format="boolean" />
</declare-styleable>

5. 最后

恭喜你,上了一堂生动形象的数学课,哈哈哈

源码已上传Github: DiyView

Android自定义圆Seekbar的实现与应用 自定义View是Android开发中不可或缺的一部分,它允许开发者超越默认控件的功能和外观,设计出更具特色的界面元素。在这一章中,我们将介绍自定义View的基础知识,并为接下来的章节搭建理论基础。自定义View的开发涉及几个核心概念,包括View的测量(onMeasure)、布局(onLayout)和绘制(onDraw)过程。理解这些基本的回调方法和它们的职责是开发自定义控件的关键。自定义View是继承自View类或其子类,并通过重写其方法来实现特定界面需求的控件。 阅读详情

相关推荐

适用于Android的自定义圆形SeekBar(圆形,半圆形和椭圆形)-Android开发

CircularSeekBar丰富的功能,适用于Android的圆形SeekBar(圆形,半圆形和椭圆形)。 该库基于Matt Joseph(devadvance)的CircularSeekBar。 但是原始作者似乎已经停止维护(l的CircularSeekBar丰富功能Circular SeekBar(圆,半圆和椭圆))。该库基于Matt Joseph的CircularSeekBar(devadvance)。但是原始作者似乎已经停止了。维护(最后一次提交是在2016年),所以我决定自己解决这个问题。我将其构建在Android Studio(Gradle)上,然后修复并向库中添加了更多功能。我添加的功能支持浮动进度,而不是仅整数。重构属性的名称,以避免con

Android SeekBar 进度条圆角

之前的不是圆角是clip切割导致的。

龙叔的专栏 2110

环形SeekBar CircleSeekBar

自定义View实现环形SeekBar,可以在xml中设置属性,也可以通过java代码设置属性

Android自定义view-CircleSeekbar

自定义view练手,效果图如下: 实现功能 可设置圆环颜色和线宽及触摸后的颜色和线宽 可设置圆环内圈显示的文本内容及字体大小、颜色 可设置触摸点的图片 可设置触摸的有效范围 使用示例<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.an

持续探索技术边界,分享前后端实战、AI 应用与工程效率的深度思考。 1835

Android自定义圆形SeekBar

自定义的圆弧行SeekBar。(PS:不支持0%-100%跳转)

圆环形的滑动条的滑杆(SeekBar)特效

圆环形的滑动条的滑杆(SeekBar)特效,源码CircularSeekBar,该效果实现圆环形的滑动条(SeekBar),效果有点类似我们常见的圆形进度条的效果的,只不过该效果是手动的,是滑杆(SeekBar)特效,特效android源码频道下载。 A circular seek bar for Android.

自定义圆点刻度可滑动的SeekBar,也可设置刻度

自定义SeekBar,可实现刻度绘制,圆点绘制。Main中调用方式,完整代码。 seekbar_bg.xml

Active的博客 2112

ECU-TEST笔记&使用技巧01

说明 1)所述均基于如下环境:ECU-TEST 8.0、Win10系统、NI Veristand 2018(HIL测试)。不排除因为版本及软件环境问题,所述方法不奏效或有更好的方法,欢迎交流,一起提高。 2)本博客以问答形式进行,所述问题均来自于实际测试(包括MIL、HIL测试)遇到的问题及需求,目的是提高测试效率和自动化测试序列的可维护性。 3)本博客尽量从测试方法及ECU-TEST原...

Eigrl的博客 1万+

Android弧形SeekBar的设计与实现

现在很多相机都会有zoom的功能,就是可以通过一个选择器设置焦距,来实现将远处的事物放大以达到能看清楚的目的,实现zoom除了可以通过双指滑动屏幕放大缩小来调整焦距外,还可以通过一个选择器。本文中要讲的就是实现一个弧形的选择器,这个弧形的选择器会随着设置的不同而改变UI的显示。因为随着googlecamera2架构的普及,相机可以通过设置更多的拍照参数来获取需要的图像,比如iso、曝光值、白平衡、快门时间等。...

知识应该共享,创造才应收费 2931

《Linux运维总结:Ubuntu22.04忘记root密码解决方案》

《Linux运维总结:Ubuntu22.04忘记root密码解决方案》

东城绝神 4459

Win11实时字幕还能这么玩?手把手教你打造专属‘AI字幕机’,告别视频网站无字幕困扰

本文详细介绍了如何利用Win11的实时字幕功能打造全平台通用的AI字幕系统。通过C#抓取程序、Node.js中转服务和前端展示界面的组合,实现自动抓取系统字幕并实时推送到任意网页视频的方案,有效解决在线视频无字幕的困扰。

weixin_33735077的博客 391

自定义Android渐变式圆环滑动条

第一步:效果展示第二:步骤说明 1.自定义Colorcircleprogressview继承view; 2.在values下建立attrs文件,并定义自己的属性; 3.在view中通过getContext().getTheme().obtainStyledAttributes()把属性引入; 4.重写onDraw方法 5.重写onTouchEvent方法第三步:java源码展示packag

alijiahua的博客 3203

手把手教你用OPA运放和MOS管搭建恒流源电路(附完整电路图)

本文详细介绍了如何使用OPA运放和MOS管搭建高精度恒流源电路,涵盖核心元器件选型、电路设计、工作原理及实际搭建步骤。通过负反馈控制机制和MOS管工作区设置技巧,实现稳定的电流输出,适用于LED驱动、电池充电等场景。附完整电路图和调试技巧,助您快速掌握恒流源设计。

weixin_29239279的博客 535

推荐一款创新的Android圆形滑动条库 - Android HoloCircleSeekBar

推荐一款创新的Android圆形滑动条库 - Android HoloCircleSeekBar HoloCircleSeekBarAndroid circle seekbar widget inspired from: https://github.com/LarsWerkman/HoloColorPicker项目地址:https://gitcode.com/gh_mirrors/ho/Hol...

gitblog_00005的博客 448

SAP CO-PA数据传送避坑指南:销售开票与FI/MM记账的日常配置实操(含KEKF/KE4I/KEI2)

本文深入解析SAP CO-PA数据传送中的常见配置问题,特别是销售开票与FI/MM记账环节的实操避坑指南。通过KEKF/KE4I/KEI2等关键配置点的详细说明,帮助用户避免数据失真和重复记录,提升获利能力分析的准确性。文章结合实战案例,揭示官方文档未提及的'数据黑洞'形成机制,为SAP顾问提供实用的排雷手册。

weixin_26905487的博客 390

CAN(2)-GD32

GD32中CAN的特征。

小柚子的博客 553

android CircularSeekBar

Android 中的 seekBar会被开发者经常用到,用的最多的空拍是控制音量。但是有时后为了更好的UI效果,横着的拖动条不能满足我们项目的需要,我们可能需要竖直的或者圆形的拖动条,那这两种样式的类SeekBar的效果如何实现呢,接下来小编会一一给出效果和源码。接下来,先说一说圆形的效果吧,有图有真相,请看图:         看过图之后是不是觉得很炫,自己赞一个,下面给出源码:

我是小黑,我要发发。 1649

315、弱电基础知识介绍

常见的弱电系统包括:闭路电视监控系统、防盗报警系统、门禁系统、电子巡更系统、停车场管理系统、可视对讲系统、家庭智能化系统及安防系统、背景音乐系 统、LED显示系统、等离子拼接屏系统、DLP大屏系统、三表抄送系统、楼宇自控系统、防雷与接地系统、寻呼对讲及专业对讲系统、弱电管道系统、UPS不 间断电源系统、机房系统、综合布线系统、计算机局域网系统、物业管理系统、多功能会议室系统、有线电视系统、卫星电视系统、卫星通讯系统、消防系统、电话 通讯系统、酒店管理系统、视频点播系统、人力资源管理系统等等。

qq_43416206的博客 1365

【Android UI】自定义圆形SeekBar和自定义Dialog的结合使用

最近要做一个比较有趣的效果,就是将android手机设置的条形的SeekBar换成圆形的SeekBar,这里我不讲怎么将定制系统的设置rom,但如果有人想了解设置里面的显示可以看看这篇博客,只讲怎么自定义圆形SeekBar和自定义Dialog,虽然网上有很多这方面的资料,但不系统,所以在这里总结下,方面以后观看。自定义圆形SeekBar关于圆形SeekBar我是在GitHub上下载的一个开源项目,项

yayax的专栏 4846

MATLAB算法实战应用案例精讲-【数模应用】梯度提升决策树(GDBT)(附python代码)

GBDT(Gradient Boosting Decision Tree) 又叫 MART(Multiple Additive Regression Tree),是一种迭代的决策树算法,该算法由多棵决策树组成,它在被提出之初就和SVM一起被认为是泛化能力(generalization)较强的算法。近些年更因为被用于搜索排序的机器学习模型而引起大家关注。.........

qq_36130719的博客 2077

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

SeekArc:圆形SeekBar视图在Android中的应用及问题解决方案

SeekArc是一款专为Android平台设计的圆形SeekBar视图,采用Java编写。此项目允许开发者在应用程序中集成具有可定制弧度、旋转方向等特性的圆形进度条,最低支持SDK版本为8。利用它,你可以创建全圆、半圆或任意角度的弧形滑动条,增添了界面的互动性和美观性。 ### 新手使用注意事项及解决步骤 #### 注意事项1:适配布局大小 **问题描述**:新手可能会遇到SeekArc视图未...

gitblog_00547的博客 1015

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

安卓自定义圆形裁剪控件,圆形seekbar可拖动

如图所示,最近做了一个圆形的裁剪控件,我也觉得这有点奇怪,横的不好吗,不过没办法设计出来就是要搞,那只能画了。 首先简单分析下怎么搞。我们把它拆分成两部分就容易了,简单的就是两个圆形的seekbar。然后裁剪的时间范围肯定不能小于0,所以开始的seekbar不能超过结束的seekbar。分析完成就开始画了。三部曲就不说了,首页画一个圆形的可以拖动的seekbar。 1、画一个底色的圆弧,就是那灰色的一圈, canvas.drawArc(mRectF, 0, 360, false, circle..

qq_22718203的博客 1219

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

自定义圆形seekBar,超简单

import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.support.annotation.Nullabl...

buyaoshitududongwo的博客 1131

Android圆形进度条控件-CircleSeekBar

Android圆形进度条控件-CircleSeekBar 1.引言 博主Android开发起步没多久,一脚踏入社会工作。对Android可以说是非常的喜欢,这里花了一天多的时间写了一个圆形进度条的控件,没有参考其他类似控件的实现方式。如果有什么好的建议,或者需要我改善的,希望大家能够指出,和你们一起进步哦。 另外项目我已经放到Git上,大家可以随意使用。CircleSeekBar项目地址

u010668114的专栏 7025

Android 圆弧形 SeekBar 项目推荐

Android 圆弧形 SeekBar 项目推荐 1. 项目基础介绍及编程语言 ArcSeekBar 是一个开源的 Android 自定义控件项目,主要使用 Java 语言开发。该项目旨在为开发者提供一个具有自定义属性的圆弧形 SeekBar 控件,使得用户界面更加美观和直观。 2. 项目核心功能 该项目的核心功能是提供了一个可以自定义样式的圆弧形进度条,主要特性包括: 支持自定义圆弧的宽度、开...

gitblog_00187的博客 432

安卓进度条:ProgressBar和Seekbar

是 Android 中的一个进度条控件,用于显示正在进行的任务的进度。它可以以水平或圆形的形式展示进度,并提供了多种样式和属性来满足不同的需求。SeekBar是 Android 中的一个可滑动条控件,用于允许用户通过滑动来选择一个数值或调整某种设置。它通常用于音量控制、亮度调节、进度选择等场景。SeekBar 继承自 ProgressBar 类。

A125679880的博客 8340
上一篇: 安卓性能调优之-检测应用启动速度
下一篇: 安卓性能调优之-掉帧测试
_蓝橙
博客等级 码龄7年 1179粉丝 174原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值