自定义区间滑动取值控件主要涉及到的知识点有坐标系、画笔、画布以及自定义属性。因此,在自定义控件之前,先来了解一下相关的知识。

相关知识

关于坐标

关于坐标系,android中有两种,分别为Android坐标系和视图坐标系。

  1. Android坐标系:以手机屏幕左上角的顶点为坐标原点,从该点向右为x轴正方向,从该点向下为y轴正方向。而触控事件中,使用getRawX()和getRawY()方法。
  2. 视图坐标系:视图坐标系是以父视图的左上角为坐标原点的。相应的原点向右为x轴正方向,原点向下为y轴正方向。在触控中,通过getX()和getY()来获取的坐标值就是视图坐标系中的坐标值。

在自定义控件时,一定会涉及到控件的大小以及间距的值,而这些值又是如何得到的呢。下面我们来看看获取距离的几种方法。

  1. View提供的获取的坐标以及距离的方法:

    • getTop() :获取到的是view自身的顶边到其父布局顶边的距离
    • getLeft():获取到的是view自身的左边到其父布局左边的距离
    • getRight():获取到的是view自身的右边到其父布局左边的距离
    • getPaddingStart():获取控件的控件内容与控件左边缘的距离,Api17之前为getPaddingLeft()
    • getPaddingTop():获取控件的控件内容与控件底部边缘的距离
    • getPaddingEnd():获取控件的控件内容与控件右边缘的距离,Api17之前为getPaddingRight()
    • getPaddingBottom():获取控件的控件内容与控件底部边缘的距离

    2 . MotionEvent提供的方法(触发屏幕触碰事件时,在onTouchEvent回调方法中传入MotionEvent的参数):

    • getX():获取点击事件距离控件左边的距离,即视图坐标
    • getY():获取点击事件距离控件顶边的距离,即视图坐标
    • getRawX():获取到的是点击事件距离整个屏幕左边的距离,即绝对坐标
    • getRawY():获取到的是点击事件距离整个屏幕顶边的距离,即绝对坐标

PS:需要特别注意,Y轴的正方向是向下的。

画笔

画笔样式:

  • Paint.Style.FILL:填充内部
  • Paint.Style.FILL_AND_STROKE:填充内部和描边
  • Paint.Style.STROKE:描边

关于画笔,这里只说明一下我们用到的几个方法以及其参数。

  • android.graphics.Paint#setColor() 设置画笔颜色。
  • android.graphics.Paint#setStrokeWidth()画笔样式为描边(Paint.Style.STROKE)时,设置画笔的宽度。
  • android.graphics.Paint#setAntiAlias()设置抗锯齿,如果不设置,加载位图的时候可能会出现锯齿状的边界,如果设置,边界就会变的稍微有点模糊,锯齿就看不到了,传入true为设置抗锯齿。
  • android.graphics.Paint#setStyle()设置画笔样式,参数为Paint.Style的枚举类型。
  • android.graphics.Paint#setTextAlign()设置画笔绘制文本的对齐方式,参数为Paint.Align的枚举值,分别为:
    • Paint.Align.LEFT:文本被绘制到x,y原点的右边。
    • Paint.Align.CENTER:文本是在x,y原点上水平居中绘制的。
    • Paint.Align.RIGHT:文本被画在x,y原点的左边。

画布

Canvas的几个draw方法,这些方法都进行了不同形式的重载。

  • canvas.drawText()绘制文本
  • canvas.drawLine()绘制线条
  • canvas.drawCircle()绘制圆形
  • canvas.drawArc()画弧度
  • canvas.drawARGB()在整个画布中填充颜色,有四个参数,参数值0到255
  • canvas.drawBitmap()绘制位图
  • canvas.drawColor()在整个画布中填充颜色
  • canvas.drawOval() 画椭圆
  • canvas.drawRoundRect()绘制圆角矩形
  • canvas.drawRect()绘制矩形
  • canvas.drawPicture()绘制图片
  • canvas.drawPoint()画点
  • canvas.drawPath()根据指定路径(即由一组坐标组成)绘制图形

