一 基础:

自定义View实现跟随手指滚动的刻度尺,实现了类似SeekBar的滑动选中效果。项目地址,欢迎star!

UI图:

功能:

通过设置最小值跟最大值的范围,以及offset值。View将根据这些数据去计算出需要几个小刻度和几个长刻度,和每个长刻度上面显示的数值。

指针可以随意的定制。

当滑动停止后,刻度尺会根据四舍五入将距离指针最近的长刻度滑动到指针的位置。

支持范围越界回弹。

支持设置默认值。

二 实现:

先扯一下,再看别人写的控件的时候总有一种一脸懵逼的感觉,好多凌乱的变量和一大堆的计算逻辑都不知道干嘛用的。比如:PullToRefreshLayout。除非自己按着整体的设计流程写一遍,一步步的写,等出了bug你就明白那些操作的价值。结合之前读第三方控件的经验,写这个刻度尺控件的时候就一步步的去完成,从简单的绘制,到点击事件,再到滑动fling,最后滑动结束更正滑动位置。每一步遇到的问题都记录下来,之后再补全解决方法,这就是成长。

1.绘制刻度

这里省略了onMeasure,这里的需求只是计算一下高度就好了。接着看onDraw方法:

private void drawRuler(Canvas canvas) {

mTextIndex = 0;

for (int index = 0; index <= mRulerHelper.getCounts(); index++) {

boolean longLine = mRulerHelper.isLongLine(index);

int lineCount = mLineWidth * index;

mRect.left = index * mLineSpace + lineCount + mMarginLeft;

mRect.top = getStartY(longLine);

mRect.right = mRect.left + mLineWidth;

mRect.bottom = getEndY();

if (longLine) {

if (!mRulerHelper.isFull()) {

mRulerHelper.addPoint(mRect.left);

}

String text = mRulerHelper.getTextByIndex(mTextIndex);

mTextIndex++;

canvas.drawText(text, mRect.centerX(), getMeasuredHeight() - dpFor14, mTextPaint);

}

canvas.drawRect(mRect, mLinePaint);

mRect.setEmpty();

}

}

这里解释一下为什么刻度采用Rect而不是设置line的宽度,其实最简单的就是设置Paint的宽度然后canvas.drawLine()。刚绘制的时候就是采用的canvas.drawLine(),绘制完之后发现每个刻度的宽度都被削减了一半,canvas.drawLine()是在设置的(x,y)坐标开始平分line的宽度的(这个你要去体验一下就会明白)。所以给定坐标之后每个刻度看起来就像是被挤了一样,所以才采用Rect简单方便一点。进入正题,绘制有几个问题:

怎么确定要绘制几个Rect?

这个比较灵活,要看具体的需求了。也就是一大格里面包含几个刻度,一般是包含10个刻度,刻度包括长短刻度。然后一大格刻度表示多少数值,也就是offSet值是多少。之后刻度的范围也要明确并且能被offSet整除,比如范围是(low,height),那么(height-low)/(offSet/10)就是你需要绘制多少个刻度。

public void setScope(int start, int count,int offSet) {

if(offSet != 0) {

this.offSet = offSet;

}

lineNumbers = (count - start) / (this.offSet / 10);

}

怎么确定那个是长刻度?

这个问题要确定一大格之间有几个小刻度了,一般为10个的话,那么当前的index/10能整除就是到了该绘制长刻度的时候了,mRulerHelper.getCounts()就是我们计算出的总共有几个刻度。

for (int index = 0; index <= mRulerHelper.getCounts(); index++) {

boolean longLine = mRulerHelper.isLongLine(index);

...

if (longLine) {

canvas.drawText(text, mRect.centerX(), getMeasuredHeight() - dpFor14, mTextPaint);

}

canvas.drawRect(mRect, mLinePaint);

}

之后呢就是我们计算Rect的左边跟绘制Text的坐标了。。。不细讲。。。具体可看这里啊。

有个问题就是你得明白Rect的left top right bottom分别表示那个区间:

[图片上传失败...(image-5d1f26-1554206618213)]

2.处理点击事件

