自定义带刻度的SeekBar

package com.example.ilaw66lawyer.ui.view;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.NinePatch;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.example.ilaw66lawyer.R;

import java.math.BigDecimal;

/**
 * @author: biwen.he
 * @date: 2017/12/11
 * @description: 自定义SeekBar 带刻度
 */
public class RangeSeekBar extends View {

    private static final float DEFAULT_RADIUS = 0.5f;

    //default seekbar's padding left and right
    private int DEFAULT_PADDING_LEFT_AND_RIGHT;

    private int defaultPaddingTop;
    //进度提示的背景 The background of the progress
    private final int mProgressHintBGId;

    // 按钮的背景   The background of the Drag button
    private final int mThumbResId;

    //刻度模式:number根据数字实际比例排列;other 均分排列
    //Scale mode:
    // number according to the actual proportion of the number of arranged;
    // other equally arranged
    private final int mCellMode;

    //single是Seekbar模式,range是RangeSeekbar
    //single is Seekbar mode, range is angeSeekbar
    //single = 1; range = 2
    private final int mSeekBarMode;

    //默认为1,当大于1时自动切回刻度模式
    //The default is 1, and when it is greater than 1,
    // it will automatically switch back to the scale mode
    private int cellsCount = 1;

    //刻度与进度条间的间距
    //The spacing between the scale and the progress bar
    private int textPadding;

    //刻度与进度条间的间距
    private float rulingWith;

    //进度提示背景与按钮之间的距离
    //The progress indicates the distance between the background and the button
    private int mHintBGPadding;

    private int mSeekBarHeight;
    private int mThumbSize;

    //两个按钮之间的最小距离
    //The minimum distance between two buttons
    private int reserveCount;

    private int mCursorTextHeight;
    private int mPartLength;
    private int heightNeeded;
    private int lineCorners;
    private int lineWidth;

    //选择过的进度条颜色
    // the color of the selected progress bar
    private int colorLineSelected;

    //进度条刻度颜色
    private int mRulingColor;

    //未选则的进度条颜色
    // the color of the unselected progress bar
    private int colorLineEdge;

    //The foreground color of progress bar and thumb button.
    private int colorPrimary;

    //The background color of progress bar and thumb button.
    private int colorSecondary;

    //刻度文字与提示文字的大小
    //Scale text and prompt text size
    private int mTextSize;
    private int mTextColor;

    private int lineTop, lineBottom, lineLeft, lineRight;

    //进度提示背景的高度,宽度如果是0的话会自适应调整
    //Progress prompted the background height, width,
    // if it is 0, then adaptively adjust
    private float mHintBGHeight;
    private float mHintBGWith;

    private float offsetValue;
    private float cellsPercent;
    private float reserveValue;
    private float reservePercent;
    private float maxValue, minValue;

    //真实的最大值和最小值
    //True maximum and minimum values
    private float mMin, mMax;

    private boolean isEnable = true;

    private final boolean mHideProgressHint;

    //刻度上显示的文字
    private CharSequence[] mTextArray;

    private Bitmap mProgressHintBG;
    private Paint mMainPaint = new Paint();
    private Paint mCursorPaint = new Paint();
    private Paint mProgressPaint;
    private RectF line = new RectF();
    private SeekBar leftSB;
    private SeekBar rightSB;
    private SeekBar currTouch;

    private OnRangeChangedListener callback;


    public RangeSeekBar(Context context) {
        this(context, null);
    }