细说一下接下来要用的三个方法:

  1. drawLine(float startX, float startY, float stopX, float stopY, @NonNull Paint paint)

    • startX:线条的起始点的x坐标
    • startY:线条的起始点的y坐标
    • stopX:线条的终止点的x坐标
    • stopY:线条的终止点的y坐标
    • paint:绘制线条的画笔
  2. drawCircle(float cx, float cy, float radius, @NonNull Paint paint)

    • cx:圆心的x坐标
    • cy:圆心的y坐标
    • radius:圆半径
    • paint:绘制圆形的画笔
  3. drawText(@NonNull String text, float x, float y, @NonNull Paint paint)

    • text:将要绘制的文字
    • x:绘制文字起始点的x坐标
    • y:绘制文字起始点的y坐标
    • paint:绘制文字的画笔

Canvas还有几个应该关注的方法:

  • canvas.save():把当前的绘制的图像保存起来,让后续的操作相当于是在一个新的图层上的操作。
  • canvas.restore():把当前画布返回(调整)到上一个save()状态之前。
  • canvas.translate(dx, dy):把当前画布的原点移到(dx,dy),后面的操作都以(dx,dy)作为参照点,默认原点为(0,0)。
  • canvas.scale(x,y):放大。x为水平方向的放大倍数,y为竖直方向的放大倍数。
  • canvas.rotate(angel):旋转.angle指旋转的角度,顺时针旋转。
  • canvas.transform():切变。所谓切变,把图像的顶部或底部推到一边。
  • canvas.saveLayer(bounds, paint, saveFlags):保存图层。

View的几个关键方法

onMeasure(int widthMeasureSpec, int heightMeasureSpec):这两个参数widthMeasureSpec, heightMeasureSpec由ViewGroup中的layout_width,la

  • yout_height和padding以及View自身的layout_margin共同决定。权值weight也是尤其需要考虑的因素,有它的存在情况可能会稍微复杂点。
  • 这个方法主要是对控件的测量,通过重写这个方法,根据传入的两个参数,并调用MeasureSpec.getSize()方法,我们才能获得控件的宽高,如下

    • MeasureSpec.getSize(heightMeasureSpec);//获取总高度,是包含padding值
    • MeasureSpec.getSize(widthMeasureSpec);//获取总宽度,是包含padding值
  • onDraw(Canvas canvas):根据设置的画笔及控件的相关参数来绘制控件,对于canvas的相关操作,已在上面进行说明。

  • 还有一个onLayou()方法,在自定义控件时,也是很关键的,但是由于此次没有用到,因此就先不说明了。

View构造方法的参数

public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {    this(context, attrs, defStyleAttr, 0);}
  • 第一个属于程序内实例化时采用,之传入Context即可。
  • 第二个用于layout文件实例化,会把XML内的参数通过AttributeSet带入到View内。
  • 第三个主题的style信息,也会从XML里带入。
  • 从API21起,还有第四个参数,默认样式资源,这里传入样式资源id。

自定义属性

在res/valuse的attrs.xml中自定义属性,几个关键词的理解:

  • declare-styleable: 表示一个属性组。它的name必须和你自定义view的名字相同。
  • attr:表示单独的一个属性。
    • name是属性名称,会和属性组的name通过下划线拼接,来唯一标识一个属性。
    • format代表属性的格式。格式包括很多种:比如颜色,数值,枚举等。

format类型有:

  • string:支付窜
  • dimension:尺寸值
  • boolean:布尔值
  • float:浮点值
  • integer:整形值
  • color:颜色值
  • enum:枚举值
  • flag:位或运算
  • fraction:百分数
  • reference:参考某一资源ID

在自定义控件时,我们通过Context实例调用obtainStyledAttributes(android.util.AttributeSet, int[], int, int)方法得到TypedArray实例,该实例中有相应的方法来获取对应格式的属性值。在自定义控件的构造方法中去调用,最后还要调用TypedArray实例的recycle()方法来释放资源(注:TypeArray内部是通过一个静态方法来维护实例的,也就是说这一个典型的单例模式)。

