首页 > 代码库 > 管窥Android中的滑动条SeekBar的父类AbsSeekBar的源码

管窥Android中的滑动条SeekBar的父类AbsSeekBar的源码

  Android中的控件中有一类是ProgressBar,其子类中有一个是AbsSeekBar。相信有不少童鞋对这个拖动条的父类比较感兴趣吧!尤其 是看到网易云音乐的进度条上面是可以处理播放与暂停事件,是不是很羡慕的哈~  俺在这里告诉大家,不用羡慕,看了我下面的代码分析,你也是可以做出那样的效果的哦。Let‘s go.

       下面先给大家列表一下AbsSeekBar的成员变量有哪些。

       

[java] view plaincopyprint?技术分享技术分享
  1. //当前的矩形  
  2. private final Rect mTempRect = new Rect();  
  3. //可以拖动的滑块  
  4. private Drawable mThumb;  
  5. //颜色的状态的列表  
  6. private ColorStateList mThumbTintList = null;  
  7. //对应的端口的融合  
  8. private PorterDuff.Mode mThumbTintMode = null;  
  9. //是否支持的欢快的tint  
  10. private boolean mHasThumbTint = false;  
  11. //对应的是滑块的模式  
  12. private boolean mHasThumbTintMode = false;  
  13. //滑块的偏移量  
  14. private int mThumbOffset;  
  15. //是否进行分割追踪  
  16. private boolean mSplitTrack;  

在变量中我们大致需要知道一下几点:

1、mTempRect是与SeekBar整个轨迹绘制相关的变量

2、mThumb是SeekBar上面的滑块的Drawable的图片

3、mThumbOffset是滑块是距离x左边距的距离


对于AbsSeekBar的成员方法,下面选取一个比较重要的,在实际的开发工作中经常用到的几个方法给大家讲解一下。

1、setThumbOffset

这个方法是设置滑块距离左边距的位置

2、

[java] view plaincopyprint?技术分享技术分享
  1. public synchronized void setMax(int max) {  
  2.         super.setMax(max);  
  3.         if ((mKeyProgressIncrement == 0) || (getMax() / mKeyProgressIncrement > 20)) {  
  4.             // It will take the user too long to change this via keys, change it  
  5.             // to something more reasonable  
  6.             //设置为比较合理的数值  
  7.             setKeyProgressIncrement(Math.max(1, Math.round((float) getMax() / 20)));  
  8.         }  
  9.     }  


这个方法实际关联到两个功能。

1、设置当前的SeekBar的最大值

2、由于存在部分手机有向左的按键与向右的按键,就比如我曾经遇到过的一款三星的商务机。按照源码的逻辑,控制左按键与右按键一次位移的边距不要超过20.

关于轨迹的绘制与滑块的绘制的更新,主要关注下面的一段代码

[java] view plaincopyprint?技术分享技术分享
  1. if (track != null) {  
  2.             track.setBounds(0, trackOffset, w - mPaddingRight - mPaddingLeft,  
  3.                     h - mPaddingBottom - trackOffset - mPaddingTop);  
  4.         }  
  5.         if (thumb != null) {  
  6.             setThumbPos(w, thumb, getScale(), thumbOffset);  
  7.         }  


轨迹的绘制比较重要,我们一起看看吧;

[java] view plaincopyprint?技术分享技术分享
  1. void drawTrack(Canvas canvas) {  
  2.         //获取当前滑块的引用  
  3.         final Drawable thumbDrawable = mThumb;  
  4.         if (thumbDrawable != null && mSplitTrack) {  
  5.             final Insets insets = thumbDrawable.getOpticalInsets();  
  6.             final Rect tempRect = mTempRect;  
  7.             thumbDrawable.copyBounds(tempRect);  
  8.             tempRect.offset(mPaddingLeft - mThumbOffset, mPaddingTop);  
  9.             tempRect.left += insets.left;  
  10.             tempRect.right -= insets.right;  
  11.   
  12.             final int saveCount = canvas.save();  
  13.             //对当前的矩形进行裁剪  
  14.             canvas.clipRect(tempRect, Op.DIFFERENCE);  
  15.             super.drawTrack(canvas);  
  16.             canvas.restoreToCount(saveCount);  
  17.         } else {  
  18.             super.drawTrack(canvas);  
  19.         }  
  20.     }  

针对上面的代码块,主要讲下面的几点:

1、mSlitTrack这个变量存在的原因是,我们通常遇到滑块的左右的颜色不一样,这个变量就是起到分割左右两边的目的。

滑块的绘制也是比较重要的哦,下面也一起来看看吧:

