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

来源:互联网 发布:ipad软件商店不动 编辑:程序博客网 时间:2024/06/04 19:49

时间过得真快,才发现好久没来逛逛了。没写博客的这段时间一直在做项目,连续完成了两个大型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;        }    }}

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

//从缓存中拿值        float maxValue = SharePreferencesUtils.getFloat(context, "maxValue", 0);        if (0 == maxValue) {            mMax = t.getFloat(R.styleable.RangeSeekBar_max, 100);//最大取值        } else {            mMax = maxValue;        }
这边的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>

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

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

一样的 存储方式,放在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();    }}

上述是我项目中用到的,需要的可以看一下,另外分享一个小技巧,如果你想知道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));            }        });    }}

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

<FrameLayout        android:id="@+id/fragment"        android:layout_width="match_parent"        android:layout_height="match_parent" />

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

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

需要注意的是要用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>

该控件上的属性封装在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>

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

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

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

9 0
原创粉丝点击