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

简介: 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


相关文章
|
29天前
|
缓存 前端开发 Android开发
安卓开发中的自定义视图:从零到英雄
【10月更文挑战第42天】 在安卓的世界里,自定义视图是一块画布,让开发者能够绘制出独一无二的界面体验。本文将带你走进自定义视图的大门,通过深入浅出的方式,让你从零基础到能够独立设计并实现复杂的自定义组件。我们将探索自定义视图的核心概念、实现步骤,以及如何优化你的视图以提高性能和兼容性。准备好了吗?让我们开始这段创造性的旅程吧!
26 1
|
2月前
|
Android开发 开发者
安卓应用开发中的自定义视图
【9月更文挑战第37天】在安卓开发的海洋中,自定义视图犹如一座座小岛,等待着勇敢的探索者去发现其独特之处。本文将带领你踏上这段旅程,从浅滩走向深海,逐步揭开自定义视图的神秘面纱。
44 3
|
4月前
|
存储 Shell Android开发
基于Android P,自定义Android开机动画的方法
本文详细介绍了基于Android P系统自定义开机动画的步骤,包括动画文件结构、脚本编写、ZIP打包方法以及如何将自定义动画集成到AOSP源码中。
87 2
基于Android P,自定义Android开机动画的方法
|
2月前
|
数据可视化 Android开发 开发者
安卓应用开发中的自定义View组件
【10月更文挑战第5天】在安卓应用开发中,自定义View组件是提升用户交互体验的利器。本篇将深入探讨如何从零开始创建自定义View,包括设计理念、实现步骤以及性能优化技巧,帮助开发者打造流畅且富有创意的用户界面。
95 0
|
4月前
|
供应链 物联网 区块链
未来触手可及:探索新兴技术的趋势与应用安卓开发中的自定义视图:从基础到进阶
【8月更文挑战第30天】随着科技的飞速发展,新兴技术如区块链、物联网和虚拟现实正在重塑我们的世界。本文将深入探讨这些技术的发展趋势和应用场景,带你领略未来的可能性。
|
4月前
|
测试技术 Android开发 Python
探索软件测试的艺术:从基础到高级安卓应用开发中的自定义视图
【8月更文挑战第29天】在软件开发的世界中,测试是不可或缺的一环。它如同艺术一般,需要精细的技巧和深厚的知识。本文旨在通过浅显易懂的语言,引领读者从软件测试的基础出发,逐步深入到更复杂的测试策略和工具的使用,最终达到能够独立进行高效测试的水平。我们将一起探索如何通过不同的测试方法来确保软件的质量和性能,就像艺术家通过不同的色彩和笔触来完成一幅画作一样。
|
1月前
|
搜索推荐 前端开发 Android开发
安卓应用开发中的自定义视图实现
【10月更文挑战第30天】在安卓开发的海洋中,自定义视图是那抹不可或缺的亮色,它为应用界面的个性化和交互体验的提升提供了无限可能。本文将深入探讨如何在安卓平台创建自定义视图,并展示如何通过代码实现这一过程。我们将从基础出发,逐步引导你理解自定义视图的核心概念,然后通过一个实际的代码示例,详细讲解如何将理论应用于实践,最终实现一个美观且具有良好用户体验的自定义控件。无论你是想提高自己的开发技能,还是仅仅出于对安卓开发的兴趣,这篇文章都将为你提供价值。
|
1月前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
37 5
|
2月前
|
XML 前端开发 Java
安卓应用开发中的自定义View组件
【10月更文挑战第5天】自定义View是安卓应用开发的一块基石,它为开发者提供了无限的可能。通过掌握其原理和实现方法,可以创造出既美观又实用的用户界面。本文将引导你了解自定义View的创建过程,包括绘制技巧、事件处理以及性能优化等关键步骤。
|
3月前
|
Android开发 开发者
安卓开发中的自定义视图:从入门到精通
【9月更文挑战第19天】在安卓开发的广阔天地中,自定义视图是一块充满魔力的土地。它不仅仅是代码的堆砌,更是艺术与科技的完美结合。通过掌握自定义视图,开发者能够打破常规,创造出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战应用,一步步展示如何用代码绘出心中的蓝图。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇通往创意和效率的大门。让我们一起探索自定义视图的秘密,将你的应用打造成一件艺术品吧!
69 10