目前采取的是点击该View的事件全拦截,感觉也没别的什么需求需要过滤事件了。事件处理起来很简单的就是计算出每次移动的差值就好了:

case MotionEvent.ACTION_DOWN:

mPressUp = false;

isFling = false;

startX = event.getX();

break;

case MotionEvent.ACTION_MOVE:

mPressUp = false;

float distance = event.getX() - startX;

if (mPreDistance != distance) {

doScroll((int) -distance, 0, 0);

invalidate();

}

startX = event.getX();

break;

问题就是:

怎么实现滑动的效果?

刻度尺如果范围很大的话总宽度肯定会超出屏幕的,但是Canvas不会绘制屏幕之外的部分,除非等到屏幕之外的部分显示出来。另外让View滑动的方法很多,最初使用的是scrollTo方法,该方法滑动的是View的内容,也符合我们要的效果,不过结果查强人意。差值计算之后稍微一滑动,刻度直接没了,成了一片空白,看起来那个变化值也不大,ok!这是一个疑问ScrollTo+invalidate内容不会显示,直接没了。之后呢换成了Scroller,这个玩意不用太多的介绍了,使用之后便达到了我们想要的效果,一样的变化值。

private void doScroll(int dx, int dy, int duration) {

mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy, duration);

}

是否有疑问?既然屏幕之外的东西Canvas不会去绘制,那么滑动的时候肯定是将屏幕之外的部分滑到屏幕中,也就是在滑动的过程中要继续绘制。从上面的绘制代码能看到这个绘制过程中跟滑动并没有任何的联系,只是单纯的for循环绘制而已,为什么呢?第一 我们scrollTo移动的是View的内容,一开始View的实际宽度会超过屏幕的宽度,当没有滑动的时候,View只会绘制屏幕中的可见区域,即使for循环依然执行也不会绘制到屏幕外面,然后在滑动的时候会不断的触发invalidate()方法,也就是for循环会被触发,View开始在新出现的未绘制的区域绘制。已经绘制过的区域会被滑出屏幕,这样就会给用户一个平滑的效果。做完以上两步你的刻度尺已经有了滑动的效果了。下面就是解决边界的问题。

3.边界的处理

UI说当超过边界之后松手回弹,这样的交互效果好。这种交互其实最简单了,在手指离开的时候计算当前的x坐标距离中心指针的x坐标的距离,然后让Scroller去执行回弹的效果。不过这个操作是整个控件中最为重要的一步,因为当手指抬起的时候,中间指针必须指向一个长刻度,不能停留再短刻度上面,那这个操作就跟边界回弹的操作重合了,边界回弹也是让最小或者最大长刻度滑动到中间指针的位置。所以松手之后的操作就分为三种:

currentX :滑动停止时的x坐标。

Point:中间指针位置。

low:刻度尺的最小边界。

height:刻度尺的最大边界。

当前的currentX小于中间指针刻度Point的x坐标,并且小于刻度的最小值low的x坐标。

-----------------Point-currentX--low------height----------

当前的currentX小于中间指针刻度Point的x坐标,并且大于刻度的最小值low表示的x坐标小于刻度尺的最大刻度height的x坐标。

------low-------currentX--Point--------height----------

当前的currentX大于中间指针刻度Point的x坐标,并且大于刻度的最大值height表示的x坐标。

------low-------height-----currentX-Point-------

简单的表示了一下三种位置。

处理就是,先计算出滑动结束之后的当前x坐标跟中间Point的x坐标的距离,然后不为0就使用Scroller滑动:

//计算距离

public int getScrollDistance(int x) {

for (int i = 0; i < mPoints.size(); i++) {

int pointX = mPoints.get(i);

if (0 == i && x < pointX) {

//当前的x比第一个位置的x坐标都小 也就是需要往右移动到第一个长线的位置.

setCurrentText(0);

return x - pointX;

} else if (i == mPoints.size() - 1 && x > pointX) {

//当前的x比最后一个左边的x都大,也就是需要往左移动到最后一个长线位置.

setCurrentText(texts.size() - 1);

return x - pointX;

} else {

if (i + 1 < mPoints.size()) {

int nextX = mPoints.get(i + 1);

if (x > pointX && x <= nextX) {

int distance = (nextX - pointX) / 2;

int dis = x - pointX;

if (dis > distance) {

//说明往下一个移动

setCurrentText(i + 1);

return x - nextX;

} else {

setCurrentText(i);

//往前一个移动

return x - pointX;

}

}

}

}

}

return 0;

}

