Android——自定义带刻度的SeekBar单向拖动条 原创 2017年04月21日 11:29:53 标签:android /listview /app /seekbar 1302 时间过得真快,

转载 2017年10月25日 11:03:05

Android——自定义带刻度的SeekBar单向拖动条

原创 2017年04月21日 11:29:53

时间过得真快,才发现好久没来逛逛了。没写博客的这段时间一直在做项目,连续完成了两个大型app,这个过程很享受,这是独立开发的,所以中途有很多很多的问题需要自己一个一个的去解决,现在接近尾声了,发现自己在这个阶段成长了不少,当然需要学习的知识还有很多很多,就让我们大家一起学习吧!

今天就分享一个自己在项目中,客户要求的功能,拖动条设置ListView列表中item的金额。这边主要的就是说seekbar这个东西,那我们开始吧!

看下效果: 
这里写图片描述

大概就是这样,上面的刻度值是可以动态设置的,下面详细说一下,先看下自定义的代码块:

package com.ds.platform.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.ds.platform.R;
import com.ds.platform.utils.SharePreferencesUtils;

/**
 * @author: Allen
 * @date: 2017/3/13
 * @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;

    //进度提示背景与按钮之间的距离
    //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;

    //未选则的进度条颜色
    // 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_max, 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[] = SharePreferencesUtils.getStringSet(context, "textArray", null);
        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_textSize, dp2px(context, 12));
        mTextColor = t.getColor(R.styleable.RangeSeekBar_textColor, ContextCompat.getColor(context, R.color.main_text));
        mHintBGHeight = t.getDimension(R.styleable.RangeSeekBar_hintBGHeight, 0);
        mHintBGWith = t.getDimension(R.styleable.RangeSeekBar_hintBGWith, 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 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;

                canvas.drawText(text2Draw, x, y, mCursorPaint);
            }
        }
        //绘制进度条
        // 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);
        }
    }

    /**
     * 初始化画笔
     * 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.drawable.progress_hint_bg);

        }

    }

    //*********************************** 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;
        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 = false;
            } else {
                if (isLeft) {
                    if (mHintText2Draw == null) {
                        text2Draw = (int) result[0] + "";
                    } else {
                        text2Draw = mHintText2Draw;
                    }

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

                } else {
                    if (mHintText2Draw == null) {
                        text2Draw = (int) result[1] + "";
                    } 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() / 2, 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.WHITE);//提示字的颜色
                    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, y, mCursorPaint);
                }
            } 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.WHITE);
                    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);
        }

        /**
         * 如果没有图片资源,则绘制默认按钮
         * <p>
         * 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<Integer> te = new TypeEvaluator<Integer>() {
            @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;
        }

        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 = false;
                }
                leftSB.isShowingHint = false;

                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 = false;
                }
                leftSB.isShowingHint = false;
                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;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
  • 477
  • 478
  • 479
  • 480
  • 481
  • 482
  • 483
  • 484
  • 485
  • 486
  • 487
  • 488
  • 489
  • 490
  • 491
  • 492
  • 493
  • 494
  • 495
  • 496
  • 497
  • 498
  • 499
  • 500
  • 501
  • 502
  • 503
  • 504
  • 505
  • 506
  • 507
  • 508
  • 509
  • 510
  • 511
  • 512
  • 513
  • 514
  • 515
  • 516
  • 517
  • 518
  • 519
  • 520
  • 521
  • 522
  • 523
  • 524
  • 525
  • 526
  • 527
  • 528
  • 529
  • 530
  • 531
  • 532
  • 533
  • 534
  • 535
  • 536
  • 537
  • 538
  • 539
  • 540
  • 541
  • 542
  • 543
  • 544
  • 545
  • 546
  • 547
  • 548
  • 549
  • 550
  • 551
  • 552
  • 553
  • 554
  • 555
  • 556
  • 557
  • 558
  • 559
  • 560
  • 561
  • 562
  • 563
  • 564
  • 565
  • 566
  • 567
  • 568
  • 569
  • 570
  • 571
  • 572
  • 573
  • 574
  • 575
  • 576
  • 577
  • 578
  • 579
  • 580
  • 581
  • 582
  • 583
  • 584
  • 585
  • 586
  • 587
  • 588
  • 589
  • 590
  • 591
  • 592
  • 593
  • 594
  • 595
  • 596
  • 597
  • 598
  • 599
  • 600
  • 601
  • 602
  • 603
  • 604
  • 605
  • 606
  • 607
  • 608
  • 609
  • 610
  • 611
  • 612
  • 613
  • 614
  • 615
  • 616
  • 617
  • 618
  • 619
  • 620
  • 621
  • 622
  • 623
  • 624
  • 625
  • 626
  • 627
  • 628
  • 629
  • 630
  • 631
  • 632
  • 633
  • 634
  • 635
  • 636
  • 637
  • 638
  • 639
  • 640
  • 641
  • 642
  • 643
  • 644
  • 645
  • 646
  • 647
  • 648
  • 649
  • 650
  • 651
  • 652
  • 653
  • 654
  • 655
  • 656
  • 657
  • 658
  • 659
  • 660
  • 661
  • 662
  • 663
  • 664
  • 665
  • 666
  • 667
  • 668
  • 669
  • 670
  • 671
  • 672
  • 673
  • 674
  • 675
  • 676
  • 677
  • 678
  • 679
  • 680
  • 681
  • 682
  • 683
  • 684
  • 685
  • 686
  • 687
  • 688
  • 689
  • 690
  • 691
  • 692
  • 693
  • 694
  • 695
  • 696
  • 697
  • 698
  • 699
  • 700
  • 701
  • 702
  • 703
  • 704
  • 705
  • 706
  • 707
  • 708
  • 709
  • 710
  • 711
  • 712
  • 713
  • 714
  • 715
  • 716
  • 717
  • 718
  • 719
  • 720
  • 721
  • 722
  • 723
  • 724
  • 725
  • 726
  • 727
  • 728
  • 729
  • 730
  • 731
  • 732
  • 733
  • 734
  • 735
  • 736
  • 737
  • 738
  • 739
  • 740
  • 741
  • 742
  • 743
  • 744
  • 745
  • 746
  • 747
  • 748
  • 749
  • 750
  • 751
  • 752
  • 753
  • 754
  • 755
  • 756
  • 757
  • 758
  • 759
  • 760
  • 761
  • 762
  • 763
  • 764
  • 765
  • 766
  • 767
  • 768
  • 769
  • 770
  • 771
  • 772
  • 773
  • 774
  • 775
  • 776
  • 777
  • 778
  • 779
  • 780
  • 781
  • 782
  • 783
  • 784
  • 785
  • 786
  • 787
  • 788
  • 789
  • 790
  • 791
  • 792
  • 793
  • 794
  • 795
  • 796
  • 797
  • 798
  • 799
  • 800
  • 801
  • 802
  • 803
  • 804
  • 805
  • 806
  • 807
  • 808
  • 809
  • 810
  • 811
  • 812
  • 813
  • 814
  • 815
  • 816
  • 817
  • 818
  • 819
  • 820
  • 821
  • 822
  • 823
  • 824
  • 825
  • 826
  • 827
  • 828
  • 829
  • 830
  • 831
  • 832
  • 833
  • 834
  • 835
  • 836
  • 837
  • 838
  • 839
  • 840
  • 841
  • 842
  • 843
  • 844
  • 845
  • 846
  • 847
  • 848
  • 849
  • 850
  • 851
  • 852
  • 853
  • 854
  • 855
  • 856
  • 857
  • 858
  • 859
  • 860
  • 861
  • 862
  • 863
  • 864
  • 865
  • 866
  • 867
  • 868
  • 869
  • 870
  • 871
  • 872
  • 873
  • 874
  • 875
  • 876
  • 877
  • 878
  • 879
  • 880
  • 881
  • 882
  • 883
  • 884
  • 885
  • 886
  • 887
  • 888
  • 889
  • 890
  • 891
  • 892
  • 893
  • 894
  • 895
  • 896
  • 897
  • 898
  • 899
  • 900
  • 901
  • 902
  • 903
  • 904
  • 905
  • 906
  • 907
  • 908
  • 909
  • 910
  • 911
  • 912
  • 913
  • 914
  • 915
  • 916
  • 917
  • 918
  • 919
  • 920
  • 921
  • 922
  • 923
  • 924
  • 925
  • 926
  • 927
  • 928
  • 929
  • 930
  • 931
  • 932
  • 933
  • 934
  • 935
  • 936
  • 937
  • 938
  • 939
  • 940
  • 941
  • 942
  • 943
  • 944
  • 945
  • 946
  • 947
  • 948
  • 949
  • 950
  • 951
  • 952
  • 953
  • 954
  • 955
  • 956
  • 957
  • 958
  • 959
  • 960
  • 961
  • 962
  • 963
  • 964
  • 965
  • 966
  • 967
  • 968
  • 969
  • 970
  • 971
  • 972
  • 973
  • 974
  • 975
  • 976
  • 977
  • 978
  • 979
  • 980
  • 981
  • 982
  • 983
  • 984
  • 985
  • 986
  • 987
  • 988
  • 989
  • 990
  • 991
  • 992
  • 993
  • 994
  • 995
  • 996
  • 997
  • 998
  • 999
  • 1000
  • 1001
  • 1002
  • 1003
  • 1004
  • 1005
  • 1006
  • 1007
  • 1008
  • 1009
  • 1010
  • 1011
  • 1012
  • 1013
  • 1014
  • 1015
  • 1016
  • 1017
  • 1018
  • 1019
  • 1020
  • 1021
  • 1022
  • 1023
  • 1024
  • 1025
  • 1026
  • 1027
  • 1028
  • 1029
  • 1030
  • 1031
  • 1032
  • 1033
  • 1034
  • 1035
  • 1036
  • 1037
  • 1038
  • 1039

取拖动条的最大值代码块:

//从缓存中拿值
        float maxValue = SharePreferencesUtils.getFloat(context, "maxValue", 0);
        if (0 == maxValue) {
            mMax = t.getFloat(R.styleable.RangeSeekBar_max, 100);//最大取值
        } else {
            mMax = maxValue;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
这边的maxValue 值是从缓存中取出来的,如果在缓存中这个key(maxValue)为空的话,默认值为0,为0的时候从本地的配置文件arrays.xml中拿取默认的最大值(100),该文件放置在values目录下面,代码如下:
  <?xml version="1.0" encoding="utf-8"?>
<resources>

    <string-array name="markArray">
        <item>0</item>
        <item>10</item>
        <item>20</item>
        <item>30</item>
        <item>50</item>
        <item>100</item>
    </string-array>

</resources>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

还有一段就是获取刻度的array。

//从缓存中拿值
        CharSequence tempArray[] = SharePreferencesUtils.getStringSet(context, "textArray", null);
        if (tempArray == null || tempArray.length == 0) {
            mTextArray = t.getTextArray(R.styleable.RangeSeekBar_markTextArray);
        } else {
            mTextArray = tempArray;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

一样的 存储方式,放在SharePreferences内存当中,默认为null,默认获取的就是上面的arrays.xml文件。不为空则在本内存中获取,那么这边可能有人会问,SharePreferences怎么存储和获取数组元素,我这边贴出来一下:

package com.ds.platform.utils;

import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;

import com.ds.platform.bean.Lot;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * 覆盖模式的SharePreference
 */
