Android自定义一个属于自己的刻度尺

Posted 大白龙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android自定义一个属于自己的刻度尺相关的知识,希望对你有一定的参考价值。

概述

      本文只要说的是自定义一个刻度尺,正好学习下android自定义控件,之前写过一篇《Android自定义一个属于自己的时间钟表》,大家如果感兴趣可以去看下,好了不扯淡了,直接上效果:


看到这个效果以后估计好多新手会觉得不知道如何入手,但是要是大神看到了就会想用什么方式实现才是最好的。这就是差距啊,没办法像我这个菜鸟只好参考下其他实现方法,写了这个demo,让我们一起来看看实现思路。 我们来分步骤一步一步来实现: 1、绘制刻度尺及刻度值(高,中,低刻度)。 2、绘制底部线。 3、绘制中间箭头。 4、监听手势处理(处理范围越界,及选中)。

第一步:1、自定义View的属性,首先在res/values/  下建立一个attrs.xml , 在里面定义我们的属性和声明我们的整个样式。

 <declare-styleable name="RulerView">
        <!--最大刻度的颜色-->
        <attr name="mMaxScaleColor" format="color"/>
        <!--中间刻度的颜色-->
        <attr name="mMidScaleColor" format="color"/>
        <!--最小刻度的颜色-->
        <attr name="mMinScaleColor" format="color"/>
        <!--底线的颜色-->
        <attr name="mBottomLineColor" format="color"/>
        <!--最大刻度的宽度-->
        <attr name="mMaxScaleWidth" format="dimension"/>
        <!--中间刻度的宽度-->
        <attr name="mMidScaleWidth" format="dimension"/>
        <!--最小刻度的宽度-->
        <attr name="mMinScaleWidth" format="dimension"/>
        <!--底线的宽度-->
        <attr name="mBottomLineWidth" format="dimension"/>
        <!--最大刻度的高度占控件的高度比例-->
        <attr name="mMaxScaleHeightRatio" format="float"/>
        <!--中间刻度的高度占控件的高度比例-->
        <attr name="mMidScaleHeightRatio" format="float"/>
        <!--最小刻度的高度占控件的高度比例-->
        <attr name="mMinScaleHeightRatio" format="float"/>
        <!--是否显示刻度值-->
        <attr name="isShowScaleValue" format="boolean"/>
        <!--是否刻度渐变 包括刻度值和刻度线及下面的线-->
        <attr name="isScaleGradient" format="boolean"/>
        <!--刻度值颜色-->
        <attr name="mScaleValueColor" format="color"/>
        <!--刻度值文字大小-->
        <attr name="mScaleValueSize" format="dimension"/>
        <!--刻度值间隔-->
        <attr name="mScaleSpace" format="dimension"/>
        <!-- 当前值-->
        <attr name="mCurrentValue" format="integer"/>
        <!--最大值-->
        <attr name="mMaxValue" format="integer"/>
        <!--最小值-->
        <attr name="mMinValue" format="integer"/>
        <!--刻度基数-->
        <attr name="mScaleBase" format="integer"/>
        <!--中间图标-->
        <attr name="mMiddleImg" format="reference"/>
    </declare-styleable>

通过这个attrs大家也看到,我们设置了还是蛮细的,所有的刻度及刻度值颜色大小都设定了,让控件设置更加的灵活。
2、下面创建一个class类为rulerview.class ,并设置main.xml中:
 <com.dalong.rulerview.RulerView
        android:id="@+id/ruler2"
        android:layout_centerInParent="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:mMaxValue="5000"
        app:mMinValue="1000"
        app:mScaleBase="100"
        app:mScaleSpace="10dp"
        app:mMaxScaleColor="@color/colorAccent"
        app:mMidScaleColor="@color/colorPrimary"
        app:mMinScaleColor="@color/colorPrimary"
        app:mBottomLineColor="@color/colorAccent"
        app:mMaxScaleHeightRatio="0.5"
        app:mMidScaleHeightRatio="0.3"
        app:mMinScaleHeightRatio="0.2"
        app:mMaxScaleWidth="2.5dp"
        app:mMidScaleWidth="2dp"
        app:mMinScaleWidth="2dp"
        app:mBottomLineWidth="2.5dp"
        app:mCurrentValue="1000"
        app:mScaleValueColor="@color/colorAccent"
        app:mScaleValueSize="12sp"
        app:mMiddleImg="@mipmap/icon_arrow"
        app:isScaleGradient="false"
        />