开始执行滑动:

public void scrollFinish() {

int finalX = mScroller.getFinalX();

int centerPointX = mRulerHelper.getCenterPointX();

int currentX = centerPointX + finalX;

int scrollDistance = mRulerHelper.getScrollDistance(currentX);

if (0 != scrollDistance) {

//第一个参数是滚动开始时的x的坐标

//第二个参数是滚动开始时的y的坐标

//第三个参数是在X轴上滚动的距离, 负数向右滚动.

//第四个参数是在Y轴上滚动的距离,负数向下滚动.

mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), -scrollDistance, 0, 300);

invalidate();

if (scrollSelected != null) {

scrollSelected.selected(getCurrentText());

}

}

}

这样已经可以使用了,滑动的刻度尺已经完成了。不过交给UI一看,人家说这东西怎么那么难滑动呢,每次怎么只能滑一大格呢,我要那种fling的感觉。确实,因为在MotionEvent.ACTION_UP的时候都会去矫正一下位置,所以给使用者的感觉就是一次只能滑一格,滑动体验很不好,只能去增加fling。。。

4.fling

增加fling多简单啊,Scroller不是有这个方法吗mScroller.fling(),使用方法这里不再介绍了。fling增加之后,用户的体验确实好了很多,不过一个新的问题出现了,就是在fling停止之后怎么矫正位置呢?这是个大问题,卡住了好大一会儿,最终找到了解决方法:

@Override

public void computeScroll() {

if (mScroller.computeScrollOffset()) {

//这里是结束之后调用矫正位置的方法。scrollFinish()。

if (mScroller.getCurrX() == mScroller.getFinalX() && mPressUp && isFling) {

mPressUp = false;

isFling = false;

scrollFinish();

}

scrollTo(mScroller.getCurrX(), 0);

invalidate();

}

super.computeScroll();

}

三 结束

效果在文章一开始已经展示出来了,指针并没有在该自定义View中绘制,底部的线也是,因为对于指针的需求是多变的,所以用了一个自定义的ViewGroup去完成剩余的指针和底部的实线。底部的实线放在Group中是因为我们的UI效果,底部的实线上面可以没有刻度,也就是这个底部的线是固定在底部,比我画在刻度下面跟随刻度滑动要简单的多。想到之后的变体,感觉刻度本身的View跟指针分开是比较好扩展的,Group只需要给刻度尺控件传入中间指针的(x,y)坐标就好了。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。

