Android仿京东金融的数值滚动尺功能
自定义数值滚动尺,这个用的还是挺多的,例如京东金融的通过滚动尺选择金额等,而这次就是高仿京东金融的数值滚动尺。首先看看下效果图,如下:

首先先给你们各个变量的含义,以免在后面的讲解中不知变量的意思,代码如下:
//最小值
private int minValue;
//最大值
private int maxValue;
//当前值
private int currentValue;
//最小单位值
private int minUnitValue;
//最小当前值
private int minCurrentValue;
//字体大小
private int textSize;
//字体颜色
private int textColor;
//线颜色
private int dividerColor;
//指示线颜色
private int indicatrixColor;
//画线的画笔
private Paint linePaint;
//控价的宽度
private int slideRulerWidth=0;
//滑动的宽度
private int rollingWidth;
//屏幕的宽
private int wrapcontentWidth;
//屏幕的高
private int wrapcontentHeight;
//一屏显示Item
private int showItemSize;
//刻度和数值的间距
private int marginCursorData;
//长刻度的大小
private int longCursor;
//短刻度的大小
private int shortCursor;
//计算每个刻度的间距
private int marginWidth=0;
//数据回调接口
private SlideRulerDataInterface slideRulerDataInterface;
//正在滑动状态
private int isScrollingState=1;
//快速一滑
private int fastScrollState=2;
//结束滑动
private int finishScrollState=3;private GestureDetector mDetector;
private Display display =null;
private Scroller scroller;public SlideRuler(Context context, AttributeSet attrs, int defStyleAttr) {super(context,attrs,defStyleAttr);display=((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();//屏幕宽高wrapcontentWidth=display.getWidth();wrapcontentHeight=display.getHeight();//初始化自定义的参数TypedArray typedArray=context.getTheme().obtainStyledAttributes(attrs,R.styleable.slideruler,defStyleAttr,0);textSize = typedArray.getDimensionPixelSize(R.styleable.slideruler_textSize,(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,15,getResources().getDisplayMetrics()));textColor=typedArray.getColor(R.styleable.slideruler_textColor,Color.DKGRAY);dividerColor=typedArray.getColor(R.styleable.slideruler_dividerColor,Color.BLACK);indicatrixColor=typedArray.getColor(R.styleable.slideruler_indicatrixColor,Color.BLACK);minValue=typedArray.getInteger(R.styleable.slideruler_min_value,0);maxValue=typedArray.getInteger(R.styleable.slideruler_max_value,199000);currentValue=typedArray.getInteger(R.styleable.slideruler_current_value,10000);minUnitValue=typedArray.getInteger(R.styleable.slideruler_min_unitValue,1000);minCurrentValue=typedArray.getInteger(R.styleable.slideruler_min_currentValue,1000);showItemSize=typedArray.getInteger(R.styleable.slideruler_show_itemSize,30);marginCursorData=typedArray.getDimensionPixelSize(R.styleable.slideruler_margin_cursor_data,(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,10,getResources().getDisplayMetrics()));longCursor=typedArray.getDimensionPixelSize(R.styleable.slideruler_longCursor,(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,25,getResources().getDisplayMetrics()));shortCursor=typedArray.getDimensionPixelSize(R.styleable.slideruler_shortCursor,(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,15,getResources().getDisplayMetrics()));scroller=new Scroller(context);mDetector=new GestureDetector(context,myGestureListener);//初始化PaintlinePaint=new Paint();linePaint.setAntiAlias(true);linePaint.setTextAlign(Paint.Align.CENTER);linePaint.setStyle(Paint.Style.STROKE);linePaint.setTextSize(textSize);//检查当前值是不是正确值checkCurrentValue();}
其次自定义View也好自定义控价也好
protected void onMeasure(int widthMeasureSpec, int heigh)
也是蛮重要的所以照例也讲讲,用来确定控件的大小,代码如下:
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthModel=MeasureSpec.getMode(widthMeasureSpec);int heightModel=MeasureSpec.getMode(heightMeasureSpec);int widthSize=MeasureSpec.getSize(widthMeasureSpec); int heightSize=MeasureSpec.getSize(heightMeasureSpec);int width;int height;if(widthModel==MeasureSpec.EXACTLY){width=widthSize;}else{width=wrapcontentWidth;}if(heightModel==MeasureSpec.EXACTLY){height=heightSize;}else{height=(getPaddingBottom()+getPaddingTop()+(wrapcontentHeight/4));}setMeasuredDimension(width,height);}
代码的意思也很简单,当MeasureSpec里的specMode类型是EXACTLY时,即设置了明确的值或者是MATCH_PARENT时,就直接把MeasureSpec.getSize()的值赋进去,如果不是即为WARP_CONTENT时,就直接赋给屏幕的宽高。控件的宽高都是同一样的做法。
当控件大小确定之后,我们再利用
protected void onSizeChanged(int w, int h, int oldw, int oldh)
进行一些变量的赋值,代码如下:
@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {//计算每个刻度的间距marginWidth=getWidth()/showItemSize;//开始时的距离rollingWidth=(int)(marginWidth*cursorNum());//整个控件的宽度slideRulerWidth=(maxValue/minUnitValue)*marginWidth;super.onSizeChanged(w, h, oldw, oldh);}
到此我们就可以在onDraw(Canvas canvas)方法里画出初始的界面,而以后的动态只是通过不断的改变数值再进行绘画而已,代码如下:
@Override
protected void onDraw(Canvas canvas){//画最基础的两条线drawBaseView(canvas);//画初始的界面drawBaseLine(canvas);
}//画最基础的两条线
public void drawBaseLine(Canvas canvas){//画中间的线linePaint.setColor(indicatrixColor);canvas.drawLine(getWidth()/2,0,getWidth()/2,getHeight(),linePaint);//画底部的直线linePaint.setColor(dividerColor);canvas.drawLine(0,getHeight(),slideRulerWidth,getHeight(),linePaint);
}//画初始的界面
public void drawBaseView(Canvas canvas){//整数刻度的个数int integerWidth= (int)Math.rint((currentValue-minValue)/minUnitValue);//剩余不整一个刻度的数值int residueWidth=(currentValue-minValue)%minUnitValue;//开始画图的X轴位置int startCursor=(getWidth()/2)-(marginWidth*integerWidth)-(int)(marginWidth*(float)residueWidth/minUnitValue);for(int i=0;i<(maxValue/minUnitValue)+1;i++){float xValue=startCursor+(marginWidth*i);if(i%10==0){//画长刻度linePaint.setColor(textColor);canvas.drawText((minCurrentValue*i)+"",xValue,getHeight()-longCursor-marginCursorData,linePaint);linePaint.setColor(dividerColor);canvas.drawLine(xValue,getHeight(),xValue,getHeight()-longCursor,linePaint);}else{//画短刻度canvas.drawLine(xValue,getHeight(),xValue,getHeight()-shortCursor,linePaint);}}}
在drawBaseView()方法里,也很简单,就是在二分之一宽度,画一条直线,然后在控价的底部画出宽度为整个控件的宽度的底线。接着在下方法里
drawBaseView(Canvas canvas)
首先用当前值(currentValue)-最小值(minValue)之后再除于最小单位值(minUnitValue)以获取整数刻度的个数
因为有余数的情况,我们再当前值(currentValue)-最小值(minValue)之后求余与最小单位值(minUnitValue)以获取余数
接着我们要获取我们画图的X轴开始的位置,因为最小值只能滑到中间,所以开始的位置为控件一半的宽度(getWidth()/2)
减去计算每个刻度的间距(marginWidth)乘以整数刻度的个数(integerWidth)即marginWidth*integerWidth再减去余数对应所产生的X轴距离即 :
(int)(marginWidth*(float)residueWidth/minUnitValue)
4、再通过For循环刻度的个数,不同的进行刻度的绘画,当i%10==0时即为一个大的单位刻度否者为一个小的单位刻度,具体代码我上面已有注释,原理和画中间线一直就不在赘述。
到此我们就已经把自定义控价静态的部分写完了,效果如下:

接着我们用GestureDetector绑定手势事件,根据回调手势事件的方法来改变数据和刷新页面,在GestureDetector里,我们只会回调:
//手指在触摸屏上滑动
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)//手指在触摸屏上迅速移动,并松开的动作
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
这两个方法就可以了。
具体代码如下:
private GestureDetector.SimpleOnGestureListener myGestureListener =new GestureDetector.SimpleOnGestureListener(){@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {//滑动刷新UIupdateView(rollingWidth+(int)distanceX,isScrollingState);return true;}@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {//快速滑动的动画scroller.fling(rollingWidth,0,(int)(-velocityX/1.5),0,0,(maxValue/minUnitValue)*marginWidth,0,0);return true;}};//动态更新滑动View
public void updateView(int srcollWidth,int action){if(action==isScrollingState){//正在滑动状态(onScroll())rollingWidth=srcollWidth;float itemNum=(float)srcollWidth/marginWidth;currentValue=(int)(minUnitValue*itemNum);}else if(action==fastScrollState){//快速一滑(onFling())rollingWidth=srcollWidth;int itemNum=(int)Math.rint((float)rollingWidth/marginWidth);currentValue=(minUnitValue*itemNum);}else if(action==finishScrollState){//结束滑动(ACTION_UP)int itemNum=(int)Math.rint((float)rollingWidth/marginWidth);currentValue=minUnitValue*itemNum;}//判断是否在最小选择值if(currentValue<=minCurrentValue){rollingWidth=(minCurrentValue/minUnitValue)*marginWidth;currentValue=minCurrentValue;}//判断是否在最大值if(currentValue>=maxValue){rollingWidth=marginWidth*allCursorNum();currentValue=maxValue;}//回调数值if(slideRulerDataInterface!=null){slideRulerDataInterface.getText(currentValue+"");}invalidate();
}
当我们滑动我们的控件是,就会回调GestureDetector里的onScroll()方法,然后rollingWidth+(int)distanceX即当前滑动的宽度(rollingWidth)加上滑动产生的宽度(distanceX)为动态产生的宽度,再除于计算每个刻度的间距(marginWidth)从而得到刻度的数量,有了刻度的数量即可得到当前值
currentValue=(int)(minUnitValue*itemNum);
有了当前值调用invalidate();刷新onDraw()即可完成连续滑动时动态绘制。
当我们快速一划时,就会回调GestureDetector里的onFling()方法,在方法里用
scroller.fling(rollingWidth,0,(int)(-velocityX/1.5),0,0,(maxValue/minUnitValue)*marginWidth,0,0);
以实现滑动有一个好的动画效果,此时在如下代码里:
@Overridepublic void computeScroll() {if(scroller.computeScrollOffset()){//快滑刷新UIupdateView(scroller.getCurrX(),fastScrollState);}}
scroller.computeScrollOffset()==true;而scroller.getCurrX()
就相当于为动态产生的滑动宽度剩下的也是调用updateView()方法不断的刷新,当
scroller.computeScrollOffset()==false
就滑动动画结束了。
最后当我们滑动结束手指抬起时:
@Overridepublic boolean onTouchEvent(MotionEvent event) {switch(event.getAction()){case MotionEvent.ACTION_UP:updateView(0,finishScrollState);default:mDetector.onTouchEvent(event);break;}return true;}
我们也要掉updateView(),以保持滑动的最后结构都指在指针上。
源码地址:
https://github.com/gaojuanjuan/MaterialDesign_V7
相关文章:

Android仿京东金融的数值滚动尺功能
自定义数值滚动尺,这个用的还是挺多的,例如京东金融的通过滚动尺选择金额等,而这次就是高仿京东金融的数值滚动尺。首先看看下效果图,如下:首先先给你们各个变量的含义,以免在后面的讲解中不知变量的意思,代码如下://最…...

Nginx 和 Tomcat 实现负载均衡
Nginx 和 tomcat 实现负载均衡 🏆荣誉认证:51CTO博客专家博主、TOP红人、明日之星;阿里云开发者社区专家博主、技术博主、星级博主。 💻微信公众号:微笑的段嘉许 📌本文由微笑的段嘉许原创! &am…...

【万能排序之qsort、b_sort 、s_sort】
文章目录前言:star:qsort函数函数参数qsort函数的使用:star:模拟实现万冒泡排序函数参数模拟实现b_sort注意点:star:模拟实现万能选择排序函数参数模拟实现s_sort最后前言 我们所熟悉的冒泡排序,选择排序,插入排序,二分排序等都是基于给定的一…...

利用InceptionV3实现图像分类
最近在做一个机审的项目,初步希望实现图像的四分类,即:正常(neutral)、涉政(political)、涉黄(porn)、涉恐(terrorism)。有朋友给推荐了个github上…...

【Java】CAS锁
一、什么是CAS机制(compare and swap) 1.概述 CAS的全称为Compare-And-Swap,直译就是对比交换。是一条CPU的原子指令,其作用是让CPU先进行比较两个值是否相等,然后原子地更新某个位置的值。经过调查发现,…...
Linux服务器配置系统安全加固方法
1. SSH空闲超时时间建议为: 600-900 解决方案: 在【/etc/ssh/sshd_config】文件中设置【ClientAliveInterval】设置为600到900之间 vim /etc/ssh/sshd_config #将 ClientAliveInterval 参数值设置为 900 2. 修改检查SSH密码修改最小间隔 解决方案: 在【/etc/login.defs】文件…...

Codeforces Round #850 (Div. 2, based on VK Cup 2022 - Final Round)(A~E)
t宝酱紫喜欢出这种分类讨论的题?!A1. Non-alternating Deck (easy version)给出n张牌,按照题目给的顺序分给两人,问最后两人手中各有几张牌。思路:模拟。AC Code:#include <bits/stdc.h>typedef long…...

qt源码--信号槽
本篇主要从Qt信号槽的连接、断开、调用、对象释放等方面展开; 1.信号建立连接过程 connect有多个重载函数,主要是为了方便使用者,比较常用的有2种方式: a. QObject::connect(&timer, &QTimer::timeout, &loop, &am…...

RecycleView详解
listview缓存请看: listview优化和详解RecycleView 和 ListView对比:使用方法上ListView:继承重写 BaseAdapter,自定义 ViewHolder 与 converView优化。RecyclerView: 继承重写 RecyclerView.Adapter 与 RecyclerView.ViewHolder。设置 Layou…...

【算法】最短路算法
😀大家好,我是白晨,一个不是很能熬夜😫,但是也想日更的人✈。如果喜欢这篇文章,点个赞👍,关注一下👀白晨吧!你的支持就是我最大的动力!Ǵ…...

< Linux > 进程间通信
目录 1、进程间通信介绍 进程间通信的概念 进程间通信的本质 进程间通信的分类 2、管道 2.1、什么是管道 2.2、匿名管道 匿名管道的原理 pipe函数 匿名管道使用步骤 2.3、管道的读写规则 2.4、管道的特点 2.5、命名管道 命名管道的原理 使用命令创建命名管道 mkfifo创建命名管…...

学习 Python 之 Pygame 开发魂斗罗(二)
学习 Python 之 Pygame 开发魂斗罗(二)魂斗罗的需求开始编写魂斗罗1. 搭建主类框架2. 设置游戏运行遍历和创建窗口3. 获取窗口中的事件4. 创建角色5. 完成角色更新函数魂斗罗的需求 魂斗罗游戏中包含很多个物体,现在要对这些物体进行总结 类…...

户籍管理系统测试用例
目录 一、根据页面的不同分别设计测试用例 登录页面 用户信息列表 用户编辑页面 用户更新页面 二、根据目的不同分别设计测试用例 一、根据页面的不同分别设计测试用例 上图是针对一个网站的测试,按照页面的不同分别来设计对应的测试用例。 登录页面 用户信息列…...

(三)代表性物质点邻域的变形分析
本文主要内容如下:1. 伸长张量与Cauchy-Green 张量2. 线元长度的改变2.1. 初始/当前构型下的长度比2.2. 主长度比与 Lagrange/Euler 主方向2.3. 初始/当前构型下任意方向的长度比3. 线元夹角的改变4. 面元的改变5. 体元的改变1. 伸长张量与Cauchy-Green 张量 由于变…...

Stream操作流 练习
基础数据:Data AllArgsConstructor NoArgsConstructor public class User {private String name;private int age;private String sex;private String city;private Integer money; static List<User> users new ArrayList<>();public static void m…...

【模拟集成电路】宽摆幅压控振荡器(VCO)设计
鉴频鉴相器设计(Phase Frequency Detector,PFD)前言一、VCO工作原理二、VCO电路设计VCO原理图三、压控振荡器(VCO)测试VCO测试电路图瞬态测试(1)瞬态输出(2)局部放大图&a…...
《英雄编程体验课》第 13 课 | 双指针
文章目录 零、写在前面一、最长不重复子串1、初步分析2、朴素算法3、优化算法二、双指针1、算法定义2、算法描述3、条件1)单调性2)时效性三、双指针的应用1、前缀和问题2、哈希问题3、K 大数问题零、写在前面 该章节节选自 《夜深人静写算法》,主要讲解最基础的枚举算法 ——…...

DS期末复习卷(十)
一、选择题(24分) 1.下列程序段的时间复杂度为( A )。 i0,s0; while (s<n) {ssi;i;} (A) O(n^1/2) (B) O(n ^1/3) © O(n) (D) O(n ^2) 12…xn xn^1/2 2.设某链表中最常用的…...

QT+OpenGL模板测试和混合
QTOpenGL模板测试和混合 本篇完整工程见gitee:QtOpenGL 对应点的tag,由turbolove提供技术支持,您可以关注博主或者私信博主 模板测试 当片段着色器处理完一个片段之后,模板测试会开始执行。和深度测试一样,它可能会丢弃片段&am…...
《英雄编程体验课》第 11 课 | 前缀和
文章目录 零、写在前面一、概念定义1、部分和2、朴素做法3、前缀和4、前缀和的边界值5、边界处理6、再看部分和二、题目描述1、定义2、求解三、算法详解四、源码剖析五、推荐专栏六、习题练习零、写在前面 该章节节选自 《算法零基础100讲》,主要讲解最基础的算法 —— 前缀和…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...

接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...

C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...