    public RangeSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray t = context.obtainStyledAttributes(attrs, R.styleable.RangeSeekBar);
        cellsCount = t.getInt(R.styleable.RangeSeekBar_cells, 1);
        reserveValue = t.getFloat(R.styleable.RangeSeekBar_reserve, 0);
        mMin = t.getFloat(R.styleable.RangeSeekBar_min, 0);//最小取值
//        //从缓存中拿值
//        float maxValue = SharePreferencesUtils.getFloat(context, "maxValue", 0);
        if (0 == maxValue) {
            mMax = t.getFloat(R.styleable.RangeSeekBar_rangeMax, 100);//最大取值
        } else {
            mMax = maxValue;
        }
        mThumbResId = t.getResourceId(R.styleable.RangeSeekBar_seekBarResId, 0);
        mProgressHintBGId = t.getResourceId(R.styleable.RangeSeekBar_progressHintResId, 0);
        colorLineSelected = t.getColor(R.styleable.RangeSeekBar_lineColorSelected, 0xFF4BD962);
        colorLineEdge = t.getColor(R.styleable.RangeSeekBar_lineColorEdge, 0xFF0000);
        colorPrimary = t.getColor(R.styleable.RangeSeekBar_thumbPrimaryColor, 0);
        colorSecondary = t.getColor(R.styleable.RangeSeekBar_thumbSecondaryColor, 0);
        //从缓存中拿值
        CharSequence tempArray[] = new CharSequence[]{};
        if (tempArray == null || tempArray.length == 0) {
            mTextArray = t.getTextArray(R.styleable.RangeSeekBar_markTextArray);
        } else {
            mTextArray = tempArray;
        }
        mHideProgressHint = t.getBoolean(R.styleable.RangeSeekBar_hideProgressHint, false);
        textPadding = (int) t.getDimension(R.styleable.RangeSeekBar_textPadding, dp2px(context, 20));
        mTextSize = (int) t.getDimension(R.styleable.RangeSeekBar_rangeTextSize, dp2px(context, 12));
        mTextColor = t.getColor(R.styleable.RangeSeekBar_rangeTextColor, ContextCompat.getColor(context, R.color.red_c30d22));
        mRulingColor = t.getColor(R.styleable.RangeSeekBar_rulingColor, ContextCompat.getColor(context, R.color.red_c30d22));
        mHintBGHeight = t.getDimension(R.styleable.RangeSeekBar_hintBGHeight, 0);
        mHintBGWith = t.getDimension(R.styleable.RangeSeekBar_hintBGWith, 0);
        rulingWith = t.getDimension(R.styleable.RangeSeekBar_rulingWith, 0);
        mSeekBarHeight = (int) t.getDimension(R.styleable.RangeSeekBar_seekBarHeight, dp2px(context, 2));
        mHintBGPadding = (int) t.getDimension(R.styleable.RangeSeekBar_hintBGPadding, 0);
        mThumbSize = (int) t.getDimension(R.styleable.RangeSeekBar_thumbSize, dp2px(context, 26));
        mCellMode = t.getInt(R.styleable.RangeSeekBar_cellMode, 0);
        mSeekBarMode = t.getInt(R.styleable.RangeSeekBar_seekBarMode, 2);

        if (mSeekBarMode == 2) {
            leftSB = new SeekBar(-1);
            rightSB = new SeekBar(1);
        } else {
            leftSB = new SeekBar(-1);
        }

        // if you don't set the mHintBGWith or the mHintBGWith < default value, if will use default value
        if (mHintBGWith == 0) {
            DEFAULT_PADDING_LEFT_AND_RIGHT = dp2px(context, 25);
        } else {
            DEFAULT_PADDING_LEFT_AND_RIGHT = Math.max((int) (mHintBGWith / 2 + dp2px(context, 5)), dp2px(context, 25));

        }

        setRules(mMin, mMax, reserveValue, cellsCount);
        initPaint();
        initBitmap();
        t.recycle();

        defaultPaddingTop = mSeekBarHeight / 2;

        mHintBGHeight = mHintBGHeight == 0 ? (mCursorPaint.measureText("国") * 3) : mHintBGHeight;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        heightNeeded = 2 * (lineTop) + mSeekBarHeight;

        /**
         * onMeasure传入的widthMeasureSpec和heightMeasureSpec不是一般的尺寸数值,而是将模式和尺寸组合在一起的数值
         * MeasureSpec.EXACTLY 是精确尺寸
         * MeasureSpec.AT_MOST 是最大尺寸
         * MeasureSpec.UNSPECIFIED 是未指定尺寸
         */
        if (heightMode == MeasureSpec.EXACTLY) {
            heightSize = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
        } else if (heightMode == MeasureSpec.AT_MOST) {
            heightSize = MeasureSpec.makeMeasureSpec(
                    heightSize < heightNeeded ? heightSize : heightNeeded, MeasureSpec.EXACTLY);
        } else {
            heightSize = MeasureSpec.makeMeasureSpec(
                    heightNeeded, MeasureSpec.EXACTLY);
        }

        super.onMeasure(widthMeasureSpec, heightSize);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        //计算进度条的位置,并根据它初始化两个按钮的位置
        // Calculates the position of the progress bar and initializes the positions of
        // the two buttons based on it

        lineLeft = DEFAULT_PADDING_LEFT_AND_RIGHT + getPaddingLeft();
        lineRight = w - lineLeft - getPaddingRight();
        lineTop = (int) mHintBGHeight + mThumbSize / 2 - mSeekBarHeight / 2 + 30;
        lineBottom = lineTop + mSeekBarHeight;
        lineWidth = lineRight - lineLeft;
        line.set(lineLeft, lineTop, lineRight, lineBottom);
        lineCorners = (int) ((lineBottom - lineTop) * 0.45f);