//自定义属性<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="RangeSelectionView">                <attr name="backLineColor" format="color" />                <attr name="connectLineColor" format="color" />                <attr name="circleColor" format="color" />                <attr name="whileCircleColor" format="color" />                <attr name="startValueColor" format="color" />                <attr name="endValueColor" format="color" />                <attr name="resultValueColor" format="color" />                <attr name="isShowResult" format="boolean" />                <attr name="isInteger" format="boolean" />                <attr name="valuePrecision" format="integer" />                <attr name="startValue" format="float" />                <attr name="endValue" format="float" />                <attr name="leftUnit" format="string" />                <attr name="rightUnit" format="string" />    declare-styleable>resources>

自定义控件

关键值

这个控件中,需要计算的几个关键值

  1. 基线的起始和终点坐标值;

    //无论是起点还是终点,Y轴的值均是控件高度的一半//起点的X轴:设定的边距值+控件的paddingStart//终点的x轴:控件宽度-设定的边距值-paddingEnd
  2. 起始和终点圆圈的坐标值;
  3. 起始和终点值的显示位置的坐标值;
  4. 顶部值显示位置的坐标值;
  5. 滑动进度值。

由于代码中已经有很明确的注释,因此对于所定义的变量和一些计算,在这里就不进行解释了。下面直接上代码。

控件代码

