Android实现滚动刻度尺效果
发布时间 - 2026-01-11 01:25:01 点击率:次缘起

最近在帮人做一个计步器,其中涉及到身高、体重等信息的采集;我参考了众多app的实现,觉得"乐动力"中滑动刻度的方式比较优雅。于是乎,反编译了该app,结果发现它是采用图片的方式实现的,即ScrollView内嵌了一张带刻度的图片。
个人觉得该方式太不灵活,且对美工的依赖较大,于是便想自定义一个刻度尺控件。
需求分析
- 绘制刻度,区分整值刻度和普通刻度
- 红色指针始终在刻度尺的中间,表示当前的刻度
- 刻度的最大值和最小值可动态设置
- 刻度尺的高度或宽度可设置,设置后中间刻度不变
- 可滑动,滑动后当前刻度随之改变
涉及的知识点
- View的机制
- canvas绘图
- Scroller工具类的使用
- 自定义View的属性
- 点击、滑动事件的处理
最终效果
由于简书上无法嵌入gif,为不影响效果,请移步github查看,如果觉得不错,帮忙给个star ^_^https://github.com/LichFaker/ScaleView
实现过程
1、新建一个class:HorizontalScaleScrollView, 继承自View
2、在构造方法中获取自定义属性:
protected void init(AttributeSet attrs) {
// 获取自定义属性
TypedArray ta = getContext().obtainStyledAttributes(attrs, ATTR);
mMin = ta.getInteger(LF_SCALE_MIN, 0);
mMax = ta.getInteger(LF_SCALE_MAX, 200);
mScaleMargin = ta.getDimensionPixelOffset(LF_SCALE_MARGIN, 15);
mScaleHeight = ta.getDimensionPixelOffset(LF_SCALE_HEIGHT, 20);
ta.recycle();
mScroller = new Scroller(getContext());
}
3、重写onMeasure,计算中间刻度
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height=MeasureSpec.makeMeasureSpec(mRectHeight, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, height);
mScaleScrollViewRange = getMeasuredWidth();
mTempScale = mScaleScrollViewRange / mScaleMargin / 2 + mMin;
mMidCountScale = mScaleScrollViewRange / mScaleMargin / 2 + mMin;
}
4、重写onDraw,绘制刻度和指针
protected void onDrawScale(Canvas canvas, Paint paint) {
paint.setTextSize(mRectHeight / 4);
for (int i = 0, k = mMin; i <= mMax - mMin; i++) {
if (i % 10 == 0) {
//整值
canvas.drawLine(i * mScaleMargin, mRectHeight, i * mScaleMargin, mRectHeight - mScaleMaxHeight, paint);
//整值文字
canvas.drawText(String.valueOf(k), i * mScaleMargin, mRectHeight - mScaleMaxHeight - 20, paint);
k += 10;
} else {
canvas.drawLine(i * mScaleMargin, mRectHeight, i * mScaleMargin, mRectHeight - mScaleHeight, paint);
}
}
}
protected void onDrawPointer(Canvas canvas, Paint paint) {
paint.setColor(Color.RED);
//每一屏幕刻度的个数/2
int countScale = mScaleScrollViewRange / mScaleMargin / 2;
//根据滑动的距离,计算指针的位置【指针始终位于屏幕中间】
int finalX = mScroller.getFinalX();
//滑动的刻度
int tmpCountScale = (int) Math.rint((double) finalX / (double) mScaleMargin);//四舍五入取整
//总刻度
mCountScale = tmpCountScale + countScale + mMin;
if (mScrollListener != null) { //回调方法
mScrollListener.onScaleScroll(mCountScale);
}
canvas.drawLine(countScale * mScaleMargin + finalX, mRectHeight,
countScale * mScaleMargin + finalX, mRectHeight - mScaleMaxHeight - mScaleHeight, paint);
}
处理滑动事件
- 在手指按下时,记录当前的x坐标(针对水平刻度尺)。
- 在手指滑动过程中,判断当前指针所指的刻度是否已经超出了边界,如果超出,则禁止滑动,同时刷新当前界面。
- 在手指抬起时,校正当前的刻度。
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mScroller != null && !mScroller.isFinished()) {
mScroller.abortAnimation();
}
mScrollLastX = x;
return true;
case MotionEvent.ACTION_MOVE:
int dataX = mScrollLastX - x;
if (mCountScale - mTempScale < 0) { //向右边滑动
if (mCountScale <= mMin && dataX <= 0) //禁止继续向右滑动
return super.onTouchEvent(event);
} else if (mCountScale - mTempScale > 0) { //向左边滑动
if (mCountScale >= mMax && dataX >= 0) //禁止继续向左滑动
return super.onTouchEvent(event);
}
smoothScrollBy(dataX, 0);
mScrollLastX = x;
postInvalidate();
mTempScale = mCountScale;
return true;
case MotionEvent.ACTION_UP:
if (mCountScale < mMin) mCountScale = mMin;
if (mCountScale > mMax) mCountScale = mMax;
int finalX = (mCountScale - mMidCountScale) * mScaleMargin;
mScroller.setFinalX(finalX); //纠正指针位置
postInvalidate();
return true;
}
return super.onTouchEvent(event);
}
最后的说明
以上只是针对水平滑动刻度的实现,垂直滑动原理一致,在源码中已经实现,其中也有许多不够完善的地方,如:
- 第一次快速滑动时,可以超出边界,之后则不会;
- 开放的自定义属性不够(根据具体情况);
- 可以考虑将水平和垂直的实现,在一个类中完成,因为在实现过程中发现其实有很多代码都是类似的,只是个别参数属性的不同,在坐标系中,垂直可以看成是水平旋转了90°,之后有时间可以朝这个方向尝试下。
# Android滚动刻度尺
# Android实现刻度尺
# Android
# 刻度尺
# Android自定义控件之刻度尺控件
# Android实现滑动刻度尺效果
# Android实现自定义滑动刻度尺方法示例
# Android自定义RecyclerView实现不固定刻度的刻度尺
# 自定义
# 重写
# 过程中
# 都是
# 也有
# 有很多
# 它是
# 做一个
# 按下
# 涉及到
# 给个
# 帮人
# 回调
# 书上
# 太不
# 内嵌
# 新建一个
# 类中
# 觉得该
# 最小值
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel如何集成微信支付SDK_Laravel使用yansongda-pay实现扫码支付【实战】
Laravel如何实现用户注册和登录?(Auth脚手架指南)
Internet Explorer官网直接进入 IE浏览器在线体验版网址
百度输入法全感官ai怎么关 百度输入法全感官皮肤关闭
Laravel中DTO是什么概念_在Laravel项目中使用数据传输对象(DTO)
详解Oracle修改字段类型方法总结
UC浏览器如何切换小说阅读源_UC浏览器阅读源切换【方法】
大连网站制作费用,大连新青年网站,五年四班里的视频怎样下载啊?
北京的网站制作公司有哪些,哪个视频网站最好?
如何在 React 中条件性地遍历数组并渲染元素
如何获取上海专业网站定制建站电话?
Android 常见的图片加载框架详细介绍
如何在建站主机中优化服务器配置?
哪家制作企业网站好,开办像阿里巴巴那样的网络公司和网站要怎么做?
EditPlus中的正则表达式 实战(2)
如何在自有机房高效搭建专业网站?
Laravel怎么发送邮件_Laravel Mail类SMTP配置教程
Laravel如何实现数据导出到CSV文件_Laravel原生流式输出大数据量CSV【方案】
Laravel如何实现全文搜索功能?(Scout和Algolia示例)
北京专业网站制作设计师招聘,北京白云观官方网站?
如何用PHP快速搭建高效网站?分步指南
如何在阿里云部署织梦网站?
logo在线制作免费网站在线制作好吗,DW网页制作时,如何在网页标题前加上logo?
Laravel如何配置任务调度?(Cron Job示例)
如何在IIS中配置站点IP、端口及主机头?
高端智能建站公司优选:品牌定制与SEO优化一站式服务
Laravel怎么创建自己的包(Package)_Laravel扩展包开发入门到发布
Laravel Telescope怎么调试_使用Laravel Telescope进行应用监控与调试
使用C语言编写圣诞表白程序
昵图网官方站入口 昵图网素材图库官网入口
如何快速查询网站的真实建站时间?
Python函数文档自动校验_规范解析【教程】
BootStrap整体框架之基础布局组件
php485函数参数是什么意思_php485各参数详细说明【介绍】
如何确保FTP站点访问权限与数据传输安全?
如何快速配置高效服务器建站软件?
智能起名网站制作软件有哪些,制作logo的软件?
Java遍历集合的三种方式
Laravel任务队列怎么用_Laravel Queues异步处理任务提升应用性能
简单实现Android验证码
Swift开发中switch语句值绑定模式
如何快速生成凡客建站的专业级图册?
浏览器如何快速切换搜索引擎_在地址栏使用不同搜索引擎【搜索】
详解免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)
高配服务器限时抢购:企业级配置与回收服务一站式优惠方案
宙斯浏览器文件分类查看教程 快速筛选视频文档与图片方法
html5怎么画眼睛_HT5用Canvas或SVG画眼球瞳孔加JS控制动态【绘制】
Laravel怎么上传文件_Laravel图片上传及存储配置
深圳防火门网站制作公司,深圳中天明防火门怎么编码?
Laravel怎么实现模型属性转换Casting_Laravel自动将JSON字段转为数组【技巧】

