给SeekBar的thumb设置点击事件

昨天朋友问我了解不了解SeekBar,他说想实现两个功能:

  1. 禁止SeekBar手动滑动thumb
  2. 给SeekBar的thumb设置点击事件,但是没有现成的方法

当时我还这没研究过SeekBar(说实话当时我还不了解SeekBar和thumb到底是什么。。。随手查了下才知道是SeekBar是进度条,thumb就是进度条上的滑块),所以决定动手实现下。

SeekBar的使用

由于没有使用过SeekBar所以先从网上查了下SeekBar的用法,用法还是比较简单的,在xml布局文件中使用方法如下:

<SeekBar
     android:id="@+id/seek_bar"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:thumb="@drawable/round"/>

然后可以调用setProgress方法动态设置进度

屏蔽点击和拖动滑块

重写SeekBar,让onTouchEvent方法返回true即可

@Override
public boolean onTouchEvent(MotionEvent event) {
    return true;
}

给thumb设置点击事件

因为thumb不是View,所以不能直接给thumb设置点击事件,那么thumb是什么?
看源码:
这里写图片描述
从源码中可以看出thumb是Drawable
那么就好办了,因为我们可以通过getBounds方法得到Drawable上下左右四个位置坐标,所以可以通过判断手指落点是否在thumb上来判断点击事件是否发生

于是写出了如下代码:

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
            break;
        case MotionEvent.ACTION_MOVE:
            break;
        case MotionEvent.ACTION_UP:
            if (isTouchInThumb(event, getThumb().getBounds())){
                if (listener != null)
                    listener.onClickThumb();
            }
            break;
    }
    return true;
}

/**
 * 判断MotionEvent事件是否位于thumb上
 * @param event
 * @param thumbBounds
 * @return
 */
private boolean isTouchInThumb(MotionEvent event, Rect thumbBounds){
    float x = event.getX();
    float y = event.getY();
    if (x >= thumbBounds.left && x <= thumbBounds.right
            && y >= thumbBounds.top && y <= thumbBounds.bottom)
        return true;
    return false;
}
public void setOnClickThumbListener(OnClickThumbListener listener){
    this.listener = listener;
}

interface OnClickThumbListener{
    void onClickThumb();
}

然后美滋滋的去运行了,结果却不尽如人意
点击的时候发现手指没在thumb上而是向右偏移很多仍能触发点击事件。。。。
示意图如下:
这里写图片描述
这就让人很尴尬了!难道是我设置的图片有很宽透明边缘?
那就换张图片试试,结果证明并不是图片的原因,更换图片以后问题依旧复现
那问题到底在哪里?
迫不得已我绘制了一下drawable所在矩形的边缘,代码如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Rect rect = getThumb().getBounds();
    canvas.drawRect(rect, new Paint());
}

结果发现了一个惊天大秘密
这里写图片描述
这是什么鬼???矩形和图片不是应该重合才对吗?
静下心来一想,这并非偶然,先看一张SeekBar的初始状态图
这里写图片描述
我们发现thumb会被遮住一部分,再对比一下绘制的矩形初始状态
这里写图片描述
原来drawable的左边界和SeekBar的左边界是重合的(有人可能会说,SeekBar的左边是没有贴到屏幕左侧的,而这个矩形已经贴到屏幕左侧了,怎么会是重合的,其实看到的进度条的左边界并不是SeekBar的左边界,通过给SeekBar设置一个背景颜色就能直观的看出来,我这里就不多做演示)
那么我可以做进一步猜想,SeekBar可能是以drawable的位置为基础进行了偏移,为了寻找依据,我不得不查看源码。
无奈之下又去查看源码,发现绘制thumb时的坐标会受到两个值的影响,分别是mPaddingLeft和的mThumbOffset,下面针对这两个变量进行分析。
mPaddingLeft控制进度条起始点的位置,mThumbOffset设置thumb相对于进度条起始点的相对位置。

public void setThumb(Drawable thumb) {
    ...
    // Assuming the thumb drawable is symmetric, set the thumb offset
    // such that the thumb will hang halfway off either edge of the
    // progress bar.
    mThumbOffset = thumb.getIntrinsicWidth() / 2;
    ...
}

从上述代码中可以看出在设置thumb时同时会设置一个初始偏移量,这个值是thumb宽度的二分之一,这个是为了保证自定义thumb水平方向的中间位置能正好位于SeekBar进度条的起始位置,如果不设置偏移量的话图片的左边缘会位于进度条的起始位置。

再看一下绘制thumb的部分

/**
 * Draw the thumb.
 */
void drawThumb(Canvas canvas) {
    if (mThumb != null) {
        final int saveCount = canvas.save();
        // Translate the padding. For the x, we need to allow the thumb to
        // draw in its extra space
        canvas.translate(mPaddingLeft - mThumbOffset, mPaddingTop);
        mThumb.draw(canvas);
        canvas.restoreToCount(saveCount);
    }
}

在绘制thumb时把画布的原点坐标移动到了(mPaddingLeft - mThumbOffset, mPaddingTop)
于是修改如下代码:

/**
 * 判断MotionEvent事件是否位于thumb上
 * @param event
 * @param thumbBounds
 * @return
 */
private boolean isTouchInThumb(MotionEvent event, Rect thumbBounds){
    float x = event.getX();
    float y = event.getY();
    //根据偏移量和左边距确定thumb位置
    int left = thumbBounds.left - getThumbOffset() + getPaddingLeft();
    int right = left + thumbBounds.width();
    if (x >= left && x <= right
            && y >= thumbBounds.top && y <= thumbBounds.bottom)
        return true;
    return false;
}

再运行时发现问题已经解决,到此就完成了需要的两个功能
源码地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值