android 带刻度的滑动条_Android实现自定义滑动刻度尺方法示例相关推荐

  1. android 带刻度的滑动条_Android自定义控件尺子 滚动刻度尺

    自定义控件是件很有乐趣的事,慢慢的会爱上它,哈哈哈.废话不多说先上设计图 接下来我们我们要先分析一下页面中需要到哪些东西 1.一条无限长的底线: 2.一根在屏幕中间的红色标记线,代表当前位置: 3.一 ...

  2. HTML滑动条自动滑动,HTML5新特性——自定义滑动条(input[type=range])

    HTML 4.01 与 HTML5之间的差异 以下 input 的 type属性值是 HTML5 中新增的: color.date.datetime.datetime-local.month.week ...

  3. Java渐变进度条_Android ProgressBar自定义图片进度,自定义渐变色进度条

    java.lang.Object ↳android.view.View ↳android.widget.ProgressBar 直接子类 AbsSeekBar 间接子类 RatingBar, Seek ...

  4. php框架加滑动条,IOS_iOS实现双向滑动条效果,最近做项目,碰到一种双向滑 - phpStudy...

    iOS实现双向滑动条效果 最近做项目,碰到一种双向滑动条,自己实现了一下,随便写一下思路,方便以后开发,避免重复写代码,以后粘贴就行了.封装了一下,代码如下: #import typedef NSSt ...

  5. python 滚动条方法_Python OpenCV 使用滑动条来调整函数参数的方法

    引言 在观察OpenCV中某个函数在不同参数的情况下,所得到的效果的时候,我之前是改一次参数运行一次,这样做起来操作麻烦,效率低下.为了更便捷的观察参数变化时带来的处理效果改变 可以使用滑动条来改变参 ...

  6. echarts的滑动条 dataZoom 样式自定义

    官方链接说明:官方链接 编写实现例子: dataZoom = [{height: 15, //高度type: "slider",// show: true,xAxisIndex: ...

  7. android 带刻度的滑动条_Android实现滑动刻度尺效果

    最近群里的开发人员咨询怎样实现刻度尺的滑动效果去选择身高体重等信息.给个横着的效果,自己试着去改编或者修改一下,看看通过自己的能力能不能做出竖着的效果来,过两天我再把竖着的那个滑动选择效果分享出来.废 ...

  8. android 带刻度的滑动条_android 滚轮刻度尺的实现

    遇到一个需求需要实现如下图的效果: 卷尺,通过左右滑动来选择不同的刻度值.这方面的东西以前没弄过,以目前你的能力,想了几种思路都死在了半路上.比如上面的刻度线如何弄,滑动的时候又该如何弄:下面的数字又 ...

  9. Android 带文字的进度条,文字颜色随进度条的增加而渐变的效果

    Android自带的ProgressBar是不带文字的,加文字的话可以参考这篇博客:http://blog.csdn.net/lixiaodaoaaa/article/details/9852327 ...

  10. 小程序自制自带滑动条的表格组件

    小程序自制自带滑动条的表格组件 在编写过程中遇到的难点 1. 表格的编写 2. 表格内容宽度固定 长度自适应 3. 滚动条逻辑及代码编写 4. 滚动条的卡段优化 5. 小程序组件的编写及应用 尾声 先 ...

最新文章

  1. J - Invitation Cards POJ - 1511
  2. android studio 3.0新功能介绍
  3. 紫书 程序 3-3 蛇形填数
  4. 【ArcGIS遇上Python】ArcGIS Python按照指定字段批量筛选不同类型的图斑(以土地利用数据为例)
  5. mysql show 命令_mysql show 相关命令
  6. 写给小白的计算机概论
  7. mysql5.7.26修改账号密码_修改mysql5.7的用户密码
  8. Spring Boot 网易云课堂视频
  9. Spring中使用aop操作需要用到的aspectjweaver-1.8.7.jar包
  10. dns服务器哪个稳定,几个非常好用的DNS服务器(解决电信DNS此劫问题) - 电脑技术,dns,ricky,...
  11. 马尔科夫不等式与切比雪夫不等式
  12. php中rand函数怎么用,php rand函数用法是什么
  13. 一文彻底解决An error occurred while creating the AVD. See idea.log for details问题
  14. 多图站点性能优化:图片压缩、图片缩放、HTTP2、CDN、网络传输优化、图片懒加载预加载、响应式图片
  15. 华为机试题python版节选(基础编程题)
  16. Ubuntu安装vim
  17. VirtualBox 解决不能为虚拟电脑打开一个新任务问题
  18. 如何自己搭建本地代理IP池并验证是否可用
  19. ios-deploy 安装与使用
  20. 小白的模拟电路初步学习20日打卡(2)

热门文章

  1. 五. python 字符串方法函数
  2. 数字逻辑复习——触发器
  3. 程序员自曝接私活:10个月时间接了30多个单子,纯收入40万
  4. php限制选择图片数量,ImagePicker组件,限制选择图片数量上限(selectable={images.length 3} )失败...
  5. CMMI五个成熟度级别和对应22个过程域(PA)
  6. dell12v18a怎么改_拆修DELL 12V 18A电源!
  7. gentoo php,Gentoo安装php7
  8. 比所有女生都小的男生mySQL_男生能接受比自己小很多的女生吗?
  9. 2018年12月份计算机,桌面CPU天梯图2018年12月最新版 十二月台式电脑处理器排名...
  10. android 浏览器弹窗提示,安卓手机浏览器打开网页弹出安全警告的原因及解决方法...