/** * Description:区间滑动取值控件 * Created by Kevin.Li on 2018-01-11. */public class RangeSelectionView extends View {    private Paint paintBackground;//背景线的画笔    private Paint paintCircle;//起始点圆环的画笔    private Paint paintWhileCircle;//起始点内圈白色区域的画笔    private Paint paintStartText;//起点数值的笔    private Paint paintEndText;//终点数值的画笔    private Paint paintResultText;//顶部结果数值的画笔    private Paint paintConnectLine;//起始点连接线的画笔    private int mHeight = 0;//控件的高度    private int mWidth = 0;//控件的宽度    private float centerVertical = 0;//y轴的中间位置    private float backLineWidth = 5;//底线的宽度    private float marginHorizontal = 1;//横向边距    private float marginTop = 60;//文字距基线顶部的距离    private float marginBottom = 40;//文字距基线底部的距离    private float pointStart = 0;//起点的X轴位置    private float pointEnd = 0;//始点的Y轴位置    private float circleRadius = 30;//起始点圆环的半径    private float numStart = 0;//数值的开始值    private float numEnd = 0;//数值的结束值    private int textSize = 35;//文字的大小    private String strConnector = " - ";//连接符    private boolean isRunning = false;//是否可以滑动    /**     * 起点还是终点 true:起点;false:终点。     */    private boolean isStart = true;    private int pdStart;//控件padding值    private int pdEnd;    private float scaling;//取值比例    /**     * 进度值范围——起点值     */    private float startNum = 0.00F;    /**     * 进度值范围——终点值     */    private float endNum = 100.00F;    /**     * 左侧单位     */    private String leftUnit;    /**     * 右侧单位     */    private String rightUnit;    /**     * 是否保留整形     */    private boolean isInteger = false;    /**     * 保留精度,默认为2。     */    private int precision = 2;    /**     * 是否显示结果值,默认显示。     */    private boolean isShowResult = true;    /**     * 开始文字颜色     */    private int startValueColor;    /**     * 终点文字颜色     */    private int endValueColor;    /**     * 结果值文字颜色     */    private int resultValueColor;    /**     * 基线颜色     */    private int backLineColor;    /**     * 连接线颜色     */    private int connectLineColor;    /**     * 外圆填充色     */    private int circleColor;    /**     * 圆形填充色     */    private int whileCircleColor;    private OnChangeListener mOnChangeListener;    public RangeSelectionView(Context context) {        super(context);        init();    }    public RangeSelectionView(Context context, @Nullable AttributeSet attrs) {        this(context, attrs, 0);        init();    }    public RangeSelectionView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        handleAttrs(context, attrs, defStyleAttr);        init();    }    /**     * 获取自定义属性的值     */    private void handleAttrs(Context context, AttributeSet attrs, int defStyleAttr) {        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.RangeSelectionView, defStyleAttr, 0);        backLineColor = ta.getColor(R.styleable.RangeSelectionView_backLineColor, Color.CYAN);        connectLineColor = ta.getColor(R.styleable.RangeSelectionView_connectLineColor, Color.BLUE);        circleColor = ta.getColor(R.styleable.RangeSelectionView_circleColor, Color.BLUE);        whileCircleColor = ta.getColor(R.styleable.RangeSelectionView_whileCircleColor, Color.WHITE);        startValueColor = ta.getColor(R.styleable.RangeSelectionView_startValueColor, Color.MAGENTA);        endValueColor = ta.getColor(R.styleable.RangeSelectionView_endValueColor, Color.MAGENTA);        resultValueColor = ta.getColor(R.styleable.RangeSelectionView_resultValueColor, Color.MAGENTA);        isShowResult = ta.getBoolean(R.styleable.RangeSelectionView_isShowResult, true);        isInteger = ta.getBoolean(R.styleable.RangeSelectionView_isInteger, false);        precision = ta.getInteger(R.styleable.RangeSelectionView_valuePrecision, 2);        startNum = ta.getFloat(R.styleable.RangeSelectionView_startValue, startNum);        endNum = ta.getFloat(R.styleable.RangeSelectionView_endValue, endNum);        if (ta.getString(R.styleable.RangeSelectionView_leftUnit) != null) {            leftUnit = ta.getString(R.styleable.RangeSelectionView_leftUnit);        }        if (ta.getString(R.styleable.RangeSelectionView_rightUnit) != null) {            rightUnit = ta.getString(R.styleable.RangeSelectionView_rightUnit);        }        ta.recycle();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        //获取控件的宽高、中线位置、起始点、起始数值        mHeight = MeasureSpec.getSize(heightMeasureSpec);//获取总高度,是包含padding值        mWidth = MeasureSpec.getSize(widthMeasureSpec);//获取总宽度,是包含padding值        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {            pdStart = getPaddingStart();            pdEnd = getPaddingEnd();        } else {            pdStart = getPaddingLeft();            pdEnd = getPaddingRight();        }        centerVertical = mHeight / 2;        pointStart = marginHorizontal + pdStart + circleRadius;        pointEnd = mWidth - marginHorizontal - pdEnd - circleRadius;        initBaseData();    }    /**     * 初始化基础值     */    private void initBaseData() {        // (父级控件宽度-左右边距-圆直径)/(结束值-起点值)        scaling = (mWidth - 2 * marginHorizontal - (pdStart + pdEnd) - 2 * circleRadius) / (endNum - startNum);        numStart = getProgressNum(pointStart);        numEnd = getProgressNum(pointEnd);    }    @SuppressLint("ClickableViewAccessibility")    @Override    public boolean onTouchEvent(MotionEvent event) {        super.onTouchEvent(event);        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                //如果点击的点在第一个圆内就是起点,并且可以滑动                if (event.getX() >= (pointStart - circleRadius) && event.getX() <= (pointStart + circleRadius)) {                    isRunning = true;                    isStart = true;                    pointStart = event.getX();                    //如果点击的点在第二个圆内就是终点,并且可以滑动                } else if (event.getX() <= (pointEnd + circleRadius) && event.getX() >= (pointEnd - circleRadius)) {                    isRunning = true;                    isStart = false;                    pointEnd = event.getX();                } else {                    //如果触控点不在圆环内,则不能滑动                    isRunning = false;                }                break;            case MotionEvent.ACTION_MOVE:                if (isRunning) {                    if (isStart) {                        //起点滑动时,重置起点的位置和进度值                        pointStart = event.getX();                        if (pointStart < marginHorizontal + pdStart + circleRadius) {                            pointStart = marginHorizontal + pdStart + circleRadius;                            numStart = startNum;                        } else {                            if (pointStart + circleRadius < pointEnd - circleRadius) {//防止起点不动而值增加的问题                                numStart = getProgressNum(pointStart);                            }                        }                    } else {                        //始点滑动时,重置始点的位置和进度值                        pointEnd = event.getX();                        if (pointEnd > mWidth - marginHorizontal - pdEnd - circleRadius) {                            pointEnd = mWidth - marginHorizontal - pdEnd - circleRadius;                            numEnd = endNum;                        } else {                            if (pointEnd < marginHorizontal + pdStart + 3 * circleRadius) {//防止终点和起点在起始点相连时,往左移动,终点不动,而值减小的问题。                                pointEnd = marginHorizontal + pdStart + 3 * circleRadius;                            }                            numEnd = getProgressNum(pointEnd);                        }                    }                    flushState();//刷新状态                }                break;            case MotionEvent.ACTION_UP:                flushState();                break;        }        return true;    }    /**     * 刷新状态和屏蔽非法值     */    private void flushState() {        //起点非法值        if (pointStart < marginHorizontal + pdStart + circleRadius) {            pointStart = marginHorizontal + pdStart + circleRadius;        }        //终点非法值        if (pointEnd > mWidth - marginHorizontal - pdEnd - circleRadius) {            pointEnd = mWidth - marginHorizontal - pdEnd - circleRadius;        }        //防止起点位置大于终点位置(规定:如果起点位置大于终点位置,则将起点位置放在终点位置前面,即:终点可以推着起点走,而起点不能推着终点走)        if (pointStart + circleRadius > pointEnd - circleRadius) {            pointStart = pointEnd - 2 * circleRadius;            numStart = getProgressNum(pointStart);//更新起点值        }        //防止终点把起点推到线性范围之外        if (pointEnd < marginHorizontal + pdStart + 3 * circleRadius) {            pointEnd = marginHorizontal + pdStart + 3 * circleRadius;            pointStart = marginHorizontal + pdStart + circleRadius;        }        invalidate();//这个方法会导致onDraw方法重新绘制        if (mOnChangeListener != null) {// call back listener.            mOnChangeListener.leftCursor(String.valueOf(numStart));            mOnChangeListener.rightCursor(String.valueOf(numEnd));        }    }    /**     * 根据屏幕像素值计算进度数值     */    private float getProgressNum(float progress) {        if (progress == marginHorizontal + pdStart + circleRadius) {// 处理边界问题,起始值            return startNum;        }        if (progress == mWidth - marginHorizontal - pdEnd - circleRadius) {// 处理边界问题,终止值            return endNum;        }        // (坐标点-左边距-圆半径)/比例 + 起始值        return (progress - marginHorizontal - pdEnd - circleRadius) / scaling + startNum;    }    /**     * 初始化画笔     */    private void init() {        paintBackground = new Paint();        paintBackground.setColor(backLineColor);        paintBackground.setStrokeWidth(backLineWidth);        paintBackground.setAntiAlias(true);        paintCircle = new Paint();        paintCircle.setColor(circleColor);        paintCircle.setStrokeWidth(backLineWidth);        paintCircle.setStyle(Paint.Style.STROKE);        paintCircle.setAntiAlias(true);        paintWhileCircle = new Paint();        paintWhileCircle.setColor(whileCircleColor);        paintCircle.setStyle(Paint.Style.FILL);        paintWhileCircle.setAntiAlias(true);        paintStartText = new Paint();        paintStartText.setColor(startValueColor);        paintStartText.setTextSize(textSize);        paintStartText.setAntiAlias(true);        paintEndText = new Paint();        paintEndText.setColor(endValueColor);        paintEndText.setTextSize(textSize);        paintEndText.setAntiAlias(true);        paintEndText.setTextAlign(Paint.Align.RIGHT);        paintResultText = new Paint();        paintResultText.setColor(resultValueColor);        paintResultText.setTextSize(textSize);        paintResultText.setAntiAlias(true);        paintConnectLine = new Paint();        paintConnectLine.setColor(connectLineColor);        paintConnectLine.setStrokeWidth(backLineWidth + 5);        paintConnectLine.setAntiAlias(true);    }    @Override    protected void onDraw(Canvas canvas) {//        super.onDraw(canvas);        //背景线        //无论是起点还是终点,Y轴的值均是控件高度的一半        //起点的X轴:设定的边距值+控件的paddingStart        //终点的x轴:控件宽度-设定的边距值-paddingEnd        canvas.drawLine(marginHorizontal + pdStart, centerVertical, mWidth - marginHorizontal - pdEnd, centerVertical, paintBackground);        //起点位置的外圈圆        canvas.drawCircle(pointStart, centerVertical, circleRadius, paintCircle);        //起点位置的内圈圆        canvas.drawCircle(pointStart, centerVertical, circleRadius - backLineWidth, paintWhileCircle);        //终点位置的外圈圆        canvas.drawCircle(pointEnd, centerVertical, circleRadius, paintCircle);        //终点位置的内圈圆        canvas.drawCircle(pointEnd, centerVertical, circleRadius - backLineWidth, paintWhileCircle);        //起始点连接线        canvas.drawLine(pointStart + circleRadius, centerVertical, pointEnd - circleRadius, centerVertical, paintConnectLine);        //起点数值        canvas.drawText(assembleStartText(), pointStart - circleRadius, centerVertical + marginBottom + circleRadius, paintStartText);        //终点数值        canvas.drawText(assembleEndText(), pointEnd + circleRadius, centerVertical + marginBottom + circleRadius, paintEndText);        if (isShowResult) {            //结果值            canvas.drawText(assembleResultText(), marginHorizontal + pdStart, centerVertical - marginTop, paintResultText);        }    }    /**     * 处理起点值精度     */    private float handleNumStartPrecision(float value) {        BigDecimal bd = new BigDecimal(value);        bd = bd.setScale(precision, BigDecimal.ROUND_HALF_UP);        return bd.floatValue();    }    /**     * 处理终点值精度     */    private float handleNumEndPrecision(float value) {        BigDecimal bd = new BigDecimal(value);        bd = bd.setScale(precision, BigDecimal.ROUND_HALF_DOWN);        return bd.floatValue();    }    /**     * 组装起点文字     */    private String assembleStartText() {        StringBuilder sb = new StringBuilder();        if (!TextUtils.isEmpty(leftUnit)) sb.append(leftUnit);        //必须在此调用String.valueOf()来提前转化为String,否则会因为append()重载而导致整形参数无效的问题。        sb.append(isInteger ? String.valueOf((int) numStart) : String.valueOf(handleNumStartPrecision(numStart)));        if (!TextUtils.isEmpty(rightUnit)) sb.append(" ").append(rightUnit);        return sb.toString();    }    /**     * 组装终点文字     */    private String assembleEndText() {        StringBuilder sb = new StringBuilder();        if (!TextUtils.isEmpty(leftUnit)) sb.append(leftUnit);        sb.append(isInteger ? String.valueOf((int) numEnd) : handleNumEndPrecision(numEnd));        if (!TextUtils.isEmpty(rightUnit)) sb.append(" ").append(rightUnit);        return sb.toString();    }    /**     * 组装结果值     */    private String assembleResultText() {        StringBuilder sb = new StringBuilder();        if (!TextUtils.isEmpty(leftUnit)) sb.append(leftUnit);        sb.append(isInteger ? String.valueOf((int) numStart) : handleNumStartPrecision(numStart));        if (!TextUtils.isEmpty(rightUnit)) sb.append(" ").append(rightUnit);        sb.append(strConnector);        if (!TextUtils.isEmpty(leftUnit)) sb.append(leftUnit);        sb.append(isInteger ? String.valueOf((int) numEnd) : handleNumEndPrecision(numEnd));        if (!TextUtils.isEmpty(rightUnit)) sb.append(" ").append(rightUnit);        return sb.toString();    }    /**     * 左侧单位     */    public RangeSelectionView setLeftUnit(String leftUnit) {        this.leftUnit = leftUnit;        return this;    }    /**     * 右侧单位     */    public RangeSelectionView setRightUnit(String rightUnit) {        this.rightUnit = rightUnit;        return this;    }    /**     * 是否保留整形     */    public RangeSelectionView setInteger(boolean integer) {        isInteger = integer;        return this;    }    /**     * 是否显示结果值,默认显示。     */    public RangeSelectionView setShowResult(boolean showResult) {        isShowResult = showResult;        return this;    }    /**     * 保留精度,默认为2。     */    public RangeSelectionView setPrecision(int precision) {        this.precision = precision;        return this;    }    /**     * 起始值     */    public RangeSelectionView setStartNum(float startNum) {        this.startNum = startNum;        return this;    }    /**     * 结束值     */    public RangeSelectionView setEndNum(float endNum) {        this.endNum = endNum;        return this;    }    /**     * 开始文字颜色     */    public RangeSelectionView setStartValueColor(int startValueColor) {        this.startValueColor = startValueColor;        return this;    }    /**     * 终点文字颜色     */    public RangeSelectionView setEndValueColor(int endValueColor) {        this.endValueColor = endValueColor;        return this;    }    /**     * 结果值文字颜色     */    public RangeSelectionView setResultValueColor(int resultValueColor) {        this.resultValueColor = resultValueColor;        return this;    }    /**     * 基线颜色     */    public RangeSelectionView setBackLineColor(int backLineColor) {        this.backLineColor = backLineColor;        return this;    }    /**     * 连接线颜色     */    public RangeSelectionView setConnectLineColor(int connectLineColor) {        this.connectLineColor = connectLineColor;        return this;    }    /**     * 外圆填充色     */    public RangeSelectionView setCircleColor(int circleColor) {        this.circleColor = circleColor;        return this;    }    /**     * 圆形填充色     */    public RangeSelectionView setWhileCircleColor(int whileCircleColor) {        this.whileCircleColor = whileCircleColor;        return this;    }    /**     * 通知刷新。     * 在调用系列setXxx方法之后,需要调用此方法,才会生效。     */    public void notifyRefresh() {        init();        initBaseData();        invalidate();//这个方法会导致onDraw方法重新绘制    }    /**     * 主要充值起点和终点的画笔值     */    public void reSetValue() {        pointStart = marginHorizontal + pdStart + circleRadius;        pointEnd = mWidth - marginHorizontal - pdEnd - circleRadius;    }    public void setOnChangeListener(OnChangeListener onChangeListener) {        mOnChangeListener = onChangeListener;    }    /**     * 值变化监听器     */    public interface OnChangeListener {        /**         * 起点进度值变化回调         */        void leftCursor(String resultValue);        /**         * 终点进度值变化回调         */        void rightCursor(String resultValue);    }}

调用代码

"http://schemas.android.com/apk/res-auto"    android:id="@+id/rsv_view"    android:layout_width="match_parent"    android:layout_height="120dp"    android:background="@android:color/white"    android:visibility="visible"    rsv:backLineColor="@color/blue_transparent_background_70"    rsv:circleColor="@color/blue_transparent_background_70"    rsv:connectLineColor="@color/auxiliary_blue"    rsv:endValue="1.0"    rsv:endValueColor="@color/warn"    rsv:isInteger="true"    rsv:leftUnit="@string/monetary_unit_rmb"    rsv:resultValueColor="@color/danger"    rsv:startValue="10.0"    rsv:startValueColor="@color/warn"    rsv:whileCircleColor="@color/white" />
//java代码调用final RangeSelectionView rsv;rsv = (RangeSelectionView) findViewById(R.id.rsv_view);rsv.setBackLineColor(Color.RED);rsv.setResultValueColor(Color.DKGRAY);rsv.setStartNum(5);rsv.setEndNum(20);rsv.notifyRefresh();

效果一览


Demo源码

Github:https://github.com/ysg-lijinwen/IntervalValueControl.git

相关知识详细介绍

Android Paint的使用详解:Android Paint的使用详解
Android自定义属性之format解析:Android自定义属性之format解析
Android自定义View(三、深入解析控件测量onMeasure):Android自定义View(三、深入解析控件测量onMeasure)

更多相关文章

  1. 【Android】UI设计之界面布局
  2. Android自定义控件以及控件属性的自定义
  3. Android开发 ListView(垂直滚动列表项视图)的简单使用
  4. Android百度地图开发(四)线路搜索
  5. Android图表控件MPAndroidChart的简单介绍(MPAndroidChart3.0)
  6. Android(安卓)自定义布局控件-圆形RelativeLayout
  7. Android图表控件MPAndroidChart,折线图LineChart最简单的使用.
  8. android样式学习(一) 使用selector改变来动态改变背景颜色
  9. not using the 2- or 3-argument View constructors

随机推荐

  1. Android之——Fragment控制切换多个页面
  2. android的style控制Theme
  3. weex转android踩坑之旅
  4. android studio更改兼容android低版本
  5. android工程项目导入问题汇…
  6. Android(安卓)Android-skin-support 换肤
  7. Android中打电话的数据流程
  8. flutter +Android(安卓)studio+Xcode 调
  9. TabHost 置于底部 顶部的方法
  10. Android中解析xml文件的接口