3、在自定义View的构造方法中,获得我们的自定义的样式

 // 默认刻度模式
    public static final int MOD_TYPE_SCALE = 5;
    //刻度基数  每个刻度代表多少 默认为1
    public int mScaleBase=1;
    //最大刻度的颜色
    public int mMaxScaleColor;
    //中间刻度的颜色
    public int mMidScaleColor;
    //最小刻度的颜色
    public int mMinScaleColor;
    //底部线的颜色
    public int mBottomLineColor;
    //最大刻度的宽度
    public float mMaxScaleWidth;
    //中间刻度的宽度
    public float mMidScaleWidth;
    //最小刻度的宽度
    public float mMinScaleWidth;
    //底线的宽度
    public float mBottomLineWidth;
    //最大刻度的高度占控件的高度比例
    public float mMaxScaleHeightRatio;
    //中间刻度的高度占控件的高度比例
    public float mMidScaleHeightRatio;
    //最小刻度的高度占控件的高度比例
    public float mMinScaleHeightRatio;
    //是否显示刻度值
    public boolean isShowScaleValue;
    //是否刻度渐变
    public boolean isScaleGradient;
    //刻度值颜色
    public int  mScaleValueColor;
    //刻度值文字大小
    public float  mScaleValueSize;
    //当前值
    public int mCurrentValue;
    //最大值
    public int mMaxValue;
    //最小值
    public int mMinValue;
    //中间图片
    private Bitmap mMiddleImg;
    //刻度线画笔
    private  Paint mScalePaint;
    // 刻度值画笔
    private  TextPaint mScaleValuePaint;
    //中间图片画笔
    private  Paint mMiddleImgPaint;
    private  float mTpDesiredWidth;
    //最大刻度高度
    private int mMaxScaleHeight;
    //中间刻度高度
    private int mMidScaleHeight;
    //最小刻度高度
    private int mMinScaleHeight;
    // 滚动偏移量
    private int scrollingOffset;
    //间隔S
    private int mScaleSpace=20;
    // 滚动器
    private RulerViewScroller scroller;
    // 是否执行滚动
    private boolean isScrollingPerformed;

    public RulerView(Context context) 
        this(context,null);
    

    public RulerView(Context context, AttributeSet attrs) 
        this(context, attrs,0);
    

    public RulerView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);

        TypedArray typedArray=context.obtainStyledAttributes(attrs,R.styleable.RulerView);

        mMaxScaleColor=typedArray.getColor(R.styleable.RulerView_mMaxScaleColor, Color.BLACK);
        mMidScaleColor=typedArray.getColor(R.styleable.RulerView_mMidScaleColor, Color.BLACK);
        mMinScaleColor=typedArray.getColor(R.styleable.RulerView_mMinScaleColor, Color.BLACK);
        mMaxScaleColor=typedArray.getColor(R.styleable.RulerView_mMaxScaleColor, Color.BLACK);
        mScaleValueColor=typedArray.getColor(R.styleable.RulerView_mScaleValueColor, Color.BLACK);
        mBottomLineColor=typedArray.getColor(R.styleable.RulerView_mBottomLineColor, Color.BLACK);

        mMaxScaleWidth = typedArray.getDimensionPixelSize(R.styleable.RulerView_mMaxScaleWidth, 15);
        mMidScaleWidth = typedArray.getDimensionPixelSize(R.styleable.RulerView_mMidScaleWidth, 12);
        mMinScaleWidth = typedArray.getDimensionPixelSize(R.styleable.RulerView_mMinScaleWidth, 10);
        mBottomLineWidth = typedArray.getDimensionPixelSize(R.styleable.RulerView_mBottomLineWidth, 15);
        mScaleValueSize = typedArray.getDimensionPixelSize(R.styleable.RulerView_mScaleValueSize, 12);
        mScaleSpace = typedArray.getDimensionPixelSize(R.styleable.RulerView_mScaleSpace, 20);

        mMaxScaleHeightRatio = typedArray.getFloat(R.styleable.RulerView_mMaxScaleHeightRatio, 0.3f);
        mMidScaleHeightRatio = typedArray.getFloat(R.styleable.RulerView_mMidScaleHeightRatio, 0.2f);
        mMinScaleHeightRatio = typedArray.getFloat(R.styleable.RulerView_mMinScaleHeightRatio, 0.1f);

        isShowScaleValue = typedArray.getBoolean(R.styleable.RulerView_isShowScaleValue, true);
        isScaleGradient = typedArray.getBoolean(R.styleable.RulerView_isScaleGradient, true);

        mMaxValue = typedArray.getInteger(R.styleable.RulerView_mMaxValue, 100);
        mMinValue = typedArray.getInteger(R.styleable.RulerView_mMinValue, 0);
        mScaleBase = typedArray.getInteger(R.styleable.RulerView_mScaleBase, 1);
        mCurrentValue = typedArray.getInteger(R.styleable.RulerView_mCurrentValue, 0);
        setCurrentValue(mCurrentValue);

        mMiddleImg = BitmapFactory.decodeResource(getResources(),
                typedArray.getResourceId(R.styleable.RulerView_mMiddleImg,R.drawable.ruler_mid_arraw));
        typedArray.recycle();

        mScalePaint=new Paint(Paint.ANTI_ALIAS_FLAG);
        mScalePaint.setStyle(Paint.Style.STROKE);
        mScalePaint.setAntiAlias(true);

        mScaleValuePaint=new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mScaleValuePaint.setColor(mScaleValueColor);
        mScaleValuePaint.setTextSize(mScaleValueSize);
        mScaleValuePaint.setTextAlign(Paint.Align.CENTER);
        mTpDesiredWidth = Layout.getDesiredWidth("0", mScaleValuePaint);

        mMiddleImgPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
        mMiddleImgPaint.setStyle(Paint.Style.STROKE);
        mMiddleImgPaint.setAntiAlias(true);

        scroller=new RulerViewScroller(context,scrollingListener);
    

