Android仿京东金融的数值滚动尺功能

news2024/10/2 20:29:55

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

首先先给你们各个变量的含义,以免在后面的讲解中不知变量的意思,代码如下:

//最小值
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);

        //初始化Paint
        linePaint=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)

也是蛮重要的所以照例也讲讲,用来确定控件的大小,代码如下:

    @Override
    protected 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)

进行一些变量的赋值,代码如下:

    @Override
    protected 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)
  1. 首先用当前值(currentValue)-最小值(minValue)之后再除于最小单位值(minUnitValue)以获取整数刻度的个数

  1. 因为有余数的情况,我们再当前值(currentValue)-最小值(minValue)之后求余与最小单位值(minUnitValue)以获取余数

  1. 接着我们要获取我们画图的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(){
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            //滑动刷新UI
            updateView(rollingWidth+(int)distanceX,isScrollingState);
            return true;
        }
        @Override
        public 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();
} 
  1. 当我们滑动我们的控件是,就会回调GestureDetector里的onScroll()方法,然后rollingWidth+(int)distanceX即当前滑动的宽度(rollingWidth)加上滑动产生的宽度(distanceX)为动态产生的宽度,再除于计算每个刻度的间距(marginWidth)从而得到刻度的数量,有了刻度的数量即可得到当前值

currentValue=(int)(minUnitValue*itemNum);

有了当前值调用invalidate();刷新onDraw()即可完成连续滑动时动态绘制。

  1. 当我们快速一划时,就会回调GestureDetector里的onFling()方法,在方法里用

scroller.fling(rollingWidth,0,(int)(-velocityX/1.5),0,0,(maxValue/minUnitValue)*marginWidth,0,0);

以实现滑动有一个好的动画效果,此时在如下代码里:

  @Override
  public void computeScroll() {
    if(scroller.computeScrollOffset()){
        //快滑刷新UI
        updateView(scroller.getCurrX(),fastScrollState);
    }
  }
scroller.computeScrollOffset()==true;而scroller.getCurrX()

就相当于为动态产生的滑动宽度剩下的也是调用updateView()方法不断的刷新,当

scroller.computeScrollOffset()==false

就滑动动画结束了。

  1. 最后当我们滑动结束手指抬起时:

    @Override
    public 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/369703.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高/低压供配电系统设计——安科瑞变电站电力监控系统的应用

摘 要&#xff1a;在电力系统的运行过程中&#xff0c;变电站作为整个电力系统的核心&#xff0c;在保证电力系统可靠的运行方面起着至关重要的作用&#xff0c;基于此需对变电站监控系统的特点进行分析&#xff0c;结合变电站监控系统的功能需求&#xff0c;对变电站电力监控系…

载誉而归!昂视荣膺CAIMRS 2023「自动化创新奖」

2月24日&#xff0c;由中国工控网举办的第二十一届自动化及数字化年会在苏州希尔顿酒店隆重举行&#xff0c;昂视受邀参加本次活动。会上&#xff0c;中国工控网发布了第二十一届自动化及数字化年度自动化创新奖&#xff0c;昂视凭借LP8000系列超高精度3D激光轮廓仪斩获“自动化…

浅谈`AI`的那些事-环境搭建

人工智能(AI)-环境搭建 目录导航人工智能(AI)-环境搭建1. 为什么人工智能(AI)首选Python&#xff1f;2. python在AI上的优势2.1 python在AI上的优势2.1.1 语法简单&#xff0c;编码少。2.1.2 内置了几乎所有的AI项目库2.1.3 开源和可用于广泛编程2.2 python的特点3. PyTorch环境…

JVM详解——垃圾回收

如果有兴趣了解更多相关内容的话&#xff0c;可以看看我的个人网站&#xff1a;耶瞳空间 GC&#xff1a;垃圾收集(Gabage Collection)&#xff0c;内存处理是编程人员容易出现问题的地方&#xff0c;忘记或者错误的内存。不当的回收可能会导致程序或系统的不稳定甚至崩溃&…

PHP面向对象05:MVC和smarty

PHP面向对象05&#xff1a;MVC 和 smarty一、MVC思想二、MVC代码设计三、项目单一入口四、Smarty模板技术1. 模板技术原理2. Smarty简单使用3. Smarty配置五、Smarty模板变量六、Smarty内置函数七、Smarty外部函数一、MVC思想 MVC思想&#xff0c;是一种基于面向对象思想形成的…

如何将本地文件自动备份到百度网盘?

如何将本地文件自动备份到百度网盘&#xff1f;说到网盘的使用&#xff0c;大家第一个想到的肯定是百度网盘&#xff0c;百度网盘第一个提出网盘这个概念&#xff0c;相信很多小伙伴都是百度网盘的忠实用户&#xff0c;大家也非常喜欢使用百度网盘来存储文件。为什么百度网盘深…

Apache Hadoop生态部署-kafka单机安装

目录 Apache Hadoop生态-目录汇总-持续更新 一&#xff1a;安装包准备 二&#xff1a;安装与常用配置 2.1&#xff1a;下载解压安装包 2.2&#xff1a;配置环境变量 2.3&#xff1a;配置修改server.properties 三&#xff1a;维护kafka 3.1 编写维护脚本 3.2 启动kafk…

