• 自定义View之身高选择器


     自定义View画一个身高测量尺子,可以理解成一个竖着的尺子

    最近刚好有空,在家里整理这几年工作内容,简单记录一下,也方便以后自己查阅,最终效果图如下

    步骤如下:

    首先还是继承view,重写onMeasure()方法

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
        }
    
        /**
         * 测量控件所需宽度
         */
        public int measureWidth(int widthMeasureSpec) {
            switch (MeasureSpec.getMode(widthMeasureSpec)) {
                case MeasureSpec.AT_MOST:
                case MeasureSpec.UNSPECIFIED: {
                    switch (mOrientation) {
                        case HORIZONTAL: {
                            return mLines * space;
                        }
                        case VERTICAL:
                        default: {
                            float textWidth = mTextPaint.measureText(String.valueOf(mLines / mSetupValue));
                            float width = textWidth + mLongLineLength + mMarkerWidth + mMarkerSpace + getPaddingLeft() + getPaddingRight();
                            return (int) width;
                        }
                    }
                }
                case MeasureSpec.EXACTLY:
                default:
                    return MeasureSpec.getSize(widthMeasureSpec);
            }
    
        }
    
        /**
         * 测量控件所需高度
         */
        public int measureHeight(int heightMeasureSpec) {
            switch (MeasureSpec.getMode(heightMeasureSpec)) {
                case MeasureSpec.AT_MOST:
                case MeasureSpec.UNSPECIFIED: {
                    switch (mOrientation) {
                        case HORIZONTAL: {
                            float height = mTextPaint.getTextSize() + mLongLineLength + mMarkerWidth + mMarkerSpace + getPaddingTop() + getPaddingBottom();
                            return (int) height;
                        }
                        case VERTICAL:
                        default: {
                            return mLines * space;
                        }
                    }
                }
                case MeasureSpec.EXACTLY:
                default:
                    return MeasureSpec.getSize(heightMeasureSpec);
            }
        }

    第二步会在onDraw()方法中绘制当前View,此时绘制的view也是初始时的view

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            adjustMarker(false);
            canvas.drawColor(mBackgroundColor);
            switch (mOrientation) {
                case VERTICAL:
                    drawVertical(canvas);
                    break;
                case HORIZONTAL:
                    drawHorizontal(canvas);
                    break;
            }
        }

    默认情况下不会调整标记view的位置。当然标记的位置可以时固定的,也可以不固定的,固定的话就是滚动尺子的位置

    画图的时候,先画红色标尺的位置,然后画刻度的位置,刻度的位置每10个画一根长线,就是每10个分为1组,具体代码如下:

        /**
         * 绘制 方向为垂直方向时的布局
         *
         * @param canvas 画布
         */
        protected void drawVertical(Canvas canvas) {
            //vertical mode
            // bottom start position
            int bottom = getHeight() - getPaddingBottom();
            int left = getPaddingLeft();
    
            float maxTextWidth = mTextPaint.measureText(String.valueOf((mLines+mStartLineValue) / mOutSideLine * mSetupValue));
    
            int shakeCenter = (getHeight() >> 1) + getScrollY();
            resetLinesArr();
    
            for (int i = 0; i <= mLines; i++) {
                int value = mStartLineValue + (i * mSetupValue);
                float lineLength;
                switch (i % mOutSideLine) {
                    case ZERO:
                        String text = String.valueOf(value);
                        float currentTextWidth = mTextPaint.measureText(text);
                        canvas.drawText(text, left + (maxTextWidth - currentTextWidth) / 2, bottom - i * space, mTextPaint);
                        lineLength = mLongLineLength;
                        break;
                    default:
                        lineLength = mShortLineLength;
                        break;
                }
                mLinesArr[i * 4] = left + maxTextWidth;
                mLinesArr[i * 4 + 1] = bottom - i * space;
                mLinesArr[i * 4 + 2] = left + maxTextWidth + lineLength;
                mLinesArr[i * 4 + 3] = bottom - i * space;
            }
    
            //绘制线
            canvas.drawLines(mLinesArr, mPaint);
            //绘制高亮线
            canvas.drawLine(left + maxTextWidth,
                    bottom - mCurrentLineIndex * space,
                    left + maxTextWidth + (((mCurrentLineIndex) % mOutSideLine == 0) ? mLongLineLength : mShortLineLength),
                    bottom - mCurrentLineIndex * space,
                    mHighlightPaint);
        }

    第三个比较重要的是触摸事件,每一次向上或者向下都要重绘刷新当前View

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    if (!mOverScroller.isFinished()) mOverScroller.abortAnimation();
                    //adjustMarker(true);
                    break;
                case MotionEvent.ACTION_MOVE:
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    adjustMarker(true);
                    break;
            }
            mGestureDetector.onTouchEvent(event);
            return true;
        }

    所有具体代码如下:

    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.support.v4.view.ViewCompat;
    import android.util.AttributeSet;
    import android.util.TypedValue;
    import android.view.GestureDetector;
    import android.view.MotionEvent;
    import android.view.View;
    
    
    import com.bingo.customer.R;
    
    import java.util.Arrays;
    
    /**
     * 身高体重测量控件
     * 这个控件是为了项目中的身高体重选择而设计编码的
     * 不适合多数据情况,因为没有使用任何的回收机制
     *
     */
    public class HeightView extends View {
    
        /**
         * 水平方向
         */
        public static final int HORIZONTAL = 1;
        /**
         * 垂直方向
         */
        public static final int VERTICAL = 2;
    
        public static final int ZERO = 0;
    
        /**
         * 默认的阻尼系数
         */
        public static final float DEFAULT_RATIO = 2.0f;
    
        private static final String TAG = "HeightView";
        /**
         * 阻尼系数,在fling下的阻力,阻力越大就飞的就约慢
         */
        private float ratio = DEFAULT_RATIO;
    
    
        /**
         * 需要绘制多少行
         */
        private int mLines = 240;
        /**
         * 突出行 就是长线 那一行
         */
        private int mOutSideLine = 10;
        /**
         * 每个outSideLine的步进值
         * 比如:
         * 步进值是 5 那么就是  0,5,10,15,20
         * 步进值是 10 那么就是 0,10,20,30,40
         */
        private int mSetupValue = 1;
    
    
        /**
         * 每个格子的间距
         */
        private int space;
        /**
         * 用于普通绘制的画笔
         */
        private Paint mPaint;
        /**
         * 绘制文本的画笔
         */
        private Paint mTextPaint;
        /**
         * 短线的长度
         */
        private float mShortLineLength;
        /**
         * 长线的长度
         */
        private float mLongLineLength;
        /**
         * 高亮画笔,绘制选中线的
         */
        private Paint mHighlightPaint;
    
        /**
         * Marker画笔
         */
        private Paint mMarkerPaint;
    
        /**
         * scroller 用于滚动的辅助类
         */
        private OverScroller mOverScroller;
        /**
         * 手势探测器 用于探测手势
         */
        private GestureDetector mGestureDetector;
        /**
         * 高亮色选择中的颜色
         */
        private int mHighLightColor;
        /**
         * 文本颜色
         */
        private int mTextColor;
        /**
         * 文本大小
         */
        private float mTextSize;
        /**
         * 高亮的线的宽度
         */
        private float mHighlightWidth;
        /**
         * 普通的线的宽度
         */
        private float mLineWidth;
    
        /**
         * 线条的颜色
         */
        private int mLineColor;
    
        /**
         * 上一次是否是fling模式
         */
        private boolean mPreviousIsFling = false;
    
        /**
         * 开始行
         * 如果开始行为 0,步进值为10
         * 则 0,10,20,30,40
         * 如果开始行为 10,步进值为10
         * 则 100,110,120,130
         */
        private int mStartLineValue = 0;
    
        /**
         * 用于记录当前的marker的位置
         */
        private int mCurrentLineIndex;
    
        /**
         * 保存线位置的数组
         */
        private float[] mLinesArr = new float[4];
    
        /**
         * 重力方向
         */
        private int mOrientation = VERTICAL;
    
        /**
         * 指示器的宽度
         */
        private int mMarkerWidth = 45;
    
        /**
         * 指示器和长线的距离
         */
        private int mMarkerSpace = 20;
    
        /**
         * 背景色
         */
        private int mBackgroundColor = Color.parseColor("#03b7ee");
    
        /**
         * 标记物路径
         */
        private Path mMarkerPath;
        /**
         * Marker颜色
         */
        private int mMarkerColor;
    
        private OnItemChangedListener mOnItemChangedListener;
    
    
        public HeightView(Context context) {
            super(context);
            init();
        }
    
        public HeightView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public HeightView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
            if (attrs != null) {
                TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.HeightView, defStyleAttr, 0);
                mOrientation = attributes.getInt(R.styleable.HeightView_orientation, VERTICAL);
                mBackgroundColor = attributes.getColor(R.styleable.HeightView_backgroundColor, mBackgroundColor);
                mTextColor = attributes.getColor(R.styleable.HeightView_textColor, mTextColor);
                mTextPaint.setColor(mTextColor);
                mTextSize = attributes.getDimension(R.styleable.HeightView_textSize, mTextSize);
                mTextPaint.setTextSize(mTextSize);
    
                mHighLightColor = attributes.getColor(R.styleable.HeightView_highlightColor, mHighLightColor);
                mHighlightPaint.setColor(mHighLightColor);
                mHighlightWidth = attributes.getDimension(R.styleable.HeightView_highlightLineWidth, mHighlightWidth);
                mHighlightPaint.setStrokeWidth(mHighlightWidth);
    
                mMarkerColor = attributes.getColor(R.styleable.HeightView_markerColor, mMarkerColor);
                mMarkerPaint.setColor(mMarkerColor);
    
                mLineColor = attributes.getColor(R.styleable.HeightView_mlineColor, mLineColor);
                mPaint.setColor(mLineColor);
    
                mLineWidth = attributes.getDimension(R.styleable.HeightView_lineWidth, mLineWidth);
                mPaint.setStrokeWidth(mLineWidth);
    
                ratio = attributes.getFloat(R.styleable.HeightView_ratio, ratio);
                mMarkerSpace = attributes.getDimensionPixelOffset(R.styleable.HeightView_markerSpace, mMarkerSpace);
    
                mLines = attributes.getInt(R.styleable.HeightView_lines, mLines);
                mStartLineValue = attributes.getInt(R.styleable.HeightView_startLineValue, mStartLineValue);
    
                mMarkerWidth = attributes.getDimensionPixelOffset(R.styleable.HeightView_markerSize, mMarkerWidth);
    
                attributes.recycle();
            }
        }
    
    
        /**
         * 初始化所需条件
         */
        private void init() {
            setLayerType(LAYER_TYPE_HARDWARE, null);
    
            space = (int) (getResources().getDisplayMetrics().density * 7);
            mHighLightColor = Color.parseColor("#1e7d9e");
    
            mTextColor = mMarkerColor = Color.WHITE;
    
            mTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics());
            mShortLineLength = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12, getResources().getDisplayMetrics());
            mLongLineLength = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, getResources().getDisplayMetrics());
            mHighlightWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1.5f, getResources().getDisplayMetrics());
            mLineWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
    
            mMarkerPath = new Path();
    
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setColor(mLineColor = Color.WHITE);
            mPaint.setStrokeWidth(mLineWidth);
    
            mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mTextPaint.setTextSize(mTextSize);
            mTextPaint.setStyle(Paint.Style.FILL);
            mTextPaint.setColor(mTextColor);
            mTextPaint.setTextAlign(Paint.Align.CENTER);
    
            mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mHighlightPaint.setStyle(Paint.Style.STROKE);
            mHighlightPaint.setColor(mHighLightColor);
            mHighlightPaint.setStrokeWidth(mHighlightWidth);
    
            mMarkerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mHighlightPaint.setStyle(Paint.Style.FILL);
            mHighlightPaint.setColor(mMarkerColor);
    
    
            mOverScroller = new OverScroller(getContext());
    
            mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                    switch (mOrientation) {
                        case VERTICAL: {
                            //往上滚动
                            if (distanceY > 0) {
                                //最大滚动到Marker位置
                                int distance = (getHeight() >> 1) - getPaddingBottom()/* - (mStartLineValue) * space*/;
                                if (getScrollY() + distanceY > distance) {
                                    scrollTo(0, distance);
                                    ViewCompat.postInvalidateOnAnimation(HeightView.this);
                                } else if (getScrollY() + distanceY < distance) {
                                    scrollBy(0, (int) distanceY);
                                    ViewCompat.postInvalidateOnAnimation(HeightView.this);
                                }
                                //往下滚动
                            } else if (distanceY < 0) {
                                int minDistance = (mLines * space - (getHeight() >> 1)) + getPaddingBottom();
                                if (getScrollY() < -minDistance) {
                                    scrollTo(0, -minDistance);
                                    ViewCompat.postInvalidateOnAnimation(HeightView.this);
                                } else if (getScrollY() > -minDistance) {
                                    scrollBy(0, (int) distanceY);
                                    ViewCompat.postInvalidateOnAnimation(HeightView.this);
                                }
                            }
                        }
                        break;
                        case HORIZONTAL: {
                            //往左滚动
                            if (distanceX > 0) {
                                int maxX = (mLines /*+ mStartLineValue*/) * space - (getWidth() >> 1) + getPaddingLeft();
                                if (getScrollX() + distanceX > maxX) {
                                    scrollTo(maxX, 0);
                                    ViewCompat.postInvalidateOnAnimation(HeightView.this);
                                } else if (getScrollX() + distanceX <= maxX) {
                                    scrollBy((int) distanceX, 0);
                                    ViewCompat.postInvalidateOnAnimation(HeightView.this);
                                }
                                //往右滚动
                            } else if (distanceX < 0) {
                                int minX = -((getWidth() >> 1) - getPaddingLeft());
                                if (getScrollX() + distanceX < minX) {
                                    scrollTo(minX, 0);
                                    ViewCompat.postInvalidateOnAnimation(HeightView.this);
                                } else if (getScrollX() + distanceX >= minX) {
                                    scrollBy((int) distanceX, 0);
                                    ViewCompat.postInvalidateOnAnimation(HeightView.this);
                                }
                            }
                        }
                        break;
                    }
    
                    return true;
                }
    
                @Override
                public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                    switch (mOrientation) {
                        case VERTICAL: {
                            int minDistance = ((mLines) * space - (getHeight() >> 1)) + getPaddingBottom();
                            int maxDistance = (getHeight() >> 1) - getPaddingBottom();
                            mOverScroller.fling(0, getScrollY(), 0, (int) (-velocityY / ratio), 0, 0, -minDistance, maxDistance, 0, 100);
                        }
                        break;
                        case HORIZONTAL: {
                            int minX = -((getWidth() >> 1) - getPaddingLeft());
                            int maxX = (mLines /*+ mStartLineValue*/) * space - (getWidth() >> 1) + getPaddingLeft();
                            mOverScroller.fling(getScrollX(), 0, (int) (-velocityX / ratio), 0, minX, maxX, 0, 0, 100, 0);
                        }
                        break;
                    }
                    ViewCompat.postInvalidateOnAnimation(HeightView.this);
                    return true;
                }
            });
        }
    
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
        }
    
        /**
         * 测量控件所需宽度
         */
        public int measureWidth(int widthMeasureSpec) {
            switch (MeasureSpec.getMode(widthMeasureSpec)) {
                case MeasureSpec.AT_MOST:
                case MeasureSpec.UNSPECIFIED: {
                    switch (mOrientation) {
                        case HORIZONTAL: {
                            return mLines * space;
                        }
                        case VERTICAL:
                        default: {
                            float textWidth = mTextPaint.measureText(String.valueOf(mLines / mSetupValue));
                            float width = textWidth + mLongLineLength + mMarkerWidth + mMarkerSpace + getPaddingLeft() + getPaddingRight();
                            return (int) width;
                        }
                    }
                }
                case MeasureSpec.EXACTLY:
                default:
                    return MeasureSpec.getSize(widthMeasureSpec);
            }
    
        }
    
        /**
         * 测量控件所需高度
         */
        public int measureHeight(int heightMeasureSpec) {
            switch (MeasureSpec.getMode(heightMeasureSpec)) {
                case MeasureSpec.AT_MOST:
                case MeasureSpec.UNSPECIFIED: {
                    switch (mOrientation) {
                        case HORIZONTAL: {
                            float height = mTextPaint.getTextSize() + mLongLineLength + mMarkerWidth + mMarkerSpace + getPaddingTop() + getPaddingBottom();
                            return (int) height;
                        }
                        case VERTICAL:
                        default: {
                            return mLines * space;
                        }
                    }
                }
                case MeasureSpec.EXACTLY:
                default:
                    return MeasureSpec.getSize(heightMeasureSpec);
            }
        }
    
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    if (!mOverScroller.isFinished()) mOverScroller.abortAnimation();
                    //adjustMarker(true);
                    break;
                case MotionEvent.ACTION_MOVE:
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    adjustMarker(true);
                    break;
            }
            mGestureDetector.onTouchEvent(event);
            return true;
        }
    
        @Override
        public boolean performClick() {
            return super.performClick();
        }
    
        /**
         * 调整目前选择中的条目
         */
        public void adjustMarker(boolean adjustPosition) {
            final int previous = mCurrentLineIndex;
    
            switch (mOrientation) {
                case VERTICAL: {
                    int startY = (getHeight() >> 1) - getPaddingBottom();
                    int scrollY = getScrollY();
                    int progress = scrollY - startY;
                    mCurrentLineIndex = -progress / space;
    
                    if (mCurrentLineIndex > mLines) mCurrentLineIndex = mLines;
                    else if (mCurrentLineIndex < 0) mCurrentLineIndex = 0;
    
                    int expectY = space * -mCurrentLineIndex + startY;
                    if (adjustPosition && scrollY != expectY) {
                        //scrollTo(0, expectY);
                        mOverScroller.startScroll(0, getScrollY(), 0, expectY - scrollY, 0);
                        ViewCompat.postInvalidateOnAnimation(this);
                    }
                }
                break;
                case HORIZONTAL: {
                    int startX = -((getWidth() >> 1) - getPaddingLeft());
                    int scrollX = getScrollX();
                    int progress = startX - scrollX;
                    mCurrentLineIndex = -progress / space;
                    if (mCurrentLineIndex > mLines) mCurrentLineIndex = mLines;
                    else if (mCurrentLineIndex < 0) mCurrentLineIndex = 0;
    
                    int expectX = space * mCurrentLineIndex + startX;
    
                    if (adjustPosition && scrollX != expectX) {
                        //scrollTo(0, expectY);
                        mOverScroller.startScroll(getScrollX(), 0, expectX - scrollX, 0, 0);
                        ViewCompat.postInvalidateOnAnimation(this);
                    }
                }
                break;
            }
            if (previous != mCurrentLineIndex) onValueChanged();
        }
    
        /**
         * 当值可能发生变化后执行
         */
        public void onValueChanged() {
            if (mCurrentLineIndex >= 0 && mCurrentLineIndex <= mLines) {
                int index = mCurrentLineIndex;
                int value = mStartLineValue + (index * mSetupValue);
                if (mOnItemChangedListener != null) mOnItemChangedListener.onItemChanged(index, value);
            }
        }
    
        @Override
        public void computeScroll() {
            if (mOverScroller.computeScrollOffset()) {
                mPreviousIsFling = true;
                switch (mOrientation) {
                    case VERTICAL:
                        scrollTo(0, mOverScroller.getCurrY());
                        break;
                    case HORIZONTAL:
                        scrollTo(mOverScroller.getCurrX(), 0);
                        break;
                }
                ViewCompat.postInvalidateOnAnimation(this);
            } else {
                if (mPreviousIsFling) {
                    mPreviousIsFling = false;
                    adjustMarker(true);
                }
            }
        }
    
        /**
         * 重置线组
         */
        private void resetLinesArr() {
            if (mLinesArr.length < (mLines + 1) * 4) {
                //需要重新创建数组
                mLinesArr = new float[(mLines + 1) * 4];
            } else {
                Arrays.fill(mLinesArr, 0);
            }
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            adjustMarker(false);
            canvas.drawColor(mBackgroundColor);
            switch (mOrientation) {
                case VERTICAL:
                    drawVertical(canvas);
                    break;
                case HORIZONTAL:
                    drawHorizontal(canvas);
                    break;
            }
        }
    
        /**
         * 绘制 方向为垂直方向时的布局
         *
         * @param canvas 画布
         */
        protected void drawVertical(Canvas canvas) {
            //vertical mode
            // bottom start position
            int bottom = getHeight() - getPaddingBottom();
            int left = getPaddingLeft();
    
            float maxTextWidth = mTextPaint.measureText(String.valueOf((mLines+mStartLineValue) / mOutSideLine * mSetupValue));
    
            int shakeCenter = (getHeight() >> 1) + getScrollY();
    
            //绘制三角形
            //如果想使用图片,可以自行绘制图片
    //        mMarkerPath.reset();
    //        mMarkerPath.moveTo(left + maxTextWidth + mLongLineLength + mMarkerSpace, shakeCenter);
    //        mMarkerPath.lineTo(left + maxTextWidth + mLongLineLength + mMarkerSpace + mMarkerWidth, shakeCenter - mMarkerWidth);
    //        mMarkerPath.lineTo(left + maxTextWidth + mLongLineLength + mMarkerSpace + mMarkerWidth, shakeCenter + mMarkerWidth);
    //        mMarkerPath.lineTo(left + maxTextWidth + mLongLineLength + mMarkerSpace, shakeCenter);
    //        canvas.drawPath(mMarkerPath, mMarkerPaint);
    
            resetLinesArr();
    
            for (int i = 0; i <= mLines; i++) {
                int value = mStartLineValue + (i * mSetupValue);
                float lineLength;
                switch (i % mOutSideLine) {
                    case ZERO:
                        String text = String.valueOf(value);
                        float currentTextWidth = mTextPaint.measureText(text);
                        canvas.drawText(text, left + (maxTextWidth - currentTextWidth) / 2, bottom - i * space, mTextPaint);
                        lineLength = mLongLineLength;
                        break;
                    default:
                        lineLength = mShortLineLength;
                        break;
                }
                mLinesArr[i * 4] = left + maxTextWidth;
                mLinesArr[i * 4 + 1] = bottom - i * space;
                mLinesArr[i * 4 + 2] = left + maxTextWidth + lineLength;
                mLinesArr[i * 4 + 3] = bottom - i * space;
            }
    
            //绘制线
            canvas.drawLines(mLinesArr, mPaint);
            //绘制高亮线
            canvas.drawLine(left + maxTextWidth,
                    bottom - mCurrentLineIndex * space,
                    left + maxTextWidth + (((mCurrentLineIndex) % mOutSideLine == 0) ? mLongLineLength : mShortLineLength),
                    bottom - mCurrentLineIndex * space,
                    mHighlightPaint);
        }
    
        /**
         * 绘制水平方向上的布局
         *
         * @param canvas 画布
         */
        protected void drawHorizontal(Canvas canvas) {
    
            //Horizontal mode
            // bottom position
            int bottom = getHeight() - getPaddingBottom();
            int left = getPaddingLeft();
    
            float maxTextWidth = mTextPaint.measureText(String.valueOf(mLines / mOutSideLine * mSetupValue));
    
            //中心
            int shakeCenter = (getWidth() >> 1) + getScrollX();
    
            //三角形顶点
            float vertexY = getHeight() - getPaddingBottom() - mTextPaint.getTextSize() - mLongLineLength - mMarkerSpace;
    
            //绘制三角形
            mMarkerPath.reset();
            mMarkerPath.moveTo(shakeCenter, vertexY);
            mMarkerPath.lineTo(shakeCenter - mMarkerWidth, vertexY - mMarkerWidth);
            mMarkerPath.lineTo(shakeCenter + mMarkerWidth, vertexY - mMarkerWidth);
            mMarkerPath.lineTo(shakeCenter, vertexY);
            canvas.drawPath(mMarkerPath, mMarkerPaint);
            //验证线组
            resetLinesArr();
    
            //生成线组
    
            for (int i = 0; i <= mLines; i++) {
                int value = mStartLineValue + (i * mSetupValue);
                float lineLength;
                switch (i % mOutSideLine) {
                    case ZERO:
                        String text = String.valueOf(value);
                        canvas.drawText(text, left + i * space, bottom, mTextPaint);
                        lineLength = mLongLineLength;
                        break;
                    default:
                        lineLength = mShortLineLength;
                        break;
                }
    
                /*startX*/
                mLinesArr[i * 4] = left + i * space;
                /*startY*/
                mLinesArr[i * 4 + 1] = bottom - mTextPaint.getTextSize();
                /*stopX*/
                mLinesArr[i * 4 + 2] = left + i * space;
                /*stopY*/
                mLinesArr[i * 4 + 3] = bottom - (mTextPaint.getTextSize() + lineLength);
            }
    
            //绘制线组
            canvas.drawLines(mLinesArr, mPaint);
    
    //        //绘制当前选中的线条
            canvas.drawLine(left + mCurrentLineIndex * space,
                    (bottom - mTextPaint.getTextSize()),
                    left + mCurrentLineIndex * space,
                    (bottom - mTextPaint.getTextSize()) - (mCurrentLineIndex % mOutSideLine == 0 ? mLongLineLength : mShortLineLength),
                    mHighlightPaint);
        }
    
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            adjustMarker(true);
        }
    
        public int getLines() {
            return mLines;
        }
    
        public void setLines(int mLines) {
            this.mLines = mLines;
            requestLayout();
        }
    
        public int getOutSideLine() {
            return mOutSideLine;
        }
    
        public void setOutSideLine(int mOutSideLine) {
            this.mOutSideLine = mOutSideLine;
            invalidate();
        }
    
        public int getSetupValue() {
            return mSetupValue;
        }
    
        public void setSetupValue(int mSetupValue) {
            this.mSetupValue = mSetupValue;
            invalidate();
        }
    
        public int getCurrentLineIndex() {
            return mCurrentLineIndex;
        }
    
        public void setCurrentLineIndex(int currentLineIndex) {
            int distance = currentLineIndex * space;
            if (mOverScroller != null && !mOverScroller.isFinished()) mOverScroller.abortAnimation();
            switch (mOrientation) {
                case HORIZONTAL:
                    int startX = (getWidth() >> 1) - getPaddingLeft();
                    scrollTo(distance - startX, getScrollY());
                    break;
                case VERTICAL:
                    int startY = ((getHeight() >> 1) - getPaddingBottom());
                    scrollTo(getScrollX(), -distance + startY);
                    break;
            }
            adjustMarker(true);
            postInvalidate();
        }
    
        public int getSpace() {
            return space;
        }
    
        public void setSpace(int space) {
            this.space = space;
            requestLayout();
        }
    
        public float getShortLineLength() {
            return mShortLineLength;
        }
    
        public void setShortLineLength(float mShortLineLength) {
            this.mShortLineLength = mShortLineLength;
            invalidate();
        }
    
        public float getLongLineLength() {
            return mLongLineLength;
        }
    
        public void setLongLineLength(float mLongLineLength) {
            this.mLongLineLength = mLongLineLength;
            invalidate();
        }
    
        public int getHighLightColor() {
            return mHighLightColor;
        }
    
        public void setHighLightColor(int highLightColor) {
            this.mHighLightColor = highLightColor;
            mHighlightPaint.setColor(highLightColor);
            invalidate();
        }
    
        public int getTextColor() {
            return mTextColor;
        }
    
        public void setTextColor(int textColor) {
            this.mTextColor = textColor;
            mTextPaint.setColor(textColor);
            invalidate();
        }
    
        public float getTextSize() {
            return mTextSize;
        }
    
        public void setTextSize(float textSize) {
            this.mTextSize = textSize;
            mTextPaint.setTextSize(textSize);
            requestLayout();
        }
    
        public float getHighlightWidth() {
            return mHighlightWidth;
        }
    
        public void setHighlightWidth(float highlightWidth) {
            this.mHighlightWidth = highlightWidth;
            mHighlightPaint.setStrokeWidth(highlightWidth);
            requestLayout();
        }
    
        public float getLineWidth() {
            return mLineWidth;
        }
    
        public void setLineWidth(float lineWidth) {
            this.mLineWidth = lineWidth;
            mPaint.setStrokeWidth(lineWidth);
            requestLayout();
        }
    
        public int getLineColor() {
            return mLineColor;
        }
    
        public void setLineColor(int lineColor) {
            this.mLineColor = lineColor;
            mPaint.setColor(lineColor);
            invalidate();
        }
    
    
        public int getStartLineValue() {
            return mStartLineValue;
        }
    
        public void setStartLineValue(int startLineValue) {
            this.mStartLineValue = startLineValue;
            requestLayout();
        }
    
        public int getOrientation() {
            return mOrientation;
        }
    
        public void setOrientation(int orientation) {
            this.mOrientation = orientation;
            requestLayout();
        }
    
        public int getMarkerWidth() {
            return mMarkerWidth;
        }
    
        public void setMarkerWidth(int markerWidth) {
            this.mMarkerWidth = markerWidth;
            requestLayout();
        }
    
        public int getMarkerSpace() {
            return mMarkerSpace;
        }
    
        public void setMarkerSpace(int markerSpace) {
            this.mMarkerSpace = markerSpace;
            requestLayout();
        }
    
        public int getBackgroundColor() {
            return mBackgroundColor;
        }
    
        public void setBackgroundColor(int backgroundColor) {
            this.mBackgroundColor = backgroundColor;
            invalidate();
        }
    
        public int getMarkerColor() {
            return mMarkerColor;
        }
    
        public void setMarkerColor(int markerColor) {
            this.mMarkerColor = markerColor;
            mMarkerPaint.setColor(markerColor);
            invalidate();
        }
    
        public float getRatio() {
            return ratio;
        }
    
        /**
         * 设置阻尼系数
         *
         * @param ratio 需要设置的阻尼系数,默认的是 {@link #DEFAULT_RATIO}
         */
        public void setRatio(float ratio) {
            this.ratio = ratio;
        }
    
        /**
         * 设置item变化监听器
         *
         * @param listener 需要设置item变化监听器
         */
        public void setOnItemChangedListener(OnItemChangedListener listener) {
            this.mOnItemChangedListener = listener;
        }
    
    
        /**
         * 条目变化监听器
         */
        public interface OnItemChangedListener {
            /**
             * 当条目发生变化后调用
             *
             * @param index 当前的选择中的条目的下表
             * @param value 选择中的条目的值
             */
            void onItemChanged(int index, int value);
        }
    
    }

    具体源码:https://github.com/zhangchunbin/Demo_Customer_view/blob/master/app/src/main/java/com/bingo/customer/view/scrollrulerview/HeightView.java

  • 相关阅读:
    [2020.11.15]CCPC Final 2019
    [2020.11.13]UOJ#424. 【集训队作业2018】count
    [2020.11.13]AtCoder Japan Alumni Group Summer Camp 2018 Day 2 K
    [2020.11.13]CF704C Black Widow
    [2020.11.13]CF765F Souvenirs
    [2020.11.13]AGC035D
    [2020.11.10]CSPS2020 翻车记
    拉格朗日反演(暂时鸽)与CF1349F2(xtq F2)
    [2020.6.20]ZJOI2020 Day1游记
    [2020.5.22]UOJ523 【美团杯2020】半前缀计数
  • 原文地址:https://www.cnblogs.com/zhang-cb/p/10483444.html
Copyright © 2020-2023  润新知