4、我们重写onMesure:
 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        setMeasuredDimension(measureWidthSize(widthMeasureSpec),measureHeightSize(heightMeasureSpec));
    

    private int measureHeightSize(int heightMeasureSpec) 
        int result;
        int mode=MeasureSpec.getMode(heightMeasureSpec);
        int size=MeasureSpec.getSize(heightMeasureSpec);
        if(mode==MeasureSpec.EXACTLY)
            result=size;
        else
            result=(int) (mMiddleImg.getHeight() + getPaddingTop() + getPaddingBottom() + 2 * mScaleValuePaint.getTextSize());
            if(mode==MeasureSpec.AT_MOST)
                result=Math.min(result,size);
            
        
        return  result;
    

    private int measureWidthSize(int widthMeasureSpec) 
        int result;
        int mode=MeasureSpec.getMode(widthMeasureSpec);
        int size=MeasureSpec.getSize(widthMeasureSpec);
        if(mode==MeasureSpec.EXACTLY)
            result=size;
        else
            result=400;
            if(mode==MeasureSpec.AT_MOST)
                result=Math.min(result,size);
            
        
        return  result;
    

5、设置下三种刻度尺的高度
  @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) 
        super.onSizeChanged(w, h, oldw, oldh);
        if (w == 0 || h == 0)
            return;
        /**
         * 在这里根据控件高度设置三中刻度线的高度
         */
        int mHeight = h - getPaddingTop() - getPaddingBottom();
        mMaxScaleHeight = (int) (mHeight*mMaxScaleHeightRatio);
        mMidScaleHeight = (int) (mHeight*mMidScaleHeightRatio);
        mMinScaleHeight = (int) (mHeight*mMinScaleHeightRatio);
    

