自定义带标尺的seekbar

前言

第一次书写技术类的文章,希望能够将自己的所学和成长记录下来。

内容

由于公司的项目需要,需要制作一个指针滑动的seekbar,如图。

应用内效果

查阅了网上大量的相关实例和知识,在此进行一个总结,如有问题希望大家能够提出。

功能需求是这样的:需要一个带有标尺的滑动条,滑动部分为游标而不是常见的滑动背景的标尺,滑动游标的时候其他的如TextView的控件可以直接拿到当前游标所指的值。因此考虑首先需要一个标尺,本来考虑标尺的展示使用图片,但是后来考虑到适配的问题较为繁琐,因此采用自定义控件的时候画出来。而游标方面,也考虑样式固定化,也采用的是绘制的方式。

绘制部分代码如下:

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        //画最下面的线
        canvas.drawLine(PADDING_LEFT,getHeight(),widthOfProgress,getHeight(),mLinePaint);
        for (int i = minProgress; i <= maxProgress; i=i+oneItemValue) {
            if (i % oneGroupValue == 0 || i == minProgress) {
                //起点x坐标10像素,画厘米线
                canvas.drawLine(PADDING_LEFT, getHeight(), PADDING_LEFT, getHeight()/2+16*scaleX, mLinePaint);
                //计算刻度数
                String text = i + "";
                Rect rect = new Rect();
                //获取文本宽度
                float txtWidth = mTextPaint.measureText(text);
                mTextPaint.getTextBounds(text, 0, text.length(), rect);
                //画标尺的数字
                canvas.drawText(text, PADDING_LEFT - txtWidth / 2, getHeight()/2+16*scaleX-rect.height()  , mTextPaint);
            } else if (i % oneItemValue == 0) {
                //每隔小单位画间隔线,lineHeight越长,线越短
                double lineHeight = (getHeight()/2)*0.6;
                canvas.drawLine(PADDING_LEFT, getHeight(), PADDING_LEFT, getHeight()/2+(float)lineHeight, mLinePaint);
            }
//            else {
//                //画毫米线
//                double lineHeight = (getHeight()/2)*0.4;
//                canvas.drawLine(44, getHeight(), 44, getHeight()/2+(float)lineHeight, mLinePaint);
//            }
            //每隔一个单位像素移动一次达到划线效果
            canvas.translate(widthOfItem, 0);
        }

        canvas.restore();
        //画红线游标
        canvas.drawLine(xProgress, getHeight()/2, xProgress, getHeight(), mRulerPaint);
        Log.i("--++",cursorRadius+"");
        canvas.drawCircle(xProgress, getHeight()/2, cursorRadius, mRulerPaint);
        //测试用显示progress
//        BigDecimal bd = new BigDecimal((progrees - 18) / 180);
//        bd = bd.setScale(1, BigDecimal.ROUND_HALF_UP);
//        mTextPaint.setTextSize(36);
//        float cursorTextWidth = mTextPaint.measureText(bd.floatValue()+"");
//        canvas.drawText(progrees+"", getWidth()/2-cursorTextWidth/2, cursorTextWidth, mTextPaint);
    }

attrs文件中的内容:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="RangeSeekBar">
        <attr name="line_color" format="color"></attr>
        <attr name="cursor_color" format="color"></attr>
        <attr name="text_color" format="color"></attr>
        <attr name="max_progress" format="integer"></attr>
        <attr name="min_progress" format="integer"></attr>
        <attr name="oneitem_value" format="integer"></attr>
        <attr name="one_group_value" format="integer"></attr>
        <attr name="current_progress" format="float"></attr>
        <attr name="linewidth" format="float"></attr>
        <attr name="scrollable" format="boolean"></attr>
    </declare-styleable>
</resources>

由于项目需要,该控件中的scrollable功能默认设置是true的,如果改为false,那么该控件就变成只能选择最大值和最小值。
该控件还做了滑动处理,当滑动到刻度中间时就自动滑动到附近最近的线上面。该部分的处理在该控件的事件处理当中,代码如下:

@Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_POINTER_DOWN:
                if (event.getX() > PADDING_LEFT + widthOfProgress || event.getX() < PADDING_LEFT){
                    isCanMove = false;
                }else{
                    isCanMove = true;

                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (!isCanMove) {
                    return false;
                }
                float x = event.getX();

                if (x<PADDING_LEFT || x>widthOfProgress){

                    return false;
                }
                if (scrollable){
                    xProgress =  x;
                    progrees = minProgress+Math.round((xProgress-PADDING_LEFT)/widthOfItem)*oneItemValue;
                    if (onRangeRulerChangeListener != null){
                        onRangeRulerChangeListener.onValueChanged((int)progrees);
                    }
                    invalidate();
                }else{
                    if (x < widthOfProgress / 2){
                        xProgress = PADDING_LEFT;
                    }else{
                        xProgress = widthOfProgress;
                    }
                    progrees = minProgress+Math.round((xProgress-PADDING_LEFT)/widthOfItem)*oneItemValue;
                    if (onRangeRulerChangeListener != null){
                        onRangeRulerChangeListener.onValueChanged((int)progrees);
                    }
                    invalidate();

                }

                break;
            case MotionEvent.ACTION_POINTER_UP:
            case MotionEvent.ACTION_UP:
                float x1 = event.getX();
                if (scrollable){
                    int number = Math.round((x1 - PADDING_LEFT)/ widthOfItem);
                    if (number < 0){
                        number = 0;

                    }else if (number >= numberOfItem){
                        number = numberOfItem;
                    }
                    xProgress = PADDING_LEFT + number * widthOfItem;
                    invalidate();
                }else{

                    if (x1  >= widthOfProgress/2){
                        xProgress = PADDING_LEFT + numberOfItem * widthOfItem;
                    }else{
                        xProgress = PADDING_LEFT;
                    }
                    invalidate();
                }
                progrees = minProgress+Math.round((xProgress-PADDING_LEFT)/widthOfItem)*oneItemValue;
                if (onRangeRulerChangeListener != null){
                    onRangeRulerChangeListener.onValueChanged((int)progrees);
                }


                break;


        }
        return true;
    }