[java] view plaincopyprint?技术分享技术分享
  1. void drawThumb(Canvas canvas) {  
  2.         if (mThumb != null) {  
  3.             canvas.save();  
  4.             // Translate the padding. For the x, we need to allow the thumb to  
  5.             // draw in its extra space  
  6.             //主要是x轴上面的变化  
  7.             canvas.translate(mPaddingLeft - mThumbOffset, mPaddingTop);  
  8.             //绘制一些Canvas的对象  
  9.             mThumb.draw(canvas);  
  10.             canvas.restore();  
  11.         }  
  12.     }  


我们知道,滑块的位置会随着进度而不断的位移,而绘制的本质实际上都利用了画布类Canvas,因此本质上是画布在不断的进行位移。也就是这一行代码的含义:

[java] view plaincopyprint?技术分享技术分享
  1. canvas.translate(mPaddingLeft - mThumbOffset, mPaddingTop);  

下面的内容主要是讲解AbsSeekBar是如何处理touch事件,而我上面的所说的网易云音乐如果做到点击进度条实现播放与暂停的效果也与下面的讲解有关:

与上面一样,咱们先上代码:

[java] view plaincopyprint?技术分享技术分享
  1. public boolean onTouchEvent(MotionEvent event) {  
  2.         if (!mIsUserSeekable || !isEnabled()) {  
  3.             return false;  
  4.         }  
  5.   
  6.         switch (event.getAction()) {  
  7.             case MotionEvent.ACTION_DOWN:  
  8.                 if (isInScrollingContainer()) {  
  9.                     mTouchDownX = event.getX();  
  10.                 } else {  
  11.                     //设置当前的状态是处于按下的状态  
  12.                     setPressed(true);  
  13.                     if (mThumb != null) {  
  14.                         invalidate(mThumb.getBounds()); // This may be within the padding region  
  15.                     }  
  16.                     onStartTrackingTouch();  
  17.                     trackTouchEvent(event);  
  18.                     attemptClaimDrag();  
  19.                 }  
  20.                 break;  
  21.   
  22.             case MotionEvent.ACTION_MOVE:  
  23.                 if (mIsDragging) {  
  24.                     trackTouchEvent(event);  
  25.                 } else {  
  26.                     final float x = event.getX();  
  27.                     //超过一定的状态  
  28.                     if (Math.abs(x - mTouchDownX) > mScaledTouchSlop) {  
  29.                         setPressed(true);  
  30.                         if (mThumb != null) {  
  31.                             invalidate(mThumb.getBounds()); // This may be within the padding region  
  32.                         }  
  33.                         onStartTrackingTouch();  
  34.                         trackTouchEvent(event);  
  35.                         attemptClaimDrag();  
  36.                     }  
  37.                 }  
  38.                 break;  
  39.   
  40.             case MotionEvent.ACTION_UP:  
  41.                 if (mIsDragging) {  
  42.                     trackTouchEvent(event);  
  43.                     onStopTrackingTouch();  
  44.                     setPressed(false);  
  45.                 } else {  
  46.                     // Touch up when we never crossed the touch slop threshold should  
  47.                     // be interpreted as a tap-seek to that location.  
  48.                     onStartTrackingTouch();  
  49.                     trackTouchEvent(event);  
  50.                     onStopTrackingTouch();  
  51.                 }  
  52.                 // ProgressBar doesn‘t know to repaint the thumb drawable  
  53.                 // in its inactive state when the touch stops (because the  
  54.                 // value has not apparently changed)  
  55.                 invalidate();  
  56.                 break;  
  57.   
  58.             case MotionEvent.ACTION_CANCEL:  
  59.                 if (mIsDragging) {  
  60.                     onStopTrackingTouch();  
  61.                     setPressed(false);  
  62.                 }  
  63.                 invalidate(); // see above explanation  
  64.                 break;  
  65.         }  
  66.         return true;  
  67.     }  


上面的代码,各位看官不要着急,且听在下一一道来:

1、如果当前的SeekBar已经设置了不能够touch操作,废话不用多说,直接return。

2、按照源码的解释,当当前的控件处于按下的状态,主要进行下面的处理:

2、1  设置当前的状态为Press的状态

2、2  刷新当前的视图


如果我们需要仿造网易云音乐,需要处理暂停音乐的逻辑,

需要注意两点

1、判断当前的event的x的坐标是否是在滑块的内部,如果是,不论当前的位移是多少,均不要改变当前的进度

2、修改当前的滑块的图片

好了,整个的源码的讲解就到这里了,相信大家看到这里,对Android中的SeekBar有了比以前更进一步的了解了吧!

管窥Android中的滑动条SeekBar的父类AbsSeekBar的源码