6、绘制刻度尺
 /**
     * 绘制刻度线
     * @param canvas
     * @param mDrawWidth
     * @param mDrawHeight
     */
    private void drawScaleLine(Canvas canvas, int mDrawWidth, int mDrawHeight) 
        int scaleNum= (int) (Math.ceil(mDrawWidth/2f/mScaleSpace))+2;
        int distanceX = scrollingOffset;
        int currValue = mCurrentValue;
        drawScaleLine(canvas,scaleNum,distanceX,currValue,mDrawWidth,mDrawHeight);
    

    /**
     * 绘制刻度线
     * @param canvas
     * @param scaleNum
     * @param distanceX
     * @param currValue
     * @param mDrawWidth
     * @param mDrawHeight
     */
    private void drawScaleLine(Canvas canvas, int scaleNum, int distanceX, int currValue, int mDrawWidth, int mDrawHeight) 
        int dy = (int) (mDrawHeight - mTpDesiredWidth - mScaleValuePaint.getTextSize()) - getPaddingBottom();
        int value;
        float xPosition;
        for (int i=0;i<scaleNum;i++)
            // 右面
            xPosition=mDrawWidth/2f+i*mScaleSpace+distanceX;
            value=currValue+i;
            if(xPosition<=mDrawWidth && value>=(mMinValue/mScaleBase)&&value<=(mMaxValue/mScaleBase))
                drawScaleLine(canvas, value,  xPosition, dy, scaleNum, i, mDrawHeight);
            
            //绘制右面线
            if(value<(mMaxValue/mScaleBase)&&value>=(mMinValue/mScaleBase))
                drawBottomLine(canvas,getAlpha(scaleNum, i),xPosition-mMaxScaleWidth/2, dy, xPosition+mScaleSpace+mMaxScaleWidth/2, dy);

            //左面
            xPosition=mDrawWidth/2f-i*mScaleSpace+distanceX;
            value=currValue-i;
            if(xPosition>getPaddingLeft() && value>=(mMinValue/mScaleBase)&&value<=(mMaxValue/mScaleBase))
                drawScaleLine( canvas, value,  xPosition, dy, scaleNum, i, mDrawHeight);
            
            //绘制左面线
            if(value>=(mMinValue/mScaleBase) && value<(mMaxValue/mScaleBase))
                drawBottomLine(canvas,getAlpha(scaleNum, i),xPosition-mMaxScaleWidth/2, dy, xPosition+mScaleSpace+mMaxScaleWidth/2, dy);
        
    

    /**
     * 绘制底部线
     * @param canvas
     * @param alpha
     * @param sx
     * @param sy
     * @param ex
     * @param ey
     */
    private void drawBottomLine(Canvas canvas,int alpha,float sx,float sy,float ex,float ey)
        mScalePaint.setColor(mBottomLineColor);
        mScalePaint.setStrokeWidth(mBottomLineWidth);
        mScalePaint.setAlpha(alpha);
        canvas.drawLine(sx, sy, ex, ey, mScalePaint);
    
    /**
     * 绘制刻度尺  左  右
     * @param canvas
     * @param value
     * @param xPosition
     * @param dy
     * @param scaleNum
     * @param i
     * @param mDrawHeight
     */
    public void drawScaleLine(Canvas canvas,int value, float xPosition,int dy,int scaleNum,int i,int mDrawHeight)
        if (value % MOD_TYPE_SCALE == 0) 
            if(value % (MOD_TYPE_SCALE*2)==0)//大刻度
                drawScaleLine(canvas,mMaxScaleWidth,mMaxScaleColor,getAlpha(scaleNum, i),
                        xPosition,dy,xPosition,dy - mMaxScaleHeight);
                if (isShowScaleValue) 
                    mScaleValuePaint.setAlpha(getAlpha(scaleNum, i));
                    canvas.drawText(String.valueOf(value*mScaleBase), xPosition, mDrawHeight - mTpDesiredWidth, mScaleValuePaint);
                
            else//中刻度
                drawScaleLine(canvas,mMidScaleWidth,mMidScaleColor,getAlpha(scaleNum, i),
                        xPosition,dy,xPosition,dy-mMidScaleHeight);
            
        else// 小刻度
            drawScaleLine(canvas,mMinScaleWidth,mMinScaleColor,getAlpha(scaleNum, i),
                    xPosition,dy,xPosition,dy-mMinScaleHeight);
        

    
    /**
     * 绘制刻度尺刻度
     * @param canvas
     * @param strokeWidth
     * @param scaleColor
     * @param alpha
     * @param sx
     * @param sy
     * @param ex
     * @param ey
     */
    private void drawScaleLine(Canvas canvas,float strokeWidth,int scaleColor,int alpha,float sx,float sy,float ex,float ey)
        mScalePaint.setStrokeWidth(strokeWidth);
        mScalePaint.setColor(scaleColor);
        mScalePaint.setAlpha(alpha);
        canvas.drawLine(sx, sy, ex, ey, mScalePaint);
    