        leftSB.onSizeChanged(lineLeft, lineBottom, mThumbSize, lineWidth, cellsCount > 1, mThumbResId, getContext());
        if (mSeekBarMode == 2) {
            rightSB.onSizeChanged(lineLeft, lineBottom, mThumbSize, lineWidth, cellsCount > 1, mThumbResId, getContext());
        }
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //绘制进度条
        // draw the progress bar
        mMainPaint.setColor(colorLineEdge);
        canvas.drawRoundRect(line, lineCorners, lineCorners, mMainPaint);
//        mMainPaint.setColor(colorLineSelected);
//        if (mSeekBarMode == 2) {
//            canvas.drawRect(leftSB.left + leftSB.widthSize / 2 + leftSB.lineWidth * leftSB.currPercent, lineTop,
//                    rightSB.left + rightSB.widthSize / 2 + rightSB.lineWidth * rightSB.currPercent, lineBottom, mMainPaint);
//        } else {
//            canvas.drawRect(leftSB.left + leftSB.widthSize / 2, lineTop,
//                    leftSB.left + leftSB.widthSize / 2 + leftSB.lineWidth * leftSB.currPercent, lineBottom, mMainPaint);
//        }
        leftSB.draw(canvas);
        if (mSeekBarMode == 2) {
            rightSB.draw(canvas);
        }

        //绘制刻度,并且根据当前位置是否在刻度范围内设置不同的颜色显示
        // Draw the scales, and according to the current position is set within
        // the scale range of different color display

        if (mTextArray != null) {
            mPartLength = lineWidth / (mTextArray.length - 1);
            for (int i = 0; i < mTextArray.length; i++) {
                final String text2Draw = mTextArray[i].toString();

                float x;
                //平分显示
                if (mCellMode == 1) {
                    mCursorPaint.setColor(mTextColor);
                    mCursorPaint.setAntiAlias(true);
                    x = lineLeft + i * mPartLength - mCursorPaint.measureText(text2Draw) / 2;
                } else {
                    float num = Float.parseFloat(text2Draw);
                    float[] result = getCurrentRange();
                    if (compareFloat(num, result[0]) != -1 && compareFloat(num, result[1]) != 1 && mSeekBarMode == 2) {
                        mCursorPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorAccent));
                    } else {
                        mCursorPaint.setColor(mTextColor);
                        mCursorPaint.setAntiAlias(true);
                    }
                    //按实际比例显示
                    x = lineLeft + lineWidth * (num - mMin) / (mMax - mMin)
                            - mCursorPaint.measureText(text2Draw) / 2;
                }
//                float y = lineTop - textPadding;
                float y = lineBottom + 45;

