写在开始之前
在Android的色彩处理中,我们通常用三个角度来描述一个图像:
色调: 图像的颜色
饱和度:颜色的纯度,从0(灰)到100%(饱和)来进行描述
亮度:颜色的相对明暗程度
在上面三个属性中,饱和度和亮度为0会使得图片看起来是纯黑色。(记住这一点)
本篇源码分析的原因就是来自这个问题。
正文
在Android开发的过程中,大家有可能都使用过SeekBar这个控件,比如拖动视频进度条、音频进度条等。不管大家用的多还是少,由于工作原因,个人用到的还是比较少的。然后最近在看书的时候,书中为了直观的展示颜色矩阵(ColorMatrix)的变换,有一段代码是通过SeekBar拖动来实时修改图像。
demo的样式就是下图展示的这样:
然后这段代码也很简单,
- 实现一个
OnSeekBarChangeListener
接口; - 给
SeekBar
设置setOnSeekBarChangeListener()
的监听; - 重写
onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
方法即可。
事实上,我们实际开发过程中也是这样处理的。
那么看下面代码:
|
|
在initSeekBarProperty()
方法中为Seekbar设置当前要显示的进度,并且设置进度条改变的监听。
然后运行Demo,只拖动控制色调的Seekbar,是不是以为大功告成了?我发现此时图片变成了黑色。
想到我们在上面的拓展,当饱和度和亮度为0时,图片是会变成黑色背景。那么我们调试一下代码,来验证下是不是这样,调试代码如图:
调试验证了我们的猜想。
从网上搜索答案,解决方案是:
将
setOnSeekBarChangeListener()
监听放在setProgress()
之前
然后将设置监听和设置进度的顺序调换了一下,果真没有问题了。
源码部分
那么真正的原因是什么呢?我们从源码的角度来简单剖析一下这个问题。
首先说一下Seekbar的继承关系:
首先看一下监听回调的方法:void onProgressRefresh(float scale, boolean fromUser, int progress)
进入SeekBar的源码,可以看到onProgressRefresh()
方法源码如下:
|
|
SeekBar
的onProgressRefresh()
方法里面是先执行了父类的onProgressRefresh()
方法,先看AbsSeekBar
,在AbsSeekBar
中是没有onProgressRefresh()
方法的,说明SeekBar
执行的是ProgresssBar
中的onProgressRefresh()
方法,
源码如下:
|
|
看一下,哪些地方调用了这个方法,发现只有在doRefreshProgress()
方法中被调用,看一下这个方法的源码如下:
|
|
可以看到在这个方法中,fromUser这个参数也是传过来的,看一下哪些地方调用了该方法。
|
|
再查看该方法被调用的地方,可以看到有这个一个方法调用了该方法,源码如下:
|
|
原来是在setProgressInternal()
这里被调用的,看方法名字就知道,意思是内部设置进度值,我们再看看这个方法是在哪里被调用的。
|
|
看到这里,终于看到了一个熟悉的方法,这个setProgress()
就是我们在初始化的时候给seekbar
设置当前进度的方法,这个方法实际上就调用了setProgressInternal()
方法。第二个fromUser
参数就是通过这个方法一层层分发下去。然后看到这个值为false,你会不会有点想法:什么时候这个值为true呢?
答案就是当我们拖动seekbar的时候。
一提到拖动,你是不是想到了onTouchEvent()
事件分发?我们来看一下源码,发现只有在AbsSeekBar中重写了onTouchEvent()
方法,源码如下:
|
|
这里注意两个方法,startDrag()
和trackTouchEvent()
|
|
我们发现在startDrag()
中也调用了trackTouchEvent()
方法,然后可以看到在trackTouchEvent()
最后是调用了setProgressInternal()
方法去设置seekbar
的进度值,并且,这个方法的第二个参数传值为true。
到这里我们基本上就能明白:
- 当我们通过
setProgress()
设置进度时,这个时候fromUser
传值为false; - 当我们拖动
seekbar
时,fromUser
传值为true;
那么回到我们最开始的问题,为什么需要先设置监听呢? 答案在SeekBar
的onProgressChanged()
方法中
|
|
总结
- 当我们先通过
setProgress()
设置进度时,此时回调到onProgressChanged()
方法时,由于mOnSeekBarChangeListener == null
, 所以不会去执行我们重写的onProgressChange()
方法,自然也就不会去改变色调、饱和度和亮度这几个的值,由于在初始化的时候,这三个值默认为0.f,然后当饱和度和亮度为0的时候,图片会变成黑色。- 如果我们先设置监听,再去通过
setProgress()
设置进度,此时由于mOnSeekBarChangeListener != null
就可以回调到onProgressChanged()
方法中修改三个变量的值。