其实上面就是绘制刻度尺的核心办法,主要实现思想就是以中心为开始点向左右绘制刻度线。其中也有一些细节需要大家慢慢去思索的,比如这里包含高,中 ,低的三种刻度线,他们在绘制的时候高度,颜色,宽度都是不一样的设置。怎么区分,我这里也写的比较的清楚。这里我就不在提了。 7、绘制中间箭头图片
    /**
     * 绘制中间图片
     * @param canvas
     * @param mDrawWidth
     * @param mDrawHeight
     */
    private void drawMiddleImg(Canvas canvas, int mDrawWidth, int mDrawHeight) 
        int left = (mDrawWidth - mMiddleImg.getWidth()) / 2;
        int top = (int) (mScaleValuePaint.getTextSize() / 2);
        canvas.drawBitmap(mMiddleImg, left, top, mMiddleImgPaint);
    

以上写完就已经可以实现刻度尺了,但是刻度尺是无法拖动的,效果如下:

下面主要就是需要如何实现拖动的效果,其实这个才是最难的。
这里单独创建一个滑动控制类:
public class RulerViewScroller 

    //滚动的时间
    public static final int SCROLLING_DURATION = 400;

    //用于滚动的最小增量
    public static final int MIN_DELTA_FOR_SCROLLING = 1;

    //Listener
    private ScrollingListener listener;

    //上下文
    private Context context;

    // Scrolling
    private GestureDetector gestureDetector;
    private Scroller scroller;
    private int lastScrollX;
    private float lastTouchedX;
    private boolean isScrollingPerformed;

    private final int MESSAGE_SCROLL = 0;
    private final int MESSAGE_JUSTIFY = 1;


    public RulerViewScroller(Context context, ScrollingListener listener) 
        this.listener = listener;
        this.context = context;
        gestureDetector = new GestureDetector(context, gestureListener);
        gestureDetector.setIsLongpressEnabled(false);
        scroller = new Scroller(context);
        scroller.setFriction(0.05f);

    


    /**
     * 手势监听
      */
    private GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() 
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) 
            return true;
        

        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) 
            lastScrollX = 0;
            scroller.fling(0, lastScrollX, (int) -velocityX, 0, -0x7FFFFFFF, 0x7FFFFFFF, 0, 0);
            setNextMessage(MESSAGE_SCROLL);
            return true;
        
    ;


    /**
     * 手势处理
     * @param event
     * @return
     */
    public boolean onTouchEvent(MotionEvent event) 
        switch (event.getAction()) 
            case MotionEvent.ACTION_DOWN:
                lastTouchedX = event.getX();
                scroller.forceFinished(true);
                clearMessages();
                break;

            case MotionEvent.ACTION_MOVE:
                int distanceX = (int) (event.getX() - lastTouchedX);
                if (distanceX != 0) 
                    startScrolling();
                    listener.onScroll(distanceX);
                    lastTouchedX = event.getX();
                
                break;
        
        //当手指离开控件时
        if (!gestureDetector.onTouchEvent(event) && event.getAction() == MotionEvent.ACTION_UP) 
            justify();
        

        return true;
    
    /**
     * 发送下一步消息,清楚之前的消息
     * @param message
     */
    private void setNextMessage(int message) 
        clearMessages();
        animationHandler.sendEmptyMessage(message);
    

    /**
     * 清楚所有的what的消息列表
     */
    private void clearMessages() 
        animationHandler.removeMessages(MESSAGE_SCROLL);
        animationHandler.removeMessages(MESSAGE_JUSTIFY);
    


    /**
     * 滚动
     * @param distance 距离
     * @param time     时间
     */
    public void scroll(int distance, int time) 
        scroller.forceFinished(true);
        lastScrollX = 0;
        scroller.startScroll(0, 0, distance, 0, time != 0 ? time : SCROLLING_DURATION);
        setNextMessage(MESSAGE_SCROLL);
        startScrolling();
    

    /**
     * 动画处理handler
     */
    private Handler animationHandler = new Handler(new Handler.Callback() 
        @Override
        public boolean handleMessage(Message msg) 
            scroller.computeScrollOffset();
            int currX = scroller.getCurrX();
            int delta = lastScrollX - currX;
            lastScrollX = currX;
            if (delta != 0) 
                listener.onScroll(delta);
            
            // 滚动是不是完成时,涉及到最终Y,所以手动完成
            if (Math.abs(currX - scroller.getFinalX()) < MIN_DELTA_FOR_SCROLLING) 
                lastScrollX = scroller.getFinalX();
                scroller.forceFinished(true);
            
            if (!scroller.isFinished()) 
                animationHandler.sendEmptyMessage(msg.what);
             else if (msg.what == MESSAGE_SCROLL) 
                justify();
             else 
                finishScrolling();
            
            return true;
        
    );


    /**
     * 滚动停止时待校验
     */
    private void justify() 
        listener.onJustify();
        setNextMessage(MESSAGE_JUSTIFY);
    

    /**
     * 开始滚动
     */
    private void startScrolling() 
        if (!isScrollingPerformed) 
            isScrollingPerformed = true;
            listener.onStarted();
        
    

    /**
     * 滚动结束
     */
    void finishScrolling() 
        if (isScrollingPerformed) 
            listener.onFinished();
            isScrollingPerformed = false;
        
    

    /**
     *  滚动监听器接口
     */
    public interface ScrollingListener 
        /**
         * 正在滚动中回调
         * @param distance 滚动的距离
         */
        void onScroll(int distance);

        /**
         * 启动滚动时调用的回调函数
         */
        void onStarted();

        /**
         * 校验完成后 执行完毕后回调
         */
        void onFinished();

        /**
         * 滚动停止时待校验
         */
        void onJustify();
    