最后放上Main的逻辑,可以用来设置该控件的默认值和一些属性:
Main.java方面:

public class MainActivity extends AppCompatActivity {
    private RangeSeekBar rangeSeekBar;
    private TextView tv1;
    private RangeSeekBar dayView;
    private TextView tvDay;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        rangeSeekBar = (RangeSeekBar)findViewById(R.id.seekbar_money);
        tv1 = (TextView)findViewById(R.id.tv_money);
        dayView = (RangeSeekBar) findViewById(R.id.seekbar_day);
        tvDay = (TextView) findViewById(R.id.tv_day);

        rangeSeekBar.setOnRangeRulerChangeListener(new OnRangeRulerChangeListener() {
            @Override
            public void onValueChanged(int value) {
                tv1.setText(value+"");
            }
        });
        //设置当前值一定要在设置了监听之后
        rangeSeekBar.setCurrentProgress(500);
        dayView.setOnRangeRulerChangeListener(new OnRangeRulerChangeListener() {
            @Override
            public void onValueChanged(int value) {
                tvDay.setText(value+"");
            }
        });
    }

}

xml方面:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app = "http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#f0f0f0">

    <LinearLayout

        android:gravity="center_horizontal"
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:layout_height="0dp"
        android:layout_weight="4">
        <TextView
            android:layout_centerHorizontal="true"
            android:layout_marginTop="10dp"
            android:id="@+id/tv_credirLine"
            android:gravity="center_vertical"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""
            android:textSize="17sp"/>

        <RelativeLayout
            android:layout_marginTop="15dp"
            android:id="@+id/rl_money"
            android:gravity="center_horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <ImageView
                android:layout_centerVertical="true"
                android:id="@+id/iv_money"
                android:layout_width="20dp"
                android:layout_height="20dp"
                />
            <TextView
                android:layout_marginLeft="15dp"
                android:layout_toRightOf="@+id/iv_money"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="借款金额"
                android:textSize="16sp"/>
        </RelativeLayout>
        <RelativeLayout
            android:padding="15dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            >
            <TextView
                android:layout_centerHorizontal="true"
                android:id="@+id/tv_money"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="300"
                android:textSize="18sp"/>

            <TextView
                android:layout_marginLeft="15dp"
                android:layout_centerHorizontal="true"
                android:layout_toRightOf="@+id/tv_money"
                android:id="@+id/tv_1"
                android:layout_centerVertical="true"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#333333"
                android:text="元"
                android:textSize="15sp"/>
        </RelativeLayout>
        <RelativeLayout
            android:layout_marginTop="15dp"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            >
            <com.moyu.wh.testcustomseekbar.customview.RangeSeekBar
                android:id="@+id/seekbar_money"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:line_color="#dddddd"
                app:text_color="#dddddd"/>
            <!--<ImageView-->
                <!--android:layout_centerInParent="true"-->
                <!--android:layout_width="15dp"-->
                <!--android:layout_height="50dp"-->
                <!--android:layout_alignParentBottom="true"-->
                <!--android:src="@drawable/ic_scoll_line"-->
                <!--/>-->
        </RelativeLayout>
    </LinearLayout>


    <LinearLayout
        android:layout_marginTop="50dp"
        android:gravity="center_horizontal"
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:layout_height="0dp"
        android:layout_weight="3">
        <RelativeLayout
            android:layout_marginTop="25dp"
            android:id="@+id/rl_day"
            android:gravity="center_horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <ImageView
                android:layout_centerVertical="true"
                android:id="@+id/iv_day"
                android:layout_width="24dp"
                android:layout_height="24dp"
                />
            <TextView
                android:layout_marginLeft="10dp"
                android:layout_toRightOf="@+id/iv_day"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="借款期数"
                android:textSize="16sp"/>
        </RelativeLayout>
        <RelativeLayout
            android:padding="15dp"
            android:gravity="center_horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView
                android:layout_centerHorizontal="true"
                android:id="@+id/tv_day"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="7"
                android:textSize="18sp"/>

            <TextView
                android:layout_centerVertical="true"
                android:layout_marginLeft="3dp"
                android:layout_toRightOf="@+id/tv_day"
                android:layout_centerHorizontal="true"
                android:id="@+id/tv_2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#333333"
                android:text="天"
                android:textSize="15sp"/>
        </RelativeLayout>

        <LinearLayout

            android:id="@+id/ll_day"

            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:orientation="horizontal">
            <com.moyu.wh.testcustomseekbar.customview.RangeSeekBar
                android:id="@+id/seekbar_day"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:max_progress="14"
                app:min_progress="7"
                app:one_group_value="7"
                app:oneitem_value="1"
                app:scrollable="false"
                app:current_progress="7"
                app:line_color="#dddddd"
                app:text_color="#dddddd"/>
        </LinearLayout>
    </LinearLayout>
    <LinearLayout
        android:layout_marginTop="50dp"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        >


    </LinearLayout>

</LinearLayout>

谢谢阅读,如果有问题,请提出,我必定会虚心请教。代码可能会有我没有发现的问题,敬请见谅。
Demo位置(CSDN)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值