                canvas.drawText(text2Draw, x, y, mCursorPaint);
                //绘制白线
//                mCursorPaint.reset();
                Paint mPaint = new Paint();
                mPaint.setColor(mRulingColor);
                mPaint.setAntiAlias(true);
                mPaint.setStrokeWidth(rulingWith);
                mPaint.setStyle(Paint.Style.FILL);
//                left = x - widthSize / 2;
//                canvas.drawLine(x,lineRight,x,lineRight + 5,mPaint);
                if(i != 0 && i != 9){
                    canvas.drawLine(x + mCursorPaint.measureText(text2Draw) / 2,lineTop + mSeekBarHeight,x + mCursorPaint.measureText(text2Draw) / 2,lineTop + mSeekBarHeight*1/3,mPaint);
                    canvas.drawLine(x + mPartLength/2 + mCursorPaint.measureText(text2Draw) / 2,lineTop + mSeekBarHeight,x + mPartLength/2 + mCursorPaint.measureText(text2Draw) / 2,lineTop + mSeekBarHeight*2/3,mPaint);
                }else if(i == 0){
                    canvas.drawLine(x + mPartLength/2 + mCursorPaint.measureText(text2Draw) / 2,lineTop + mSeekBarHeight,x + mPartLength/2 + mCursorPaint.measureText(text2Draw) / 2,lineTop + mSeekBarHeight*2/3,mPaint);
                }
            }
        }

    }

    /**
     * 初始化画笔
     * init the paints
     */
    private void initPaint() {

        mMainPaint.setStyle(Paint.Style.FILL);
        mMainPaint.setColor(colorLineEdge);

        mCursorPaint.setStyle(Paint.Style.FILL);
        mCursorPaint.setColor(colorLineEdge);
        mCursorPaint.setTextSize(mTextSize);

        mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mProgressPaint.setTypeface(Typeface.DEFAULT);
        mProgressPaint.setColor(0xFF0000);

        //计算文字的高度
        //Calculate the height of the text
        Paint.FontMetrics fm = mCursorPaint.getFontMetrics();
        mCursorTextHeight = (int) (Math.ceil(fm.descent - fm.ascent) + 2);


    }


    /**
     * 初始化进度提示的背景
     */
    private void initBitmap() {
        if (mProgressHintBGId != 0) {
            mProgressHintBG = BitmapFactory.decodeResource(getResources(), mProgressHintBGId);
        } else {
            mProgressHintBG = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
        }

    }

    //*********************************** SeekBar ***********************************//

    private class SeekBar {

        private int lineWidth;
        private int widthSize, heightSize;
        private int left, right, top, bottom;
        private float currPercent;
        private float material = 0;
        public boolean isShowingHint = true;
        private boolean isLeft;
        private Bitmap bmp;
        private ValueAnimator anim;
        private RadialGradient shadowGradient;
        private Paint defaultPaint;
        private String mHintText2Draw;
        private Boolean isPrimary = true;


        public SeekBar(int position) {
            if (position < 0) {
                isLeft = true;
            } else {
                isLeft = false;
            }
        }

        /**
         * 计算每个按钮的位置和尺寸
         * Calculates the position and size of each button
         *
         * @param x
         * @param y
         * @param hSize
         * @param parentLineWidth
         * @param cellsMode
         * @param bmpResId
         * @param context
         */
        protected void onSizeChanged(int x, int y, int hSize, int parentLineWidth, boolean cellsMode, int bmpResId, Context context) {
            heightSize = hSize;
            widthSize = heightSize;
            left = x - widthSize / 2;
            right = x + widthSize / 2;
            top = y - heightSize / 2;
            bottom = y + heightSize / 10;

            if (cellsMode) {
                lineWidth = parentLineWidth;
            } else {
                lineWidth = parentLineWidth;
            }

            if (bmpResId > 0) {
                Bitmap original = BitmapFactory.decodeResource(context.getResources(), bmpResId);
                if (original != null) {
                    Matrix matrix = new Matrix();
                    float scaleHeight = mThumbSize * 1.0f / original.getHeight();
                    float scaleWidth = scaleHeight;
                    matrix.postScale(scaleWidth, scaleHeight);
                    bmp = Bitmap.createBitmap(original, 0, 0, original.getWidth(), original.getHeight(), matrix, true);
                }

            } else {
                defaultPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
                int radius = (int) (widthSize * DEFAULT_RADIUS);
                int barShadowRadius = (int) (radius * 0.95f);
                int mShadowCenterX = widthSize / 2;
                int mShadowCenterY = heightSize / 2;
                shadowGradient = new RadialGradient(mShadowCenterX, mShadowCenterY, barShadowRadius, Color.BLACK, Color.TRANSPARENT, Shader.TileMode.CLAMP);
            }
        }

        /**
         * 绘制按钮和提示背景和文字
         * Draw buttons and tips for background and text
         *
         * @param canvas
         */
        protected void draw(Canvas canvas) {
            int offset = (int) (lineWidth * currPercent);
            canvas.save();
            canvas.translate(offset, 0);
            String text2Draw = "";
            int hintW = 0, hintH = 0;
            float[] result = getCurrentRange();

            if (mHideProgressHint) {
                isShowingHint = true;
            } else {
                if (isLeft) {
                    if (mHintText2Draw == null) {
                        BigDecimal b = new BigDecimal(result[0]);
                        double f1 = b.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
                        String[] content = (f1 + "").split("\\.");
                        if(Integer.parseInt(content[1]) >= 5){
                            text2Draw = content[0] + ".5";
                        }else{
                            text2Draw = content[0] + ".0";
                        }
                    } else {
                        text2Draw = mHintText2Draw;
                    }

                    // if is the start,change the thumb color
                    isPrimary = (compareFloat(result[0], mMin) == 0);

                } else {
                    if (mHintText2Draw == null) {
                        BigDecimal b = new BigDecimal(result[1]);
                        double f1 = b.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
                        String[] content = (f1 + "").split("\\.");
                        if(Integer.parseInt(content[1]) >= 5){
                            text2Draw = content[0] + ".5";
                        }else{
                            text2Draw = content[0] + ".0";
                        }
                    } else {
                        text2Draw = mHintText2Draw;
                    }

                    isPrimary = (compareFloat(result[1], mMax) == 0);
                }

                hintH = (int) mHintBGHeight - 20;//设置提示背景的高度
                hintW = (int) (mHintBGWith == 0 ? (mCursorPaint.measureText(text2Draw) + DEFAULT_PADDING_LEFT_AND_RIGHT)
                        : mHintBGWith);

                if (hintW < 1.5f * hintH) hintW = (int) (1.5f * hintH);
            }


            if (bmp != null) {
                canvas.drawBitmap(bmp, left, lineTop - bmp.getHeight()-12, null);
//                canvas.drawBitmap(bmp, left, 30, null);
                if (isShowingHint) {
                    Rect rect = new Rect();
                    rect.left = left - (hintW / 2 - bmp.getWidth() / 2);
                    rect.top = bottom - hintH - bmp.getHeight();
                    rect.right = rect.left + hintW;
                    rect.bottom = rect.top + hintH - 10;
//                    drawNinePath(canvas, mProgressHintBG, rect);.
                    mCursorPaint.setColor(Color.BLACK);//提示字的颜色
                    mCursorPaint.setTextSize(mTextSize);
                    int x = (int) (left + (bmp.getWidth() / 2) - mCursorPaint.measureText(text2Draw) / 2);
                    int y = bottom - hintH - bmp.getHeight() + hintH / 2;
                    canvas.drawText(text2Draw, x, lineTop - bmp.getHeight()-35, mCursorPaint);

                    mCursorPaint.getTextBounds(text2Draw, 0, text2Draw.length(), rect);
                    Paint mPaint = new Paint();
                    mPaint.setColor(Color.BLACK);
                    mPaint.setAntiAlias(true);
                    mPaint.setStrokeWidth(1f);
                    mPaint.setStyle(Paint.Style.STROKE);
                    int w = rect.width();//文字宽度
                    int h = rect.height();//文字宽度
                    int paddW = 45;//内边距
                    int paddH = 30;
                    int txtW = x + w/2;
                    int txtH = lineTop - bmp.getHeight()-35 - h/2;
                    canvas.drawRect(txtW - paddW, txtH - paddH,txtW + paddW, txtH + paddH,mPaint);
                }
            } else {
                canvas.translate(left, 0);
                if (isShowingHint) {
                    Rect rect = new Rect();
                    rect.left = widthSize / 2 - hintW / 2;
                    rect.top = defaultPaddingTop;
                    rect.right = rect.left + hintW;
                    rect.bottom = rect.top + hintH - 10;
//                    drawNinePath(canvas, mProgressHintBG, rect);

                    mCursorPaint.setColor(Color.BLACK);
                    mCursorPaint.setTextSize(mTextSize);

                    int x = (int) (widthSize / 2 - mCursorPaint.measureText(text2Draw) / 2);

                    // TODO: 2017/2/6
                    //这里和背景形状有关,暂时根据本图形状比例计算
                    //Here and the background shape, temporarily based on the shape of this figure ratio calculation
                    int y = hintH / 3 + defaultPaddingTop + mCursorTextHeight / 2;

                    canvas.drawText(text2Draw, x, y, mCursorPaint);
                }
                drawDefault(canvas);
            }

            canvas.restore();
        }

        /**
         * 绘制 9Path
         *
         * @param c
         * @param bmp
         * @param rect
         */
        public void drawNinePath(Canvas c, Bitmap bmp, Rect rect) {
            NinePatch patch = new NinePatch(bmp, bmp.getNinePatchChunk(), null);
            patch.draw(c, rect);
        }

        /**
         * 如果没有图片资源,则绘制默认按钮
         * 

* If there is no image resource, draw the default button * * @param canvas */ private void drawDefault(Canvas canvas) { int centerX = widthSize / 2; int centerY = lineBottom - mSeekBarHeight / 2; int radius = (int) (widthSize * DEFAULT_RADIUS); // draw shadow defaultPaint.setStyle(Paint.Style.FILL); canvas.save(); canvas.translate(0, radius * 0.25f); canvas.scale(1 + (0.1f * material), 1 + (0.1f * material), centerX, centerY); defaultPaint.setShader(shadowGradient); canvas.drawCircle(centerX, centerY, radius, defaultPaint); defaultPaint.setShader(null); canvas.restore(); // draw body defaultPaint.setStyle(Paint.Style.FILL); if (isPrimary) { //if not set the color,it will use default color if (colorPrimary == 0) { defaultPaint.setColor(te.evaluate(material, 0xFFFFFFFF, 0xFFE7E7E7)); } else { defaultPaint.setColor(colorPrimary); } } else { if (colorSecondary == 0) { defaultPaint.setColor(te.evaluate(material, 0xFFFFFFFF, 0xFF0000)); } else { defaultPaint.setColor(colorSecondary); } } canvas.drawCircle(centerX, centerY, radius, defaultPaint); // draw border defaultPaint.setStyle(Paint.Style.STROKE); defaultPaint.setColor(0xFFD7D7D7); canvas.drawCircle(centerX, centerY, radius, defaultPaint); } final TypeEvaluator te = new TypeEvaluator() { @Override public Integer evaluate(float fraction, Integer startValue, Integer endValue) { int alpha = (int) (Color.alpha(startValue) + fraction * (Color.alpha(endValue) - Color.alpha(startValue))); int red = (int) (Color.red(startValue) + fraction * (Color.red(endValue) - Color.red(startValue))); int green = (int) (Color.green(startValue) + fraction * (Color.green(endValue) - Color.green(startValue))); int blue = (int) (Color.blue(startValue) + fraction * (Color.blue(endValue) - Color.blue(startValue))); return Color.argb(alpha, red, green, blue); } }; /** * 拖动检测 * * @param event * @return */ protected boolean collide(MotionEvent event) { float x = event.getX(); float y = event.getY(); int offset = (int) (lineWidth * currPercent); // return x > left + offset && x < right + offset && y > top && y < bottom; return x > left + offset && x < right + offset;// && y > top && y < bottom; } private void slide(float percent) { if (percent < 0) percent = 0; else if (percent > 1) percent = 1; currPercent = percent; } private void materialRestore() { if (anim != null) anim.cancel(); anim = ValueAnimator.ofFloat(material, 0); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { material = (float) animation.getAnimatedValue(); invalidate(); } }); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { material = 0; invalidate(); } }); anim.start(); } public void setProgressHint(String hint) { mHintText2Draw = hint; } } //*********************************** SeekBar ***********************************// public interface OnRangeChangedListener { void onRangeChanged(RangeSeekBar view, float min, float max, boolean isFromUser); } public void setOnRangeChangedListener(OnRangeChangedListener listener) { callback = listener; } public void setValue(float min, float max) { min = min + offsetValue; max = max + offsetValue; if (min < minValue) { throw new IllegalArgumentException("setValue() min < (preset min - offsetValue) . #min:" + min + " #preset min:" + minValue + " #offsetValue:" + offsetValue); } if (max > maxValue) { throw new IllegalArgumentException("setValue() max > (preset max - offsetValue) . #max:" + max + " #preset max:" + maxValue + " #offsetValue:" + offsetValue); } if (reserveCount > 1) { if ((min - minValue) % reserveCount != 0) { throw new IllegalArgumentException("setValue() (min - preset min) % reserveCount != 0 . #min:" + min + " #preset min:" + minValue + "#reserveCount:" + reserveCount + "#reserve:" + reserveValue); } if ((max - minValue) % reserveCount != 0) { throw new IllegalArgumentException("setValue() (max - preset min) % reserveCount != 0 . #max:" + max + " #preset min:" + minValue + "#reserveCount:" + reserveCount + "#reserve:" + reserveValue); } leftSB.currPercent = (min - minValue) / reserveCount * cellsPercent; if (mSeekBarMode == 2) { rightSB.currPercent = (max - minValue) / reserveCount * cellsPercent; } } else { leftSB.currPercent = (min - minValue) / (maxValue - minValue); if (mSeekBarMode == 2) { rightSB.currPercent = (max - minValue) / (maxValue - minValue); } } if (callback != null) { if (mSeekBarMode == 2) { callback.onRangeChanged(this, leftSB.currPercent, rightSB.currPercent, false); } else { callback.onRangeChanged(this, leftSB.currPercent, leftSB.currPercent, false); } } invalidate(); } public void setValue(float value) { setValue(value, mMax); } public void setRange(float min, float max) { setRules(min, max, reserveCount, cellsCount); } public void setRules(float min, float max, float reserve, int cells) { if (max <= min) { throw new IllegalArgumentException("setRules() max must be greater than min ! #max:" + max + " #min:" + min); } mMax = max; mMin = min; if (min < 0) { offsetValue = 0 - min; min = min + offsetValue; max = max + offsetValue; } minValue = min; maxValue = max; if (reserve < 0) { throw new IllegalArgumentException("setRules() reserve must be greater than zero ! #reserve:" + reserve); } if (reserve >= max - min) { throw new IllegalArgumentException("setRules() reserve must be less than (max - min) ! #reserve:" + reserve + " #max - min:" + (max - min)); } if (cells < 1) { throw new IllegalArgumentException("setRules() cells must be greater than 1 ! #cells:" + cells); } cellsCount = cells; cellsPercent = 1f / cellsCount; reserveValue = reserve; reservePercent = reserve / (max - min); reserveCount = (int) (reservePercent / cellsPercent + (reservePercent % cellsPercent != 0 ? 1 : 0)); if (cellsCount > 1) { if (mSeekBarMode == 2) { if (leftSB.currPercent + cellsPercent * reserveCount <= 1 && leftSB.currPercent + cellsPercent * reserveCount > rightSB.currPercent) { rightSB.currPercent = leftSB.currPercent + cellsPercent * reserveCount; } else if (rightSB.currPercent - cellsPercent * reserveCount >= 0 && rightSB.currPercent - cellsPercent * reserveCount < leftSB.currPercent) { leftSB.currPercent = rightSB.currPercent - cellsPercent * reserveCount; } } else { if (1 - cellsPercent * reserveCount >= 0 && 1 - cellsPercent * reserveCount < leftSB.currPercent) { leftSB.currPercent = 1 - cellsPercent * reserveCount; } } } else { if (mSeekBarMode == 2) { if (leftSB.currPercent + reservePercent <= 1 && leftSB.currPercent + reservePercent > rightSB.currPercent) { rightSB.currPercent = leftSB.currPercent + reservePercent; } else if (rightSB.currPercent - reservePercent >= 0 && rightSB.currPercent - reservePercent < leftSB.currPercent) { leftSB.currPercent = rightSB.currPercent - reservePercent; } } else { if (1 - reservePercent >= 0 && 1 - reservePercent < leftSB.currPercent) { leftSB.currPercent = 1 - reservePercent; } } } invalidate(); } public float getMax() { return mMax; } public float getMin() { return mMin; } public float[] getCurrentRange() { float range = maxValue - minValue; if (mSeekBarMode == 2) { return new float[]{-offsetValue + minValue + range * leftSB.currPercent, -offsetValue + minValue + range * rightSB.currPercent}; } else { return new float[]{-offsetValue + minValue + range * leftSB.currPercent, -offsetValue + minValue + range * 1.0f}; } } @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); this.isEnable = enabled; } public void setProgressDescription(String progress) { if (leftSB != null) { leftSB.setProgressHint(progress); } if (rightSB != null) { rightSB.setProgressHint(progress); } } public void setLeftProgressDescription(String progress) { if (leftSB != null) { leftSB.setProgressHint(progress); } } public void setRightProgressDescription(String progress) { if (rightSB != null) { rightSB.setProgressHint(progress); } } @Override public boolean onTouchEvent(MotionEvent event) { if (!isEnable) return true; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: boolean touchResult = false; if (rightSB != null && rightSB.currPercent >= 1 && leftSB.collide(event)) { currTouch = leftSB; touchResult = true; } else if (rightSB != null && rightSB.collide(event)) { currTouch = rightSB; touchResult = true; } else if (leftSB.collide(event)) { currTouch = leftSB; touchResult = true; } //Intercept parent TouchEvent if (getParent() != null) { getParent().requestDisallowInterceptTouchEvent(true); } return touchResult; case MotionEvent.ACTION_MOVE: float percent; float x = event.getX(); currTouch.material = currTouch.material >= 1 ? 1 : currTouch.material + 0.1f; if (currTouch == leftSB) { if (cellsCount > 1) { if (x < lineLeft) { percent = 0; } else { percent = (x - lineLeft) * 1f / (lineWidth); } int touchLeftCellsValue = Math.round(percent / cellsPercent); int currRightCellsValue; if (mSeekBarMode == 2) { currRightCellsValue = Math.round(rightSB.currPercent / cellsPercent); } else { currRightCellsValue = Math.round(1.0f / cellsPercent); } percent = touchLeftCellsValue * cellsPercent; while (touchLeftCellsValue > currRightCellsValue - reserveCount) { touchLeftCellsValue--; if (touchLeftCellsValue < 0) break; percent = touchLeftCellsValue * cellsPercent; } } else { if (x < lineLeft) { percent = 0; } else { percent = (x - lineLeft) * 1f / (lineWidth); } if (mSeekBarMode == 2) { if (percent > rightSB.currPercent - reservePercent) { percent = rightSB.currPercent - reservePercent; } } else { if (percent > 1.0f - reservePercent) { percent = 1.0f - reservePercent; } } } leftSB.slide(percent); leftSB.isShowingHint = true; //Intercept parent TouchEvent if (getParent() != null) { getParent().requestDisallowInterceptTouchEvent(true); } } else if (currTouch == rightSB) { if (cellsCount > 1) { if (x > lineRight) { percent = 1; } else { percent = (x - lineLeft) * 1f / (lineWidth); } int touchRightCellsValue = Math.round(percent / cellsPercent); int currLeftCellsValue = Math.round(leftSB.currPercent / cellsPercent); percent = touchRightCellsValue * cellsPercent; while (touchRightCellsValue < currLeftCellsValue + reserveCount) { touchRightCellsValue++; if (touchRightCellsValue > maxValue - minValue) break; percent = touchRightCellsValue * cellsPercent; } } else { if (x > lineRight) { percent = 1; } else { percent = (x - lineLeft) * 1f / (lineWidth); } if (percent < leftSB.currPercent + reservePercent) { percent = leftSB.currPercent + reservePercent; } } rightSB.slide(percent); rightSB.isShowingHint = true; } if (callback != null) { float[] result = getCurrentRange(); callback.onRangeChanged(this, result[0], result[1], true); } invalidate(); //Intercept parent TouchEvent if (getParent() != null) { getParent().requestDisallowInterceptTouchEvent(true); } break; case MotionEvent.ACTION_CANCEL: if (mSeekBarMode == 2) { rightSB.isShowingHint = true; } leftSB.isShowingHint = true; if (callback != null) { float[] result = getCurrentRange(); callback.onRangeChanged(this, result[0], result[1], false); } //Intercept parent TouchEvent if (getParent() != null) { getParent().requestDisallowInterceptTouchEvent(true); } break; case MotionEvent.ACTION_UP: if (mSeekBarMode == 2) { rightSB.isShowingHint = true; } leftSB.isShowingHint = true; currTouch.materialRestore(); if (callback != null) { float[] result = getCurrentRange(); callback.onRangeChanged(this, result[0], result[1], false); } //Intercept parent TouchEvent if (getParent() != null) { getParent().requestDisallowInterceptTouchEvent(true); } break; } return super.onTouchEvent(event); } @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.minValue = minValue - offsetValue; ss.maxValue = maxValue - offsetValue; ss.reserveValue = reserveValue; ss.cellsCount = cellsCount; float[] results = getCurrentRange(); ss.currSelectedMin = results[0]; ss.currSelectedMax = results[1]; return ss; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); float min = ss.minValue; float max = ss.maxValue; float reserve = ss.reserveValue; int cells = ss.cellsCount; setRules(min, max, reserve, cells); float currSelectedMin = ss.currSelectedMin; float currSelectedMax = ss.currSelectedMax; setValue(currSelectedMin, currSelectedMax); } private class SavedState extends BaseSavedState { private float minValue; private float maxValue; private float reserveValue; private int cellsCount; private float currSelectedMin; private float currSelectedMax; SavedState(Parcelable superState) { super(superState); } private SavedState(Parcel in) { super(in); minValue = in.readFloat(); maxValue = in.readFloat(); reserveValue = in.readFloat(); cellsCount = in.readInt(); currSelectedMin = in.readFloat(); currSelectedMax = in.readFloat(); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeFloat(minValue); out.writeFloat(maxValue); out.writeFloat(reserveValue); out.writeInt(cellsCount); out.writeFloat(currSelectedMin); out.writeFloat(currSelectedMax); } } /** * 根据手机的分辨率从 dp 的单位 转成为 px(像素) */ private int dp2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } /** * Compare the size of two floating point numbers * * @param a * @param b * @return 1 is a > b * -1 is a < b * 0 is a == b */ private int compareFloat(float a, float b) { int ta = Math.round(a * 1000); int tb = Math.round(b * 1000); if (ta > tb) { return 1; } else if (ta < tb) { return -1; } else { return 0; } } }


package com.example.ilaw66lawyer.ui.activitys;

import android.util.Log;

import com.example.ilaw66lawyer.R;
import com.example.ilaw66lawyer.base.BaseActivity;
import com.example.ilaw66lawyer.ui.view.RangeSeekBar;

import java.math.BigDecimal;

public class TestAcvitity extends BaseActivity {
    private RangeSeekBar mRangeSeekBar;

    @Override
    protected int initPageLayoutID() {
        return R.layout.activity_test;
    }

    @Override
    protected void initBaseWidget() {
        setIsShowTitleBar(false);
        setIsShowAnimation(false);
    }

    @Override
    protected void initWidget() {
        RangeSeekBar mRangeSeekBar = (RangeSeekBar)findViewById(R.id.mRangeSeekBar);
        mRangeSeekBar.setOnRangeChangedListener(new RangeSeekBar.OnRangeChangedListener() {
            @Override
            public void onRangeChanged(RangeSeekBar view, float min, float max, boolean isFromUser) {
                //四舍五入保留一位小数
                BigDecimal b = new BigDecimal(min);
                double f1 = b.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
                String[] content = (f1 + "").split("\\.");
                //其实每一次拖动都会变化值,只是过滤掉了一些更精确的值,只保留了0.5的整数倍的值
                if(Integer.parseInt(content[1]) >= 5){
                    Log.e("TestAcvitity",content[0] + ".5");
                }else{
                    Log.e("TestAcvitity",content[0] + ".0");
                }
            }
        });
    }

}
 

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

    

    
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
            
            
        
        
            
            
        
        
        
        
        
    



        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
    

基于http://blog.csdn.net/u013836857/article/details/70308577这篇博客的源码上修改成的。

请看效果图

自定义带刻度的SeekBar_第1张图片

你可能感兴趣的:(Android自定义控件)