精华内容
下载资源
问答
  • 自定义刻度尺进度条总结

    千次阅读 2018-04-18 11:04:25
    应项目需要我这边要定义个播放录音的控件,效果如图:这边先讲一下具体的初版思路吧1、首先我的思路是复杂的问题...组合成一个控件先上一下效果动图下面是上代码时间::首先来画刻度尺的部分刻度尺的部分是...
    应项目需要我这边要定义一个播放录音的控件,效果如图:

    这边先讲一下具体的初版思路吧

    1、首先我的思路是复杂的问题简单化,先把这个控件拆分为:上面的刻度和下面的进度条

    2、刻度可以根据录音的具体时间来画,下面的进度可以继承SeekBar来实现

    3、然后将两个控件的组合在一起放在一个自定义的Relativelayout中,组合成一个控件

    先上一下效果动图


    下面是上代码时间:

    一:首先来画刻度尺的部分

    刻度尺的部分是比较简单的这里我自定义了一个控件继承View然后同onDraw来画出这个刻度尺来

    如果要画出刻度尺则必须知道自定义控件在屏幕的中位置,下面是获取控件在屏幕中具体位置的方法

    下面是获取屏幕宽度的方法
    WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    DisplayMetrics dm = new DisplayMetrics();
    windowManager.getDefaultDisplay().getMetrics(dm);
    screenWidth = dm.widthPixels;
    
    下面是获取视图绝对坐标和相对坐标的方法
    getLocationOnScreen ,计算该视图在全局坐标系中的xy值,(注意这个值是要从屏幕顶端算起,也就是索包括了通知栏的高度)//获取在当前屏幕内的绝对坐标
    getLocationInWindow ,计算该视图在它所在的widnow的坐标xy值,//获取在整个窗口内的绝对坐标 (不是很理解= =)
    getLeft ,getTop,getBottom,getRight,这一组是获取相对在它父亲里的坐标
    如果在ActivityOnCreate()事件输出那些参数,是全为0,要等UI控件都加载完了才能获取到这些。
    
    可在onMeasure中中设置自定义控件的高度
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    
        int width;
        int height;
    
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            height = Math.min(mMinHeight, heightSize);
        }
        width = widthSize;
        setMeasuredDimension(width, height);
    }
    
    控件的宽和高确定好之后接下来就是要画刻度值了,刻度值分为两部分,一部分是水平的横线另一部分是

    竖直的竖杠,横线比较简单至于竖线考虑到录音的时间是从一到无穷大的,于是这竖线的部分就需要分一分了

    首先是1-6s的时候分别画录取时间加一的竖线,大于6的时候则要将对录音的时间进行三等分或者两等分,把时间分的均匀一点就行了,上面的刻度只是个大概的显示所以尽力做到平均就行了。

    线画完之后就要画时间的显示了时间的显示如下

    将时间进行等分的处理函数

    private int courseTime(int record, int time) {
        if (record % 6 == 0) {//整除6则返回6的整倍数
            return record / 6 * time;
        } else if (record % 3 == 0) {//整除3
            if (time % 2 == 0) {//三等分的位置
                return (record / 3) * (time / 2);
            } else {//其他的位置
                return ((record / 3) * ((time + 1) / 2) - ((record / 3) * ((time - 1) / 2))) / 2 + ((record / 3) * ((time - 1) / 2));
            }
        } else if (record % 2 == 0) {//能整除2的数值
            if (time % 3 == 0) {//进行二等分的位置
                return (record / 2) * (time / 3);
            } else {//其他位置
                if ((record / 2) % 3 == 1) {//二等分之后再三等分进行余数的分配
                    if (time < 3) {//前半部分的二等分
                        return (record / 2 / 3 * time);
                    } else {//后半部分的二等分
                        return (record / 2 + record / 2 / 3 * (time - 3));
                    }
                } else {//同上
                    if ((record / 2) % 3 == 2) {
                        if (time < 3) {
                            return (record / 2 / 3 * time + time);
                        } else {
                            return (record / 2 + record / 2 / 3 * (time - 3) + (time - 3));
                        }
                    }
                }
            }
        } else {//质数的分配
            if (time == 0) {
                return 0;
            } else if (time == 3) {
                return record / 2;
            } else if (time == 6) {
                return record;
            } else if (time < 3) {
                if ((record / 2) % 3 == 1) {
                    return (record / 2 / 3 * time);
                } else if ((record / 2) % 3 == 2) {
                    return (record / 2 / 3 * time + time);
                } else {
                    return record / 2 / 3 * time;
                }
            } else {
                if ((record / 2 + 1) % 3 == 1) {
                    return (record / 2 + record / 2 / 3 * (time - 3));
                } else if ((record / 2) % 3 == 2) {
                    return (record / 2 + record / 2 / 3 * (time - 3) + (time - 3));
                } else {
                    return record / 2 + (record / 2 + 1) / 3 * (time - 3);
                }
            }
        }
        return 0;
    }
    

    直接获取绘制字符串的宽度
    textPaint.measureText(str)
    将int值的秒级数转换为时分秒格式
    private String getCourseTime(Integer seconds) {
        int day = seconds / (60 * 60 * 24);//换成天
        int hour = (seconds - (60 * 60 * 24 * day)) / 3600;//总秒数-换算成天的秒数=剩余的秒数    剩余的秒数换算为小时
        int minute = (seconds - 60 * 60 * 24 * day - 3600 * hour) / 60;//总秒数-换算成天的秒数-换算成小时的秒数=剩余的秒数    剩余的秒数换算为分
        int second = seconds - 60 * 60 * 24 * day - 3600 * hour - 60 * minute;//总秒数-换算成天的秒数-换算成小时的秒数-换算为分的秒数=剩余的秒数
        if (hour != 0) {
            return (hour >= 10 ? hour : "0" + hour) + ":" + (minute >= 10 ? minute : "0" + minute) + ":" + (second >= 10 ? second : "0" + second);
        } else if (minute != 0) {
            return (minute >= 10 ? minute : "0" + minute) + ":" + (second >= 10 ? second : "0" + second);
        } else {
            return "00:" + (second >= 10 ? second : "0" + second);
        }
    }
    
    然后直接在几道竖线下画平分的时间就行了,至此刻度尺就制作完毕了。

    二:接下来就是下面的进度条部分了,这个部分我是继承了SeekBar的自定义View制作的

    我们也分为三步

    1、首先是红色的游标

    把默认的游标去除

    android:duplicateParentState="true"
    android:thumb="@null"
    
    要画出完整的图标就必须把进度条的背景放大

    //设置控件的padding 给提示指示图标留出位置
    setPadding(linePadding, (int) Math.ceil(imageHeight) - 30, linePadding, 25);
    

    2、就是进度条的颜色、进度和上面的刻度尺要保持两端相同,我的思路是:①进度条的长度保持和上面的刻度尺长度相同 ②画一个图片来覆盖进度条默认的进度图片 ③画一个半透明的灰色横线来作为进度

    rectSeek = this.getProgressDrawable().getBounds();
    float bm_x = rectSeek.width() * getProgress() / getMax() + (linePadding - (int) Math.ceil(imageWidth) / 2);

    //设置控件的padding 给提示指示图标留出位置
    setPadding(linePadding, (int) Math.ceil(imageHeight) - 30, linePadding, 25);
    

    图片使用一个xml格式的shape做的可以添加起始、中部和结尾的颜色

    canvas.drawBitmap(seekbarThumbBitmap, 0, (int) Math.ceil(imageHeight) - 28, cursorImagePaint);//绘制假的进度条背景色

    canvas.drawLine(0, (int) Math.ceil(imageHeight) - 28, bm_x + (int) Math.ceil(imageWidth) / 2, (int) Math.ceil(imageHeight) - 28, seekbarThumbPaint);

    3、下面一个就是画tag的功能了,这个主要就是画一个红色的矩形

    根据打印tag的时间来确定红色矩形的位置就可以了

    mBarRect.left = getPaddingLeft();
    mBarRect.right = getWidth() - getPaddingRight();
    //final boolean isLayoutRtl = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
    float cx;
    //float cy = getHeight() / 2 - getPaddingBottom() / 2 - 2;
    float barRectwidth = mBarRect.width() - 50;
    
    cx = mBarRect.left + barRectwidth
            * (mMarks.get(i).postion / (float) mDuration);
        //}
        //radius = getResources().getDimension(R.dimen.panit_line_space) / 3;
    //canvas.drawCircle(cx, cy, radius, mShapePaint);
        //canvas.drawBitmap(mBitmap, cx, cy, mPaintTag);
    canvas.drawRect(cx, (int) Math.ceil(imageHeight) - 28, cx + tagWidth, (int) Math.ceil(imageHeight) - 28 + tagHeight, tagPaint);
    
    至此这个控件就基本结束了,下面就是要合入record的代码中了。
    展开全文
  • 尺子数据图

    2019-02-25 14:04:47
    种标准的毫米为单位的尺子模型,供大家下载,打印的时候选择1:1的比例打印即可,这样就直接可以看到纸质打印的尺子。
  • 自定义View实现跟随手指滚动的刻度尺,实现了类似SeekBar的滑动选中效果。项目地址,欢迎star! UI图: 功能: 通过设置最小值跟最大值的范围,以及offset值。View将根据这些数据去计算出需要几个小刻度和几个长...

    一 基础:

    自定义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分别表示那个区间:

    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)坐标就好了。

    有好多学习的资源~
    我的Github
    我的掘金
    我的简书
    我的CSDN

    展开全文
  • 最近一直在学自定义view,博大精深,感觉如果向底层看的话,功力不够且时间不允许,所以一直都是停留在怎么实现...这是自定义View学习的第三篇,学习主要是在项目的基础上来学,并不是点点的基础死扣,因为你看懂

    最近一直在学自定义view,博大精深,感觉如果向底层看的话,功力不够且时间不允许,所以一直都是停留在怎么实现自定义view上,而为何会这么实现以及差异并没有考虑的很清晰,因为面向对象编程以及封装,都是让我们将功能抽出来使用,而不是去考虑每个功能里是怎么实现的,所以很苦恼,还是先实现吧,至于原理慢慢理解吧!

    这是自定义View学习的第三篇,学习主要是在项目的基础上来学,并不是一点点的基础死扣,因为你看懂,敲出来一个自定义view的代码,你就会慢慢有所理解,包括中间的一些计算,所以还是建议大家先去敲代码,自己写不出来,就把别人的代码敲一遍,去理解一遍,比仅仅去看会领悟的更多,这就有点书读百遍,其义自现的意思,哈哈,一点浅见,谁让我笨呢。

    今天讲一下自定义刻度尺的实现,咋听刻度尺,难度并不高,但是对于刚刚开始动手敲,摆脱Ctrl+c/Ctrl+z的同学来说还是有些难度的。

    看看我的实现效果吧:

    这里写图片描述

    横向显示10cm,模拟器暂时找不到横屏,就这样吧!@_@

    思路呢?

    刚开始敲的时候,先明确思路,磨刀不误砍柴工,进去就敲最后敲得自己也糊涂了!

    1:画刻度尺
    2:画刻度数
    3:画红线标记
    4:处理滑动

    画刻度尺

     canvas.save();
            for (int i = min; i < max; i++) {
                if (i % 10 == 0) {
                    //起点x坐标10像素,画厘米线
                    canvas.drawLine(10, 0, 10, 72, mLinePaint);
                    String text = i / 10 + "";
                    Rect rect = new Rect();
                    float txtWidth = mTextPaint.measureText(text);
                    mTextPaint.getTextBounds(text, 0, text.length(), rect);
                    canvas.drawText(text, 10 - txtWidth / 2, 72 + rect.height() + 10, mTextPaint);
                } else if (i % 5 == 0) {
                    //每隔0.5cm画间隔线
                    canvas.drawLine(10, 0, 10, 64, mLinePaint);
                } else {
                    //画毫米线
                    canvas.drawLine(10, 0, 10, 48, mLinePaint);
                }
                //每隔18像素移动一次,达到画线效果
                canvas.translate(18, 0);
            }
            canvas.restore();

    画刻度数

    if (i % 10 == 0) {
                    //起点x坐标10像素,画厘米线
                    canvas.drawLine(10, 0, 10, 72, mLinePaint);
                    //计算刻度数
                    String text = i / 10 + "";
                    Rect rect = new Rect();
                    //获取文本宽度
                    float txtWidth = mTextPaint.measureText(text);
                    mTextPaint.getTextBounds(text, 0, text.length(), rect);
                    //画字
                    canvas.drawText(text, 10 - txtWidth / 2, 72 + rect.height() + 10, mTextPaint);
                }

    画红线标记

    看效果图:

    这里写图片描述

    很明显,是一条线,外加一个圆。计算好距离就ok。

    //画线
     canvas.drawLine(progrees, 0, progrees, 160, mRulerPaint);
     //线下画圆
            canvas.drawCircle(progrees, 170, 10, mRulerPaint);
            BigDecimal bd = new BigDecimal((progrees - 18) / 180);
            bd = bd.setScale(1, BigDecimal.ROUND_HALF_UP);
            //计算刻度数,保留一位小数,单位cm
            mTextPaint.setTextSize(48);
            canvas.drawText(bd.floatValue() + "cm", 500, 400, mTextPaint);

    progrees是手指触摸的x轴坐标,线高160,圆心y轴坐标170,半径10即可。

    处理滑动

     @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    isCanMove = true;
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (!isCanMove) {
                        return false;
                    }
                    //前面0坐标线从10像素开始,故而计算的时候要-10
                    float x = event.getX() - 10;
                    progrees = x;
                    //刷新
                    invalidate();
                    break;
            }
            return true;
        }

    绘画步骤就是这么多。

    完整代码:

    package com.example.com.testview;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Rect;
    import android.graphics.RectF;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.View;
    
    import java.math.BigDecimal;
    
    /**
     * 刻度尺
     * Created by Administrator on 2016/9/14.
     */
    public class ScaleView extends View {
    
        private Paint mLinePaint;
        private Paint mTextPaint;
        private Paint mRulerPaint;
        private float progrees = 10;
        private int max = 101;
        private int min = 0;
        private boolean isCanMove;
    
        public ScaleView(Context context) {
            super(context);
            init();
        }
    
        public ScaleView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public ScaleView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            mLinePaint = new Paint();
            mLinePaint.setColor(Color.CYAN);
            mLinePaint.setAntiAlias(true);//抗锯齿
            mLinePaint.setStyle(Paint.Style.STROKE);
            mLinePaint.setStrokeWidth(4);
            mTextPaint = new Paint();
            mTextPaint.setColor(Color.CYAN);
            mTextPaint.setAntiAlias(true);
            mTextPaint.setStyle(Paint.Style.FILL);
            mTextPaint.setStrokeWidth(2);
            mTextPaint.setTextSize(48);
            //
            mRulerPaint = new Paint();
            mRulerPaint.setAntiAlias(true);
            mRulerPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            mRulerPaint.setColor(Color.RED);
            mRulerPaint.setStrokeWidth(4);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            setMeasuredDimension(setMeasureWidth(widthMeasureSpec), setMeasureHeight(heightMeasureSpec));
        }
    
        private int setMeasureHeight(int spec) {
            int mode = MeasureSpec.getMode(spec);
            int size = MeasureSpec.getSize(spec);
            int result = Integer.MAX_VALUE;
            switch (mode) {
                case MeasureSpec.AT_MOST:
                    size = Math.min(result, size);
                    break;
                case MeasureSpec.EXACTLY:
                    break;
                default:
                    size = result;
                    break;
            }
            return size;
        }
    
        private int setMeasureWidth(int spec) {
            int mode = MeasureSpec.getMode(spec);
            int size = MeasureSpec.getSize(spec);
            int result = Integer.MAX_VALUE;
            switch (mode) {
                case MeasureSpec.AT_MOST:
                    size = Math.min(result, size);
                    break;
                case MeasureSpec.EXACTLY:
                    break;
                default:
                    size = result;
                    break;
            }
            return size;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.save();
            for (int i = min; i < max; i++) {
                if (i % 10 == 0) {
                    canvas.drawLine(10, 0, 10, 72, mLinePaint);
                    String text = i / 10 + "";
                    Rect rect = new Rect();
                    float txtWidth = mTextPaint.measureText(text);
                    mTextPaint.getTextBounds(text, 0, text.length(), rect);
                    canvas.drawText(text, 10 - txtWidth / 2, 72 + rect.height() + 10, mTextPaint);
                } else if (i % 5 == 0) {
                    canvas.drawLine(10, 0, 10, 64, mLinePaint);
                } else {
                    canvas.drawLine(10, 0, 10, 48, mLinePaint);
                }
                canvas.translate(18, 0);
            }
            canvas.restore();
            canvas.drawLine(progrees, 0, progrees, 160, mRulerPaint);
            canvas.drawCircle(progrees, 170, 10, mRulerPaint);
            BigDecimal bd = new BigDecimal((progrees - 18) / 180);
            bd = bd.setScale(1, BigDecimal.ROUND_HALF_UP);
            mTextPaint.setTextSize(48);
            canvas.drawText(bd.floatValue() + "cm", 500, 400, mTextPaint);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    isCanMove = true;
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (!isCanMove) {
                        return false;
                    }
                    float x = event.getX() - 10;
                    progrees = x;
                    invalidate();
                    break;
            }
            return true;
        }
    }
    

    布局:

     <com.example.com.testview.ScaleView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="4dp"/>

    现在看一下横向效果:

    这里写图片描述

    代码就不上传了,上面就是。

    展开全文
  • 具体思路是把个view分成三段,当总长度&amp;amp;amp;gt;... 这次除了无限滚动还我上次写的多了手指滑动速度的判断,去掉了滚动结束自动归位到对应刻度的效果,代码中除了极端情况默认...

    具体思路是把一个view分成三段,当总长度>=40个刻度向左滚动,滚动到2/3的时候view移动到1/3出然后刷新显示的刻度这时为第一页,一次更新页数,当向右滚动的时候滚动且不为第一页则每滚动到1/3处view移动到2/3处。之后在添加一些首页和最后一页的判断。
    这一次除了无限滚动还比我上一次写的多了手指滑动速度的判断,去掉了滚动结束自动归位到对应刻度的效果,代码中除了极端情况默认使用的view显示的刻度为30个,也就是说除了开始和结束,显示的位置始终在10~20之间,向右拖动图示:
    无标题.png

    效果展示:
    GIF.gif

    核心代码

    package com.zqb.scrolldividingrule;
    
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.os.Handler;
    import android.os.Looper;
    import android.support.annotation.Nullable;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.VelocityTracker;
    import android.view.View;
    import android.widget.Scroller;
    import java.util.ArrayList;
    
    /**
     * 横向滚动的刻度尺
     * Created by zhangqingbin on 2018/1/5.
     */
    
    public class ScrollDividingRuleView extends View {
    
      private Scroller mScroller;
      private Paint mPaint;
      private VelocityTracker mVelocityTracker;
      private OnScrollListener mListener;
      private int mScaleMargin; //刻度间距
      private float mScaleWidth; //总刻度宽度
      private float mTextSize;//文字大小
      private ArrayList<String> mTextList;//刻度文字
      private ArrayList<String> mTotalTextList;//所有的刻度
      private float mLineHeight;//线高度
      private int mRectHeight;//总高度
      private int mScrollLastX;
      private int mInitDistance;//初始距离
      private int mInitPosition;//初始位置
      private int mTextLineMargin;//文字距线的距离
      private int mPage;//当前为复用所在页
      private long mLastInstance;//余数刻度
      private long mStartMoney;//开始金额
      private int mUnit;//每个小格的单位
      private long mFinalMoney;//结束金额
    
      public ScrollDividingRuleView(Context context) {
        this(context, null);
      }
    
      public ScrollDividingRuleView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
      }
    
      public ScrollDividingRuleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setWillNotDraw(false);
        init(context, attrs);
      }
    
      private void init(Context context, @Nullable AttributeSet attrs) {
        if (attrs != null) {
          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")) {
                  float margin = Float.valueOf(value.substring(0, value.length() - 2));
                  mScaleWidth = Utils.dp2px(context, margin);
                } 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);
        //初始设置每个刻度间距为30px
        mScaleMargin = 20;
        mTextList = new ArrayList<>();
        mTotalTextList = new ArrayList<>();
        mScroller = new Scroller(context);
      }
    
      @Override
      protected 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);//画刻度中间横线
        super.onDraw(canvas);
      }
    
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int height = MeasureSpec.makeMeasureSpec(mRectHeight, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, height);
        //初始化开始位置
        mInitDistance = getMeasuredWidth() / 2 - mInitPosition * mScaleMargin * 10;
      }
    
      @SuppressLint("ClickableViewAccessibility")
      @Override
      public boolean onTouchEvent(MotionEvent event) {
        if (mVelocityTracker == null) {
          mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(event);
        int x = (int) event.getX();
        int startX = 0;
        switch (event.getAction()) {
          case MotionEvent.ACTION_DOWN:
            if (mScroller != null) {//重新初始化fling效果,防止下一次移动有初始速度
              mScroller.fling(mScroller.getFinalX(), mScroller.getFinalY(), 0, 0, 0,
                  (int) (mScaleWidth - mInitPosition * mScaleMargin * 10), 0, 0);
              mScroller.abortAnimation();
            }
            startX = x;
            mScrollLastX = x;
            return true;
          case MotionEvent.ACTION_MOVE:
            int dataX = mScrollLastX - x;
            smoothScrollBy(dataX, 0);
            mScrollLastX = x;
            return true;
          case MotionEvent.ACTION_CANCEL:
          case MotionEvent.ACTION_UP:
            dealActionUp(x - startX);
            return true;
        }
        return super.onTouchEvent(event);
      }
    
      /**
       * 处理手势抬起之后的操作
       *
       * @param i 根据正负判断方向
       */
      private void dealActionUp(int i) {
        mVelocityTracker.computeCurrentVelocity(1000);
        if (mScroller.getFinalX() >= mScaleMargin * 10 * 10 * 2 - 10 * mScaleMargin && i > 0) {
          if (!notifyDataChanged(0)) {
            dealFling();
          }
        } else if (mScroller.getFinalX() <= mScaleMargin * 10 * 10 - 10 * mScaleMargin && i < 0) {
          if (!notifyDataChanged(1)) {
            dealFling();
          }
        } else {
          dealFling();
        }
        long i1 = mPage > 0 ? mPage * 100 : 0;
        if (mListener != null) {
          mListener.onScaleScrollChanged(
              mScroller.getFinalX() / mScaleMargin + mInitPosition * 10 + i1 + mStartMoney);//返回滚动选中的位置
        }
        mVelocityTracker.clear();
        mVelocityTracker.recycle();
        mVelocityTracker = null;
      }
    
      /**
       * 处理手指抬起之后的减速滚动
       */
      private void dealFling() {
        int minX;//最小值
        if (mPage == 0) {
          minX = -mInitPosition * mScaleMargin * 10;
        } else {
          minX = -mInitPosition * mScaleMargin * 10 + mScaleMargin * 10 * 10 - mScaleMargin * 10;
        }
        int maxX;//最大值 根据位置和总数改变
        if (mPage == (mTotalTextList.size() - 20) / 10 - 1 || mTotalTextList.size() < 40) {
          maxX = (int) (mScaleWidth - mInitPosition * mScaleMargin * 10);
        } else {
          maxX = mScaleMargin * 10 * 20 - mScaleMargin * 10;
        }
        mScroller.fling(mScroller.getFinalX(), mScroller.getFinalY(),
            -(int) mVelocityTracker.getXVelocity(), 0, minX, maxX, 0, 0);
      }
    
      private void onDrawScale(Canvas canvas, Paint paint) {
        paint.setAntiAlias(true);
        paint.setTextSize(mTextSize);
        paint.setStyle(Paint.Style.FILL);
        for (int i = 0, k = 0; i < mTextList.size() * 10; i++) {
          if (i < mTextList.size() * 10 - 9) {
            if (i % 10 == 0) { //整值
              paint.setColor(Color.GRAY);
              canvas.drawLine(i * mScaleMargin + mInitDistance, mRectHeight,
                  i * mScaleMargin + mInitDistance, mRectHeight - mLineHeight - mTextLineMargin + 20,
                  paint);
              //整值文字
              paint.setColor(Color.GRAY);
              canvas.drawText(mTextList.get(k), i * mScaleMargin + mInitDistance,
                  mRectHeight - mLineHeight - mTextLineMargin, paint);
              k++;
            } else {
              paint.setColor(Color.GRAY);
              canvas.drawLine(i * mScaleMargin + mInitDistance, mRectHeight,
                  i * mScaleMargin + mInitDistance, mRectHeight - mLineHeight / 2 - mTextLineMargin,
                  paint);
            }
          } else {//画滚动到末尾余数 30200
            if (mPage == (mTotalTextList.size() - 20) / 10 - 1 || mTotalTextList.size() < 40) {
              if ((mLastInstance / mScaleMargin) > 0
                  && (mLastInstance / mScaleMargin) > i - (mTextList.size() * 10 - 9)) {
                paint.setColor(Color.GRAY);
                canvas.drawLine(i * mScaleMargin + mInitDistance, mRectHeight,
                    i * mScaleMargin + mInitDistance, mRectHeight - mLineHeight / 2 - mTextLineMargin,
                    paint);
              }
            }
          }
        }
      }
    
      private void onDrawLine(Canvas canvas, Paint paint) {
        paint.setStrokeWidth(2);
        canvas.drawLine(mInitDistance, mRectHeight, mScaleWidth + mInitDistance, mRectHeight, paint);
      }
    
      /**
       * 使用Scroller的时候需要重写该方法
       */
      @Override
      public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
          scrollTo(mScroller.getCurrX(), 0);
          postInvalidate();
        }
      }
    
      private void smoothScrollBy(int dx, int dy) {
        if (mScroller.getFinalX() >= mScaleMargin * 10 * 10 * 2 - 10 * mScaleMargin
            && dx > 0) {//向左滚动,判断是否滚动到第三部分起始位置
          if (!notifyDataChanged(0)) {
            mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
          }
        } else if (mScroller.getFinalX() <= mScaleMargin * 10 * 10 - 10 * mScaleMargin
            && dx < 0) {///向右滚动,判断是否滚动到第二部分开始位置
          if (!notifyDataChanged(1)) {
            mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
          }
        } else {//否则根据手指滚动
          mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
        }
        postInvalidate();
      }
    
      public interface OnScrollListener {
        void onScaleScrollChanged(long scale);
      }
    
      /**
       * 设置当前位置
       *
       * @param scale 刻度
       */
      public void setNowScale(float scale) {
        float i = scale * mScaleMargin;
        int maxPage = (mTotalTextList.size() - 20) / 10 - 1;
        mPage = 0;
        if (mTotalTextList.size() < 40) {//如果总数小于40 不需要处理多页
          mPage = 0;
          smoothScrollBy((int) (i - mScroller.getFinalX()), 0);
          return;
        } else if (i < mScaleMargin * 10 * 10 * 2 - mScaleMargin * 10) {//判断刻度是否在第一页
          mPage = 0;
        } else if (scale >= 10 * 10 + maxPage * 10 * 10) {//判断刻度是否在最后一页
          mPage = maxPage;
        } else {
          mPage = (int) (scale / 100 - 1);
        }
        if (scale * mUnit >= mFinalMoney) {//如果要设置的刻度大于集合里的最大值 则只滚动到末尾
          mPage = maxPage;
          mTextList.clear();
          for (int j = mPage * 10; j < mTotalTextList.size(); j++) {
            mTextList.add(mTotalTextList.get(j));
          }
          mScaleWidth = (mTextList.size() * 10 - 10) * mScaleMargin + mLastInstance;
          smoothScrollBy((int) (mScaleWidth - mScroller.getFinalX()), 0);
        } else {
          if (mPage == (mTotalTextList.size() - 20) / 10 - 1) {//判断是否是最后一页
            if (mTotalTextList.size() >= mPage * 10 + 30) {
              mTextList.clear();
              for (int j = mPage * 10; j < mTotalTextList.size(); j++) {//更新刻度
                mTextList.add(mTotalTextList.get(j));
              }
              mScaleWidth = (mTextList.size() * 10 - 10) * mScaleMargin + mLastInstance;//加上余数的宽度
              smoothScrollBy((int) (i - ((mPage + 1) * 10 * 10 * mScaleMargin) + 10 * 10 * mScaleMargin
                  - mScroller.getFinalX()), 0);
            }
          } else {
            if (mTotalTextList.size() >= mPage * 10 + 30) {
              mTextList.clear();
              for (int j = mPage * 10; j < mPage * 10 + 30; j++) {//更新刻度
                mTextList.add(mTotalTextList.get(j));
              }
              mScaleWidth = (mTextList.size() * 10 - 10) * mScaleMargin;
              smoothScrollBy((int) (i - ((mPage + 1) * 10 * 10 * mScaleMargin) + 10 * 10 * mScaleMargin
                  - mScroller.getFinalX()), 0);
            }
          }
        }
      }
    
      /**
       * 初始化数据
       *
       * @param startMoney 开始金额
       * @param finalMoney 最终金额
       * @param unit 每个刻度单位
       * @param listener 滚动监听
       */
      public void bindMoneyData(int startMoney, int finalMoney, int unit, OnScrollListener listener) {
        if (mTotalTextList != null && mTotalTextList.size() > 0) {
          refresh(startMoney, finalMoney);
        } else {
          mPage = 0;
          mUnit = unit;
          mStartMoney = startMoney / unit;
          mTextList = new ArrayList<>();
          mTotalTextList = new ArrayList<>();
          mFinalMoney = finalMoney;
          for (int i = 0; i < (finalMoney - startMoney) / (unit * 10) + 1; i++) {
            if (i < 30 || (finalMoney - startMoney) / (unit * 10) + 1 < 40) {//当前显示刻度数
              mTextList.add(String.valueOf(i * unit * 10 + startMoney));
            }
            mTotalTextList.add(String.valueOf(i * unit * 10 + startMoney));//总共刻度数
          }
          mLastInstance = ((finalMoney - startMoney) % (unit * 10)) / 100 * mScaleMargin;//余数刻度
          if (mTotalTextList.size() < 40) {
            mScaleWidth = (mTextList.size() * 10 - 10) * mScaleMargin + mLastInstance;
          } else {
            mScaleWidth = (mTextList.size() * 10 - 10) * mScaleMargin;
          }
          mListener = listener;
          postInvalidate();
        }
      }
    
      /**
       * 判断更新当前页
       *
       * @param type 0左滑  1右滑
       * @return 是否处理了滚动到对应页
       */
      private boolean notifyDataChanged(int type) {
        if (mTotalTextList.size() < 40) {
          return false;
        }
        if (type == 0) {//向前移动
          if ((mTotalTextList.size() - 20) / 10 - 1 == 0) {
            return false;
          }
          if (mPage < (mTotalTextList.size() - 20) / 10 - 1) {
            mPage++;
            if (mPage == (mTotalTextList.size() - 20) / 10 - 1) {
              if (mTotalTextList.size() >= mPage * 10 + 30) {
                mTextList.clear();
                for (int i = mPage * 10; i < mTotalTextList.size(); i++) {
                  mTextList.add(mTotalTextList.get(i));
                }
                mScaleWidth = (mTextList.size() * 10 - 10) * mScaleMargin + mLastInstance;
                mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(),
                    -mScroller.getFinalX() + mScaleMargin * 10 * 10 - 10 * mScaleMargin,
                    mScroller.getFinalY());
                return true;
              }
            } else {
              if (mTotalTextList.size() >= mPage * 10 + 30) {
                mTextList.clear();
                for (int i = mPage * 10; i < mPage * 10 + 30; i++) {
                  mTextList.add(mTotalTextList.get(i));
                }
                mScaleWidth = (mTextList.size() * 10 - 10) * mScaleMargin;
                mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(),
                    -mScroller.getFinalX() + mScaleMargin * 10 * 10 - 10 * mScaleMargin,
                    mScroller.getFinalY());
                return true;
              }
            }
          }
        } else {//向后移动
          if (mPage > 0) {
            mPage--;
            if (mTotalTextList.size() > mPage * 10 + 30) {
              mTextList.clear();
              for (int i = mPage * 10; i < mPage * 10 + 30; i++) {
                mTextList.add(mTotalTextList.get(i));
              }
              mScaleWidth = (mTextList.size() * 10 - 10) * mScaleMargin;
              mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(),
                  mScaleMargin * 10 * 10, mScroller.getFinalY());
              return true;
            }
          }
        }
        return false;
      }
    
      /**
       * 刷新操作
       */
      public void refresh(final int startMoney, final int finalMoney) {
        new Thread(new Runnable() {//防止数据量过多导致阻塞主线程
          @Override
          public void run() {
            mPage = 0;
            mStartMoney = startMoney / mUnit;
            mFinalMoney = finalMoney;
            mTextList.clear();
            mTotalTextList.clear();
            for (int i = 0; i < (finalMoney - startMoney) / (mUnit * 10) + 1; i++) {
              if (i < 30 || (finalMoney - startMoney) / (mUnit * 10) + 1 < 40) {//当前显示刻度数
                mTextList.add(String.valueOf(i * mUnit * 10 + startMoney));
              }
              mTotalTextList.add(String.valueOf(i * mUnit * 10 + startMoney));//总共刻度数
            }
            mLastInstance = ((finalMoney - startMoney) % (mUnit * 10)) / 100 * mScaleMargin;//余数刻度
            if (mTotalTextList.size() < 40) {
              mScaleWidth = (mTextList.size() * 10 - 10) * mScaleMargin + mLastInstance;
            } else {
              mScaleWidth = (mTextList.size() * 10 - 10) * mScaleMargin;
            }
            new Handler(Looper.getMainLooper()).post(new Runnable() {
              @Override
              public void run() {
                postInvalidate();
              }
            });
          }
        });
      }
    
      /**
       * 设置每个刻度间距
       *
       * @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;
      }
    
      /**
       * 设置文字和刻度线的间距(滚动有问题)
       */
      public ScrollDividingRuleView setInitPosition(int position) {
        mInitPosition=position;
        return this;
      }
    }
    

    调用代码:

    final ScrollDividingRuleView scrollDividingRuleView = findViewById(R.id.scroll_dividing_rule_view);
        editText = findViewById(R.id.edit_text);
        scrollDividingRuleView.bindMoneyData(0, 100000, 100,
            new ScrollDividingRuleView.OnScrollListener() {
              @Override public void onScaleScrollChanged(long scale) {
                isDividingScroll=true;//防止滚动的时候和edittext绑定数据出问题
                editText.setText(String.valueOf(scale*100));
                editText.setSelection(String.valueOf(scale*100).length());
              }
            });
    
        scrollDividingRuleView.setScaleMargin(30)
            .setTextLineMargin(30);
    
        editText.addTextChangedListener(new TextWatcher() {
          @Override
          public void beforeTextChanged(CharSequence s, int start, int count, int after) {
          }
    
          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
          }
    
          @Override
          public void afterTextChanged(Editable s) {
            String money = s.toString().trim();
            if (money.length() > 0 && !TextUtils.isEmpty(money)) {
              float i = Float.parseFloat(money);
                if (!isDividingScroll) {
                  scrollDividingRuleView.setNowScale(i >= 100? i / 100 : 0);
                } else {
                  isDividingScroll = false;
                }
              }
          }
        });

    布局代码:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.zqb.scrolldividingrule.MainActivity">
        <com.zqb.scrolldividingrule.ScrollDividingRuleView
            android:layout_marginTop="50px"
            android:id="@+id/scroll_dividing_rule_view"
            android:layout_width="match_parent"
            android:layout_marginLeft="30px"
            android:layout_marginRight="30px"
            android:layout_height="wrap_content"
            app:line_height="80px"
            app:dividing_text_size="30px"/>
        <View
            android:layout_marginTop="90px"
            android:background="@color/colorAccent"
            android:layout_centerHorizontal="true"
            android:layout_width="1px"
            android:layout_height="120px"/>
        <EditText
            android:inputType="number"
            android:id="@+id/edit_text"
            android:layout_marginLeft="10dp"
            android:textSize="25sp"
            android:layout_marginTop="300px"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </RelativeLayout>
    

    下载地址

    如果显示30个有卡顿的话,可以自己调整。
    注:paint.setStyle(Paint.Style.FILL); 防止画出来的文字带空心
    paint.measureText(“123”); 获取文字显示的宽度

    展开全文
  • Android自定义View实现方位刻度尺(类似于吃鸡手游) 先上效果图 gif可能看不清,我下面放几张图片 原理分析 首先,我们应该把看得到的内容从上至下分成三部分:最上面的文字、中间的竖线和最下面...
  • 间隔中数目最高的“-”,要比刻度上的“-”少输出个。 思考: 间隔部分是个递归,使用递归输出即可。刻度线上数字按需要的最大刻度来,刻度线做个循环,每循环次调用递归打印间隔。 python版本: ...
  • 小甲鱼零基础入门学习python笔记

    万次阅读 多人点赞 2019-08-14 11:06:30
    004 改进我们的小游戏 •第个改进要求:猜错的时候程序提示用户当前的输入答案大了还是小了 与操作and •第二个改进要求:程序应该提供多次机会给用户猜测,专业点来讲就是程序需要重复运行某些代码。...
  • 完美的垂直刻度尺

    千次阅读 2016-11-17 13:04:01
    这个垂直刻度尺可以滑动,可以配合硬件根据结果展示在刻度尺上,话不多少,先看效果吧~ 是不是还可以? 上代码: public class HeightTestRule extends View { private static final int Max = 225;//...
  • 波浪是的种股价空间分析工具,主要判断后续股价运动的预期。 经常遇见的回吐比率为0.382、0.5及0.618。 下图是东方财富的波浪 HQChart波浪 默认刻度比例 默认的刻度比例是 0%, 38.2% , 61.8%, 100% , 161...
  • 把数学上完美的尺子(哥隆

    千次阅读 2020-03-09 16:19:42
    作者:蒋迅 本文发表在《中国工业与应用数学学会通讯》2019年第2期。...具体地说,我们要讲的是种叫哥隆的数学概念,同时看看它有什么实际的应用。 1. 什么是哥隆 先说哥隆。哥隆真的是...
  • 但这是把单独的刻度尺,需要借助圆规的帮助才能得出计算结果。 康熙皇帝对西方数学很感兴趣。他是中国历史上最早使用计算尺的人,康熙年间御制的象牙计算尺就是证据。 奥特雷德计算尺原理图(图片见水印) 大约到...
  • 刚好看到刻度尺文章,实现手机屏幕上画刻度尺。 然后就有个疑问:这个现实中的1mm(1毫米)长度与手机像素之间的换算怎么来的呢? 看了下demo代码,发现这样写的: CGFloat sc_w = [[UIScreen ...
  • 屏幕尺子,HTML,尺子,测量工具。

    热门讨论 2011-04-11 15:05:07
    款小巧的方便的屏幕尺子。解压直接安装即可。
  • 坐标轴(Axis) 坐标轴(Axis)在很多图表中都可见到,...D3提供了坐标轴的制作方法,需要之前所给大家讲的比例一起使用。开发者仅仅需要几行代码,就能够生成各式各样的坐标轴。 与坐标轴相关的方法有: ...
  • 昨天帮老婆弄个医用的刻度尺,很是花了一番功夫,最终在做了近俩小时没弄出来准备睡觉的瞬间想到了怎么做。考虑到在网上搜索到的教程中只有篇是使用Firework做的,但是实际应用中,在排版打印方面的便利性上,...
  • 项目中,需要做个可滑动选择的控件,如下图: 可左右滑动,滑动结束后,刻度与红色刻度线对齐。红色刻度线上的刻度为选中的刻度。 这种可滑动选择的交互比较常见,比如app中的日期、地区选择等等,常常会有这样的...
  • 本来按照网上找到的方法进行绘制的时候,发现要先画个外圆,然后再画个内圆。。。(此处省略),尝试过次后发现直接画线即可。 分享一下自己的代码 initCanvas(canvas){ //当前视口宽度 let ...
  • 那里实现的部分启发了这里所做的部分工作是能够将用户可设置的填充添加到图形的边缘(为轴刻度刻度标签和标签添加空间)。 这消除了在保存图形时使用bbox_inches='tight'的需要,并使您能够确保您的图
  • 最开始是想做横向的刻度尺效果,后来实现的时候发现竖向以及圆形刻度尺(表盘效果)实现代码有很大的相同之处,索性全部做出效果. 实现横向,竖向,圆形刻度效果 游标可自由设置图片,大小以及与刻度的距离 可自定义...
  • 如果只想要条轴刻度,我们要把 axisLabel: { show: true }这条属性添加到indicator 数据中,想要在那条显示,就在那条添加。 我们使用雷达图有时会需要展示它的轴刻度, 作者:你故事温柔 链接:...
  • 尺子从,分为四的故事(BooheeRuler的创造和重构思路) 本文出处: 炎之铠csdn博客:http://blog.csdn.net/totond 炎之铠邮箱:yanzhikai_yjk@qq.com 本项目Github地址:...
  •  在遥远的周朝,人们受生产力水平所限,无法管理庞大的土地和众多的人民,因此采用了封邦建国的封建制度,把土地层划分下去,以达到分而治之的目的,这也许是最古老的分治法了: 分治的步骤  正像分封...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,860
精华内容 1,544
关键字:

一比一刻度尺