然后在activity中:
 /**
     * 滚动回调接口
     */
    RulerViewScroller.ScrollingListener scrollingListener = new RulerViewScroller.ScrollingListener() 

        /**
         * 滚动开始
         */
        @Override
        public void onStarted() 
            isScrollingPerformed = true;
            //滚动开始
            if (null != onWheelListener) 
                onWheelListener.onScrollingStarted(RulerView.this);
            
        

        /**
         * 滚动中
         * @param distance 滚动的距离
         */
        @Override
        public void onScroll(int distance) 
            doScroll(distance);
        

        /**
         * 滚动结束
         */
        @Override
        public void onFinished() 
            if (outOfRange()) 
                return;
            
            if (isScrollingPerformed) 
                //滚动结束
                if (null != onWheelListener) 
                    onWheelListener.onScrollingFinished(RulerView.this);
                
                isScrollingPerformed = false;
            
            scrollingOffset = 0;
            invalidate();
        

        /**
         * 验证滚动是否在正确位置
         */
        @Override
        public void onJustify() 
            if (outOfRange()) 
                return;
            
            if (Math.abs(scrollingOffset) > RulerViewScroller.MIN_DELTA_FOR_SCROLLING) 
                if (scrollingOffset < -mScaleSpace / 2) 
                    scroller.scroll(mScaleSpace + scrollingOffset, 0);
                 else if (scrollingOffset > mScaleSpace / 2) 
                    scroller.scroll(scrollingOffset - mScaleSpace, 0);
                 else 
                    scroller.scroll(scrollingOffset, 0);
                
            
        
    ;



    /**
     * 超出左右范围
     * @return
     */
    private boolean outOfRange() 
        //这个是越界后需要回滚的大小值
        int outRange = 0;
        if (mCurrentValue < mMinValue/mScaleBase) 
            outRange = (mCurrentValue - mMinValue/mScaleBase) * mScaleSpace;
         else if (mCurrentValue > mMaxValue/mScaleBase) 
            outRange = (mCurrentValue - mMaxValue/mScaleBase) * mScaleSpace;
        
        if (0 != outRange) 
            scrollingOffset = 0;
            scroller.scroll(-outRange, 100);
            return true;
        
        return false;
    

    /**
     * 滚动中回调最新值
     * @param delta
     */
    private void doScroll(int delta) 
        scrollingOffset += delta;
        int offsetCount = scrollingOffset / mScaleSpace;
        if (0 != offsetCount) 
            // 显示在范围内
            int oldValueIndex = Math.min(Math.max(mMinValue, mCurrentValue*mScaleBase), mMaxValue);
            mCurrentValue -= offsetCount;
            scrollingOffset -= offsetCount * mScaleSpace;
            if (null != onWheelListener) 
                //回调通知最新的值
                int valueIndex = Math.min(Math.max(mMinValue, mCurrentValue*mScaleBase), mMaxValue);
                onWheelListener.onChanged(this, oldValueIndex + "",valueIndex+"");
            
        
        invalidate();
    

    private float mDownFocusX;
    private float mDownFocusY;
    private boolean isDisallowIntercept;
    @Override
    public boolean onTouchEvent(MotionEvent event) 
        if (!isEnabled()) 
            return true;
        
        switch (event.getAction()) 
            case MotionEvent.ACTION_DOWN:
                mDownFocusX = event.getX();
                mDownFocusY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (!isDisallowIntercept && Math.abs(event.getY() - mDownFocusY) < Math.abs(event.getX() - mDownFocusX)) 
                    isDisallowIntercept = true;
                    if (getParent() != null) 
                        getParent().requestDisallowInterceptTouchEvent(true);
                    
                
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (getParent() != null) 
                    getParent().requestDisallowInterceptTouchEvent(false);
                
                isDisallowIntercept = false;
                break;
        
        return scroller.onTouchEvent(event);
    

    private OnRulerViewScrollListener onWheelListener;

    /**
     * 添加滚动回调
     * @param listener the listener
     */
    public void setScrollingListener(OnRulerViewScrollListener listener) 
        onWheelListener = listener;
    


    public interface OnRulerViewScrollListener<T> 
        /**
         * 当更改选择的时候回调方法
         * @param rulerView 状态更改的view
         * @param oldValue  当前item的旧值
         * @param newValue  当前item的新值
         */
        void onChanged(RulerView rulerView, T oldValue, T newValue);

        /**
         * 滚动启动时调用的回调方法
         * @param rulerView
         */
        void onScrollingStarted(RulerView rulerView);

        /**
         * 滚动结束时调用的回调方法
         * @param rulerView
         */
        void onScrollingFinished(RulerView rulerView);
    

其中有些参考了网上实现方式并进行拓展以后就实现了下面的效果。

这里附上github:https://github.com/dalong982242260/AndroidRulerView

以上是关于Android自定义一个属于自己的刻度尺的主要内容,如果未能解决你的问题,请参考以下文章

Android 自定义View:实现一个 FM 刻度尺

Android自定义View之区块选择器

Android:TextInputLayout - 自定义提示、底线和错误消息的颜色

Android自定义刻度尺的实现思路以及步骤

Android自定义一个属于自己的时间钟表

Android自定义View——绘制一个会动的时钟