public class SharePreferencesUtils {
    private final static String SP_NAME = "sp_cache";
    private static SharedPreferences mPreferences;        // SharedPreferences的实例
    private static final String TAG = LogUtils.LogName;

    private static SharedPreferences getSp(Context context) {
        if (mPreferences == null) {
            mPreferences = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);//覆盖
        }
        return mPreferences;
    }

    /**
     * 通过SP获得boolean类型的数据,没有默认为false
     *
     * @param context : 上下文
     * @param key     : 存储的key
     * @return
     */
    public static boolean getBoolean(Context context, String key) {
        SharedPreferences sp = getSp(context);
        return sp.getBoolean(key, false);
    }

    /**
     * 通过SP获得boolean类型的数据,没有默认为false
     *
     * @param context  : 上下文
     * @param key      : 存储的key
     * @param defValue : 默认值
     * @return
     */
    public static boolean getBoolean(Context context, String key, boolean defValue) {
        SharedPreferences sp = getSp(context);
        return sp.getBoolean(key, defValue);
    }

    /**
     * 设置int的缓存数据
     *
     * @param context
     * @param key     :缓存对应的key
     * @param value   :缓存对应的值
     */
    public static void setBoolean(Context context, String key, boolean value) {
        SharedPreferences sp = getSp(context);
        SharedPreferences.Editor edit = sp.edit();// 获取编辑器
        edit.putBoolean(key, value);
        edit.commit();
    }

    /**
     * 缓存float
     *
     * @param context
     * @param key
     * @param defValue
     * @return
     */
    public static void setFloat(Context context, String key, float defValue) {
        SharedPreferences sp = getSp(context);
        SharedPreferences.Editor edit = sp.edit();// 获取编辑器
        edit.putFloat(key, defValue);
        edit.commit();
    }

    /**
     * 获取int型数据
     * @param context
     * @param key
     * @param defValue
     * @return
     */
    public static int getInt(Context context, String key, int defValue) {
        SharedPreferences sp = getSp(context);
        return sp.getInt(key, defValue);
    }

    /**
     * 获取string值
     * @param context
     * @param key
     * @param defValue
     * @return
     */
    public static String getString(Context context, String key, String defValue) {
        SharedPreferences sp = getSp(context);
        return sp.getString(key, defValue);
    }

    public static float getFloat(Context context, String key, float defValue) {
        SharedPreferences sp = getSp(context);
        return sp.getFloat(key, defValue);
    }

    public static long getLong(Context context, String key, long defValue) {
        SharedPreferences sp = getSp(context);
        return sp.getLong(key, 0);
    }

    /**
     * 储存数组
     */
    public static void setArrayData(Context context, String key, CharSequence text[]) {
        if (text == null || text.length == 0) {
            return;
        }
        SharedPreferences sp = getSp(context);
        SharedPreferences.Editor edit = sp.edit();// 获取编辑器
        Set set = new HashSet();
        for (int i = 0; i < text.length; i++) {
            set.add(text[i]);
        }
        edit.putStringSet(key, set);
        edit.commit();
    }

    /**
     * 获取数组
     */
    public static CharSequence[] getStringSet(Context context, String key, Set<String> defValue) {
        CharSequence sequence[] = new CharSequence[6];
        SharedPreferences sp = getSp(context);
        Set set = sp.getStringSet(key, defValue);
        if (set != null && set.size() > 0) {
            Iterator<Object> it = set.iterator();
            for (int i = 0; i < set.size(); i++) {
                sequence[i] = (CharSequence) it.next();
            }
            Arrays.sort(sequence);
        }
        return sequence;
    }


    /**
     * 保存list数据
     */
    public static void setArrayList(Context context, String key, ArrayList<Lot> list) {
        if (list == null || list.size() == 0) {
            return;
        }
        Log.d(TAG, list.size() + "");
        SharedPreferences sp = getSp(context);
        SharedPreferences.Editor edit = sp.edit();// 获取编辑器
        int size = list.size();
        setInt(context, "listSize", size);//保存list的大小
        for (int i = 0; i < list.size(); i++) {
            edit.putString(key + i, list.get(i).getId());
            edit.putString(key + i + "n", list.get(i).getName());
        }
        edit.commit();
    }

    /**
     * 获取list数据
     */
    public static ArrayList<Lot> getArrayList(Context context, String key) {
        ArrayList<Lot> list = new ArrayList<>();
        int size = getInt(context, "listSize", 0);
        for (int i = 0; i < size; i++) {
            Lot lot = new Lot();
            lot.setId(getString(context, key + i, null));
            lot.setName(getString(context, key + i + "n", null));
            list.add(lot);
        }
        return list;
    }

    //删除list
    public static void deleteList(Context context, String key) {
        SharedPreferences sp = getSp(context);
        SharedPreferences.Editor edit = sp.edit();// 获取编辑器
        int size = getInt(context, "listSize", 0);
        for (int i = 0; i < size; i++) {
            edit.remove(key + i);
            edit.remove(key + i + "n");
            edit.commit();
        }
        deleteData(context, "listSize");//删除列表玩法的缓存数据
    }

    /**
     * 设置int的缓存数据
     *
     * @param context
     * @param key     :缓存对应的key
     * @param value   :缓存对应的值
     */
    public static void setInt(Context context, String key, int value) {
        SharedPreferences sp = getSp(context);
        SharedPreferences.Editor edit = sp.edit();// 获取编辑器
        edit.putInt(key, value);
        edit.commit();
    }


    public static void setString(Context context, String key, String value) {
        SharedPreferences sp = getSp(context);
        SharedPreferences.Editor edit = sp.edit();// 获取编辑器
        edit.putString(key, value);
        edit.commit();
    }

    public static void setLong(Context context, String key, long value) {
        SharedPreferences sp = getSp(context);
        SharedPreferences.Editor edit = sp.edit();// 获取编辑器
        edit.putLong(key, value);
        edit.commit();
    }


    public static void setInt(Context context, String key, String value) {
        SharedPreferences sp = getSp(context);
        SharedPreferences.Editor edit = sp.edit();// 获取编辑器
        edit.putString(key, value);
        edit.commit();
    }

    /**
     * 删除指定key的value
     *
     * @param context
     * @param key
     */
    public static void deleteData(Context context, String key) {
        SharedPreferences sp = getSp(context);
        SharedPreferences.Editor edit = sp.edit();// 获取编辑器
        edit.remove(key);
        edit.commit();
    }

    /**
     * 清空缓存中的全部数据
     *
     * @param context
     */
    public static void clearData(Context context) {
        SharedPreferences sp = getSp(context);
        SharedPreferences.Editor editor = sp.edit();
        editor.clear().commit();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260

上述是我项目中用到的,需要的可以看一下,另外分享一个小技巧,如果你想知道app中存入在Sp内存中的数据怎么看呢?可以在android studio中去查看,工具导航栏Tools–>Android–>Android Device Monitor ,打开之后点击右侧导航菜单的File Explorer ,找到data–>data–>自己的项目包名–>shared_prefs–>自己定义的sp名字,这边定义的是sp_cache,然后该目录下就存在一个sp_cache.xml的文件,点击右上角的图标这里写图片描述 第一个导出电脑就可以查看了。好了,原归正传: 
我怎么动态设置数组呢?先看下效果图: 
这里写图片描述

不知你们有没有发现一些细节,每次拖动条上面的刻度文字跟跳转另一个activity输入框的数字是顺序大小一样,还有动态去设置刻度值得时候我没有从0开始,而是大于0的数字开始,我也做了启用停用的开关,停用之后不能使用拖动条功能,这个状态也是保存在SP内存里面的。

接下来看一下拖动条的对话框,我使用fragment做的,这边也一起说一下吧! 
代码如下:

package com.ds.platform.dialog;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;

import com.ds.platform.R;
import com.ds.platform.activity.OrderActivity;
import com.ds.platform.utils.LogUtils;
import com.ds.platform.view.RangeSeekBar;

/**
 * @author: Allen.
 * @date: 2017/4/14
 * @description: 拖动条对话框
 */

public class MyDialogFragment extends android.support.v4.app.DialogFragment {
    private RangeSeekBar seekbar1;
    private String progressData = "0";//初始第一个数值
    private static final String TAG = LogUtils.LogName;
    private OrderActivity orderActivity;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.dialog_balance_seekbar, null);
        orderActivity = (OrderActivity) getActivity();
        initView(view);//初始控件
        //去除标题
        getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
        setCancelable(true);
        return view;
    }

    /**
     * 获取控件
     *
     * @param view
     */
    private void initView(View view) {
        seekbar1 = (RangeSeekBar) view.findViewById(R.id.seekbar1);
        seekbar1.setValue(0);//设置默认值

        //监听控件去设置列表订单的item金额
        seekbar1.setOnRangeChangedListener(new RangeSeekBar.OnRangeChangedListener() {
            @Override
            public void onRangeChanged(RangeSeekBar view, float min, float max, boolean isFromUser) {
                seekbar1.setProgressDescription((int) min + "");
                progressData = ((int) min) + "";
                orderActivity.setMoney(Integer.parseInt(progressData));
            }
        });


    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

那怎么使用这个fragment呢?首先不用思考的,其父类必须继承FragmentActivity,然后在父类的layout中必须有

<FrameLayout
        android:id="@+id/fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
  • 1
  • 2
  • 3
  • 4

然后可以通过view来触发这个fragment。

 FragmentTransaction tran = getSupportFragmentManager().beginTransaction();
        MyDialogFragment dialogFragment = new MyDialogFragment();
        dialogFragment.show(tran, "myDialog");
  • 1
  • 2
  • 3

需要注意的是要用V4的getSupportFragmentManager。

MyDialogFragment 的layout中需要使用自己定义的seekbar,如下:

<com.dsn.platform.view.RangeSeekBar
            android:id="@+id/seekbar1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@+id/view1"
            app:cellMode="number"
            app:lineColorEdge="@color/silery"
            app:lineColorSelected="@color/main_top"
            app:markTextArray="@array/markArray"
            app:seekBarMode="single"
            app:textPadding="15dp"
            app:textSize="@dimen/text14"
            app:textColor="@color/login_text"
            app:seekBarResId="@drawable/seekbar_thumb" />
    </RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

该控件上的属性封装在attrs.xml中:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- ******************** 参数解释 ******************** -->

    <!--最大值-->
    <!--最小值-->
    <!--两个按钮的最小间距-->
    <!--cells 等于0为普通模式,大于1时切换为刻度模式-->
    <!--是否关闭进度提示-->
    <!--拖动后的Seekbar颜色-->
    <!--默认的Seekbar颜色-->
    <!--进度为最小值或最大值时按钮的颜色,默认此属性不调用-->
    <!--进度不为最小值或最大值时按钮的颜色,默认此属性不调用-->
    <!--刻度文字,不设置的时候默认隐藏-->
    <!--按钮的背景资源,不设置的时候默认为圆形按钮-->
    <!--进度提示背景资源,必须使用 9 path文件-->
    <!--刻度文字与进度条之间的距离-->
    <!--刻度文字和进度提示文字的大小-->
    <!--进度提示背景的高度,不设置时根据文字尺寸自适应-->
    <!--进度提示背景的宽度,不设置时根据文字尺寸自适应-->
    <!--进度提示背景和进度条之间的距离-->
    <!--进度条的高度-->
    <!--按钮的尺寸-->
    <!--刻度模式
    number 根据刻度的实际所占比例分配位置(markTextArray中必须都为数字)
    other 平分当前布局(markTextArray可以是任何字符)
    -->
    <!--单向、双向模式 single 单向模式,只有一个按钮 range 双向模式,有两个按钮 -->

    <!-- ******************** 参数解释 ******************** -->

    <declare-styleable name="RangeSeekBar">
        <attr name="max" format="float"/>
        <attr name="min" format="float"/>
        <attr name="reserve" format="float"/>
        <attr name="cells" format="integer"/>
        <attr name="hideProgressHint" format="boolean"/>
        <attr name="lineColorSelected" format="color"/>
        <attr name="lineColorEdge" format="color"/>
        <attr name="thumbPrimaryColor" format="color"/>
        <attr name="thumbSecondaryColor" format="color"/>
        <attr name="markTextArray" format="reference"/>
        <attr name="seekBarResId" format="reference"/>
        <!-- must use 9 path !!!-->
        <attr name="progressHintResId" format="reference"/>
        <attr name="textPadding" format="dimension" />
        <attr name="textSize" format="dimension" />
        <attr name="textColor" format="color" />
        <attr name="hintBGHeight" format="dimension" />
        <attr name="hintBGWith" format="dimension" />
        <attr name="hintBGPadding" format="dimension" />
        <attr name="seekBarHeight" format="dimension"/>
        <attr name="thumbSize" format="dimension"/>
        <attr name="cellMode" format="enum">
            <enum name="number" value="0"/>
            <enum name="other" value="1"/>
        </attr>
        <attr name="seekBarMode" format="enum">
            <enum name="single" value="1"/>
            <enum name="range" value="2"/>
        </attr>

    </declare-styleable>
</resources>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

这边要注意progressHintResId属性,设置刻度提示背景必须使用.9的图片。

另一个跳转的activity页面我就不贴代码了,逻辑很简单,把输入的数字封装成数组,存入SP中,然后你每次弹出拖动条对话框,它都会去判断缓存中是否有值。

好了,今天先分享这个,感谢!


Android自定义组合控件

-
  • 1970年01月01日 08:00

Android——自定义带刻度的SeekBar单向拖动条

时间过得真快,才发现好久没来逛逛了。没写博客的这段时间一直在做项目,连续完成了两个大型app,这个过程很享受,这是独立开发的,所以中途有很多很多的问题需要自己一个一个的去解决,现在接近尾声了,发现自己...
  • u013836857
  • u013836857
  • 2017-04-21 11:29:53
  • 2443

【Android】高仿大众点评中的范围选择控件之RangeSeekBar

1.Android中自带的SeekBar可以用于一定范围内
  • u010119170
  • u010119170
  • 2014-07-25 22:59:08
  • 6515

自定义View实现SeekBar点值选择效果(一)

这几天在看google原生CameraApp的代码,不得不说写的是真TM的复杂,特别是逻辑处理那一块,关于google原生CameraApp这一块后续可能会作为一个新的系列来写,今天主要介绍我在网上看...
  • u011043551
  • u011043551
  • 2017-08-19 15:52:50
  • 1028

一款美观的自定义SeekBar,支持单、双向、阶段滑动、刻度、负数等多种强大自定义属性

效果图前言篇幅有限,本文只讲解关键关键思路,伸手党和想看详细思路的请移步 传送门点我点我!!,如果喜欢,欢迎 Star 和 Fork !实现思路本控件其实奔着双向滑动的SeekBar实现的,不过兼容了...
  • u011747781
  • u011747781
  • 2017-02-10 16:03:46
  • 3408

自定义<em>单向</em>带<em>刻度</em>的<em>seekbar</em>

一款美观的自定义<em>SeekBar</em>,支持单、双向、阶段<em>滑动</em>、<em>刻度</em>、负数等多种强大自定义...Android——自定义带<em>刻度</em>的<em>SeekBar单向</em>拖动条 自定义View实现<em>SeekBar</em>点值选择效果...
  • 2018年04月16日 00:00

自定义带刻度的SeekBar

package com.example.ilaw66lawyer.ui.view; import android.animation.Animator; import android.animati...
  • u010660841
  • u010660841
  • 2017-12-12 14:00:00
  • 249

android 双向<em>seekbar</em>

Android的双向带<em>刻度</em>的<em>seekbar</em>,表面有<em>刻度</em>,两个滑块,随意在<em>刻度</em>上停留。... Android——自定义带<em>刻度</em>的<em>SeekBar单向</em>拖动条...自定义双向<em>滑动SeekBar</em> Android双向<em>seekb</em>...
  • 2018年04月17日 00:00

Android可<em>滑动刻度</em>尺

Android——自定义带刻度的<em>SeekBar单向</em>拖动条 android 滚轮刻度尺的实现 ...Android可<em>滑动刻度</em>尺 会员到期时间: 剩余下载个数: 剩余C币: 剩余积分:0 ...
  • 2018年04月14日 00:00

android 拖动条(SeekBar)

android 拖动条(SeekBar)  拖动条类似进度条,不同的是用户可以控制,比如,应用程序中用户可以对音效进行控制,这就可以使用拖动条来实现。由于拖动条可以被用户控制,所以需要对其进...
  • shuangziqiqi
  • shuangziqiqi
  • 2014-11-12 14:33:12
  • 259
收藏助手
不良信息举报
您举报文章:Android——自定义带刻度的SeekBar单向拖动条 原创 2017年04月21日 11:29:53 标签:android /listview /app /seekbar 1302 时间过得真快,
举报原因:
原因补充:

(最多只允许输入30个字)