el-cascader 级联选择器懒加载的使用及回显 + 点击任意一级都能返回

需要实现的需求 数据渲染使用懒加载点击任意一级都可返回&#xff0c;不需要一直点到最后一级编辑或者查看功能&#xff0c;回显之前选择的数据 实例解析 dom 元素 <el-cascaderv-model"value":options"options":props"props":key"n…

华为服务器驱动下载及安装

1.服务器技术支持网站 https://support.xfusion.com/support/#/zh/home 2.选择软件下载 3.选择服务器型号 4.选择驱动 5.根据需求选择驱动 例如红帽7.4系统 6.安装驱动 自动安装驱动步骤&#xff1a; 1)使用BMC虚拟光驱挂载onboard_driver_xxx.iso: 2)mount /dev/sr0 /mnt …

【vue3】ref , reactive ,toRef ,toRefs 使用和理解

这篇文章是基于理解写的&#xff0c;仅助于理解&#xff0c;如有任何错误之处&#xff0c;感谢指正&#xff01; 文章目录一.ref的使用1. ref的功能主要有两个&#xff1a;2.使用ref注意事项二.reactive的使用三.使用ref 和 reactive 实现双向数据绑定四.toRef 和 toRefs 的使用…

ARM uboot 源码分析7 - uboot的命令体系

一、uboot 命令体系基础 1、使用 uboot 命令 (1) uboot 启动后进入命令行环境下&#xff0c;在此输入命令按回车结束&#xff0c;uboot 会收取这个命令然后解析&#xff0c;然后执行。 2、uboot 命令体系实现代码在哪里 (1) uboot 命令体系的实现代码在 uboot/common/cmd_xx…

PA的包络跟踪电源

对于传统PA&#xff0c;电源一般设计成固定电压供电&#xff0c;电压不可变化。这种设计对于GSM和GPRS等使用恒定包络GMSK调制的系统来说&#xff0c;PA的效率是比较高的。 ​但随着追求更高的数据吞吐量以及更高的频谱效率&#xff0c;在现代的通信系统中使用了更复杂的调制方…

react定义css样式,使用less,css模块化

引入外部 css文件 import ./index.css此时引入的样式是全局样式 使用less 安装 npm i style-loader css-loader sass-loader node-sass -D生成config文件夹 npm run eject配置 以上代码运行完&#xff0c;会在根目录生成config文件夹 进入 config > webpack.config.js 查找…

基于jeecgboot的flowable为uniapp适配的流程页面调整

为了满足在uniapp上也能进行webview的流程页面操作与显示&#xff0c;需要对流程页面&#xff0c;特别是record/index.vue进行修改与适配。 一、对各个内容的宽带进行调整 主要是样式的调整 <el-col :span"16" :offset"4" 都修改成<el-col :span…

倾向得分匹配只看这篇就够了

一、倾向得分匹配法说明 倾向得分匹配模型是由Rosenbaum和Rubin在1983年提出的&#xff0c;首次运用在生物医药领域&#xff0c;后来被广泛运用在药物治疗、计量研究、政策实施评价等领域。倾向得分匹配模型主要用来解决非处理因素&#xff08;干扰因素&#xff09;的偏差。 …

协作对象死锁及其解决方案

协作对象死锁及其解决方案 1.前言 在遇到转账等的需要保证线程安全的情况时&#xff0c;我们通常会使用加锁的方式来保证线程安全&#xff0c;但如果无法合理的使用锁&#xff0c;很可能导致死锁。或者有时我们使用线程池来进行资源的使用&#xff0c;如调用数据库&#xff0…

Swagger狂神学习笔记

学习目标&#xff1a; 了解Swagger的概念及作用 掌握在项目中集成Swagger自动生成API文档 前后端分离 前端 -> 前端控制层、视图层 后端 -> 后端控制层、服务层、数据访问层 前后端通过API进行交互 前后端相对独立且松耦合 产生的问题 前后端集成&#xff0c;前端或…

支持U盘数据、误删文件、硬盘数据 、回收站数据恢复的软件

好用的Windows数据恢复软件的标准 在数字和信息经济时代&#xff0c;数据是必不可少的。没有人可以承受由于意外删除、格式化和其他原因导致数据丢失的风险。与其在数据恢复服务上花费大量资金或花费大量时间努力自己取回数据&#xff0c;用户更喜欢使用Windows数据恢复软件…

Ask林曦|来回答,30个你关心的日常问题(一)

在林曦老师的线上书法直播课上&#xff0c;上课前后的聊天时间里&#xff0c;时常有同学向林曦老师提问&#xff0c;这些问题涵盖了日常生活的诸多方面&#xff0c;从身体的保养&#xff0c;到快乐的法门&#xff0c;皆是大家感兴趣的&#xff0c;也都共同关切的。      暄…

破解票房之谜:为何高票房电影绕不过“猫眼们”?

如此火爆的春节档很多&#xff0c;如此毁誉参半的春节档鲜有。2023开年&#xff0c;集齐张艺谋、沈腾的《满江红》&#xff0c;以及有票房前作打底的《流浪地球2》接连两部春节档电影票房进入前十&#xff0c;为有些颓靡的中国电影市场注入了一针“强心剂”。与票房同样热闹起来…