自定义刻度尺,通过Scroller实现的滚动效果,一般用于金额选择,样子为:

1,创建自定view,并在values文件下创建attrs文件

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="ScrollDividingRuleView"><attr name="line_height" format="dimension|reference" /><attr name="dividing_text_size" format="dimension|reference" /></declare-styleable>
</resources>

ScrollDividingRuleView为创建的自定义view 的名
line_height为刻度线的高度
dividing_text_size 刻度显示文字的大小
2,初始化获取view的属性

private void init(Context context, AttributeSet attrs) {for (int i = 0; i < attrs.getAttributeCount(); i++) {String name = attrs.getAttributeName(i);if ("layout_width".equals(name)) {String value = attrs.getAttributeValue(i);if (value.length() > 2) {if (value.endsWith("dp")) {mScaleWidth = Utils.dp2px(context, Float.valueOf(value.substring(0, value.length() - 2)));} else {mScaleWidth = Float.valueOf(value.substring(0, value.length() - 2));}} else if (value.equals("-1") || value.equals("-2") || value.equals("0")) {mScaleWidth = 0;}}else if ("line_height".equals(name)) {String value = attrs.getAttributeValue(i);if (value.length() > 2) {if (value.endsWith("dp")) {mLineHeight = Utils.dp2px(context, Float.valueOf(value.substring(0, value.length() - 2)));} else {mLineHeight = Float.valueOf(value.substring(0, value.length() - 2));}} else {mLineHeight = 50;}} else if ("dividing_text_size".equals(name)) {String value = attrs.getAttributeValue(i);if (value.length() > 2) {if (value.endsWith("sp")) {mTextSize = Utils.sp2px(context, Float.valueOf(value.substring(0, value.length() - 2)));} else {mTextSize = Float.valueOf(value.substring(0, value.length() - 2));}} else {mTextSize = 32;}}}// 画笔mPaint = new Paint();//总的高度,因为text的高度和设置的textSize会有误差所以加上20的高度mRectHeight = (int) (mLineHeight +mTextSize+mTextLineMargin+20);//初始设置每个刻度间距为30pxmScaleMargin = 30;mTextList = new ArrayList<>();//计算宽度mScaleWidth = (mTextList.size() * 5 - 5) * mScaleMargin;mScroller = new Scroller(context);mVelocityTracker = VelocityTracker.obtain();}

其中VelocityTracker类是为了计算滚动速度,VelocityTracke具体介绍参见Android 官方文档VelocityTracke链接
3,重写onMeasure,onDraw方法

@Overrideprotected void onDraw(Canvas canvas) {mPaint.setColor(Color.GRAY);// 抗锯齿mPaint.setAntiAlias(true);// 设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰mPaint.setDither(true);// 空心mPaint.setStyle(Paint.Style.STROKE);// 文字居中mPaint.setTextAlign(Paint.Align.CENTER);onDrawScale(canvas, mPaint); //画刻度onDrawLine(canvas, mPaint);//画刻度中间横线onDrawCenter(canvas, mPaint);//画中心远点super.onDraw(canvas);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int height = View.MeasureSpec.makeMeasureSpec(mRectHeight, View.MeasureSpec.AT_MOST);super.onMeasure(widthMeasureSpec, height);//view宽度mScaleRange = getMeasuredWidth();//初始化开始位置mInitDistance = mScaleRange / 2-mInitPosition*mScaleMargin*5;}private void onDrawScale(Canvas canvas, Paint paint) {paint.setColor(Color.GRAY);paint.setStrokeWidth(2);paint.setTextSize(mTextSize);for (int i = 0, k = 0; i < mTextList.size() * 5 - 4; i++) {if (i % 5 == 0) { //整值canvas.drawLine(i * mScaleMargin + mInitDistance, mRectHeight, i * mScaleMargin + mInitDistance, mRectHeight - mLineHeight, paint);//整值文字canvas.drawText(mTextList.get(k), i * mScaleMargin + mInitDistance, mRectHeight -mLineHeight-mTextLineMargin, paint);k++;} else {canvas.drawLine(i * mScaleMargin + mInitDistance, mRectHeight - mLineHeight / 4, i * mScaleMargin + mInitDistance, mRectHeight - mLineHeight + mLineHeight / 4, paint);}}}private void onDrawLine(Canvas canvas, Paint paint) {paint.setColor(Color.GRAY);paint.setStrokeWidth(2);canvas.drawLine(mInitDistance, mRectHeight - mLineHeight / 2, mScaleWidth + mInitDistance, mRectHeight - mLineHeight / 2, paint);}private void onDrawCenter(Canvas canvas, Paint paint) {paint.setColor(Color.GRAY);paint.setStrokeWidth(6);for (int i = 0; i < mTextList.size() * 5 - 4; i++) {if (i % 5 == 0) { //整值canvas.drawCircle(i * mScaleMargin + mInitDistance, mRectHeight-mLineHeight/2, 4, paint);}}}

mInitDistance 是为了计算中间点显示初始化中间点为哪个位置
4,处理触摸事件
这里就涉及到了一些计算,当手指抬起如果为快速滑动则滑动到开始或者结尾,慢速滑动则会滚动到比较近的主刻度,如果滑动超过也会回到起点或者重点

@Overridepublic boolean onTouchEvent(MotionEvent event) {mVelocityTracker.addMovement(event);int x = (int) event.getX();switch (event.getAction()) {case MotionEvent.ACTION_DOWN:if (mScroller != null && !mScroller.isFinished()) {mScroller.abortAnimation();}mScrollLastX = x;mStartX = x;return true;case MotionEvent.ACTION_MOVE:mVelocityTracker.computeCurrentVelocity(1000);int dataX = mScrollLastX - x;smoothScrollBy(dataX, 0);mScrollLastX = x;return true;case MotionEvent.ACTION_CANCEL:case MotionEvent.ACTION_UP:int moveX = mStartX - x;//如果快速滑动则滚动到滑动方向结尾的位置if(Math.abs(mVelocityTracker.getXVelocity())>4000) {if(moveX>0) {mListener.onScaleScrollChanged(mTextList.size()-1);mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), (int) (mScaleWidth - mScaleRange / 2 + mInitDistance)-mScroller.getFinalX(), 0, 800);}else{mListener.onScaleScrollChanged(0);mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), -mScaleRange / 2 + mInitDistance-mScroller.getFinalX(), 0, 800);}}else {dealActionUp(moveX);}return true;}return super.onTouchEvent(event);}/*** 处理手势抬起之后的操作*/private void dealActionUp(int moveX) {int finalX = mScroller.getFinalX();//当前位置//计算获取手指抬起之后要回到的位置if (moveX >= 0 && finalX > mScaleWidth - mScaleRange / 2 + mInitDistance) {finalX = (int) (mScaleWidth - mScaleRange / 2 + mInitDistance);} else if (moveX <= 0 && finalX <= -mScaleRange / 2 + mInitDistance) {finalX = -mScaleRange / 2 + mInitDistance;} else {int round = Math.abs(Math.round((float) finalX / mScaleMargin));if (round % 5 > 2) {if(finalX>0) {if (finalX / mScaleMargin < round) {finalX = finalX + (5 - round % 5) * mScaleMargin - finalX % mScaleMargin + mScaleMargin;} else {finalX = finalX + (5 - round % 5) * mScaleMargin - finalX % mScaleMargin;}}else{if (finalX / mScaleMargin > -round) {finalX = finalX + ( round % 5-5) * mScaleMargin - finalX % mScaleMargin - mScaleMargin;} else {finalX = finalX + (round % 5-5) * mScaleMargin - finalX % mScaleMargin;}}} else {if(finalX>0) {if (finalX / mScaleMargin < round) {finalX = finalX - (round % 5) * mScaleMargin + (mScaleMargin - finalX % mScaleMargin);} else {finalX = finalX - (round % 5) * mScaleMargin - finalX % mScaleMargin;}}else{if (finalX / mScaleMargin > -round) {finalX = finalX - (-round % 5) * mScaleMargin - finalX % mScaleMargin-mScaleMargin;} else {finalX = finalX - (-round % 5) * mScaleMargin - finalX % mScaleMargin;}}}}mListener.onScaleScrollChanged(finalX/ mScaleMargin / 5+mInitPosition);//返回滚动选中的位置mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), finalX - mScroller.getFinalX(), 0, 800);//纠正指针位置postInvalidate();}/*** 处理手势抬起之后的操作*/private void dealActionUp(int moveX) {int finalX = mScroller.getFinalX();//当前位置//计算获取手指抬起之后要回到的位置if (moveX >= 0 && finalX > mScaleWidth - mScaleRange / 2 + mInitDistance) {finalX = (int) (mScaleWidth - mScaleRange / 2 + mInitDistance);} else if (moveX <= 0 && finalX <= -mScaleRange / 2 + mInitDistance) {finalX = -mScaleRange / 2 + mInitDistance;} else {int round = Math.abs(Math.round((float) finalX / mScaleMargin));if (round % 5 > 2) {if(finalX>0) {if (finalX / mScaleMargin < round) {finalX = finalX + (5 - round % 5) * mScaleMargin - finalX % mScaleMargin + mScaleMargin;} else {finalX = finalX + (5 - round % 5) * mScaleMargin - finalX % mScaleMargin;}}else{if (finalX / mScaleMargin > -round) {finalX = finalX + ( round % 5-5) * mScaleMargin - finalX % mScaleMargin - mScaleMargin;} else {finalX = finalX + (round % 5-5) * mScaleMargin - finalX % mScaleMargin;}}} else {if(finalX>0) {if (finalX / mScaleMargin < round) {finalX = finalX - (round % 5) * mScaleMargin + (mScaleMargin - finalX % mScaleMargin);} else {finalX = finalX - (round % 5) * mScaleMargin - finalX % mScaleMargin;}}else{if (finalX / mScaleMargin > -round) {finalX = finalX - (-round % 5) * mScaleMargin - finalX % mScaleMargin-mScaleMargin;} else {finalX = finalX - (-round % 5) * mScaleMargin - finalX % mScaleMargin;}}}}mListener.onScaleScrollChanged(finalX/ mScaleMargin / 5+mInitPosition);//返回滚动选中的位置mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), finalX - mScroller.getFinalX(), 0, 800);//纠正指针位置postInvalidate();}private void smoothScrollBy(int dx, int dy) {mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);postInvalidate();}

5,传值回调方法
使用Scroller 必须实现的方法

    @Overridepublic void computeScroll() {if (mScroller.computeScrollOffset()) {scrollTo(mScroller.getCurrX(), 0);postInvalidate();}}
 public interface OnScrollListener {void onScaleScrollChanged(int scale);}/*** 初始化数据* @param textList 刻度尺显示文字* @param initPosition 初始位置* @param listener 滚动监听*/public void bindDataAndListener(ArrayList<String> textList,int initPosition,OnScrollListener listener){mInitPosition=initPosition;mTextList = textList;mScaleWidth = (mTextList.size() * 5 - 5) * mScaleMargin;mListener=listener;}/*** 设置每个刻度间距* @param margin 间距* @return 返回当前view 链式编程*/public ScrollDividingRuleView setScaleMargin(int margin){mScaleMargin=margin;mRectHeight = (int) (mLineHeight +mTextSize+mTextLineMargin+20);return this;}/*** 设置文字和刻度线的间距*/public ScrollDividingRuleView setTextLineMargin(int textLineMargin){mTextLineMargin=textLineMargin;mRectHeight = (int) (mLineHeight +mTextSize+mTextLineMargin+20);return this;}

最后附上github 上的地址ScrollDividingRule

自定义View之滚动刻度尺,2018/1/14 05相关推荐

  1. Android自定义View实现方位刻度尺(类似于吃鸡手游)

    Android自定义View实现方位刻度尺(类似于吃鸡手游) 先上效果图 gif可能看不清,我下面放几张图片 原理解析 首先,我们应该把看得到的内容从上至下分成三部分:最上面的文字.中间的竖线和最下面 ...

  2. Android自定义view之(刻度尺view)

    前言: 最近一直在做h5,感觉学的东西多了还真有点混淆了,再来看anroid的时候,觉得有点点陌生了,难道真的是鱼与熊掌不可兼得吗? 好吧,也罢- 在技术群中看到一个小伙伴有一个这样的需求,所以在不是 ...

  3. 自定义 View 循环滚动刻度控件

    LoopScaleView 先看效果图: enter description here LoopScaleView 是一个自定义的刻度尺风格的选值控件,从上面的动图大家可以看到 LoopScaleVi ...

  4. android自定义view,时间刻度尺,时间轴,视频轴

    TimeRuler 最新版见github地址(欢迎star):https://github.com/huangdali/TimeRuler 时间轴.时间刻度尺 继承至TextureView,效率更高 ...

  5. Android自定义半圆形圆盘滚动选择器View

    本文为原创作品,转载请注明出处:https://blog.csdn.net/wjj1996825/article/details/80646526 前段时间公司项目要求做一个特效的滑动选择器,效果如下 ...

  6. Android自定义View(广告栏上下滚动效果)

    需求中涉及到的广告栏变化千变万化,这里,我们综合取材,有了下面的这篇文章. 开始的时候,我们使用的是MarqueeView,继承的ViewFlipper,但是会有一些bug,比如刷新数据时的重叠阴影等 ...

  7. Android自定义View精品(LimitScrollerView-仿天猫广告栏上下滚动效果)

    版权声明:本文为openXu原创文章[openXu的博客],未经博主允许不得以任何形式转载 文章目录 1.分析 2.定义组合控件布局 3.继承最外层控件 4.自定义属性 5.重写onMeasure 6 ...

  8. [Android]自定义View带效果的滚动数字

    [Android]自定义View带效果的滚动数字 @Author GQ 2016年07月29日 一个可以让数字滚动的View,可以自定义参数,是想要的那种效果! 原文github地址 效果图 Andr ...

  9. Android自定义View简单实现手绘折(曲)线滚动图效果

    目录 一.简介 二.实现 三.代码 四.实现效果 一.简介 通过安卓自定义View实现根据手指点击或移动轨迹绘制出折(曲)线图并循环滚动. 二.实现 获取手指点击和移动的y坐标存入数组,设定好x间隔, ...

  10. 自定义View -- 刻度尺

    [图片] 这次在自定义View时主要通过以下几个步骤: 1.准备阶段(在构造方法处) 初始化各种默认的Paint,图片资源.( 其中NinePatch资源需要通过Bitmap生成,绘制时调用nineP ...

最新文章

  1. 机器学习面试题集 - 详解四种交叉验证方法
  2. mapreduce编程实例(3)-求平均值
  3. cpu进程调度---RT Throttling【转】
  4. 微信开发博客——柳峰
  5. 最短路径之dijkstra算法的C语言实现
  6. 大数据学习路线2019版(附全套视频教程及网盘下载)
  7. 飞机大战(微信小游戏)
  8. 1036: 谭浩强C语言(第三版)习题1.6
  9. git报用户名和密码错误
  10. linux终端下载速度只有几kb,[菜鸟教学]如何提高linux下的下载速度!新手必看!...
  11. environment-modules安装配置
  12. nginx 代理 负载均衡 网站转接的用法
  13. hadoop - hadoop2.6 伪分布式 示例 wordcount 分词 和 hdfs常用操作命令
  14. 计算机设计大赛应用软件组,组一览表(计算机设计大赛).pdf
  15. Memwatch简介
  16. 7-42 大炮打蚊子 (15 分)
  17. 上海航芯|电池均衡如何提高电池寿命
  18. [Nikon D80]春芽
  19. 【工作】Amazon Fraud Detection
  20. 【NLP】使用 BERT 和 PyTorch Lightning 进行多标签文本分类

热门文章

  1. (转)安装完Fedora 18后需要做的事情
  2. 驾驭你的“职场布朗运动”---转载
  3. 流媒体服务器ZLMediaKit
  4. 带图标显示的ls---lsd
  5. 关于opencv新版无法使用LSD算法的问题
  6. 打开word出现自动化automation错误、Microsoft visual basic 运行时错误
  7. python登录qq定时发消息_python自动发送qq消息
  8. 彪悍晨读 | 每天价值投资常识
  9. [GAMES101]现代计算机图形学课程总结2:光栅化和反走样
  10. 【多校联赛】The Crime-solving Plan of Groundhog