动态改变SeekBar

灯光SeekBar

这个页面主要功能是RecyclerView展示各个灯光卡片,选择色盘,亮度调节,整体算法联动控制的效果。

1. 色盘

首先我们来讲解下这个色盘吧。这个页面除了亮度算法,就是颜色的控制啦。
想搞出色盘,能够准确的计算出色值,需要搞清楚这几个属性:

  • 色调H:0-360
    从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,紫色为300°

  • 饱和度S:0%-100%
    饱和度高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,值越大,颜色越饱和。

  • 明度V:0%-100%
    明度表示颜色明亮的程度,通常取值范围为0%(黑)到100%(白)。

  • 亮度B:0%-100%
    亮度表示颜色明亮的程度,通常取值范围为0%(黑)到100%(白)。

  • 色温T:0%-100%
    色温表示光谱能量分布的指标,色温高,光谱能量分布于短波的成分略多,颜色偏蓝。色温低,光谱能量分布于长波的成分略多,颜色偏黄。

即便理解了这几个属性,是不是也还是一脸懵逼呀?

日常生活中用到的,像有的灯具只有亮度可以调节,有的灯具可以调节亮度和冷暖色温,有的灯具可以调节色调,饱和度和亮度,功能再多点的灯,可以调节色调+饱和度+亮度+色温。

对应到UI色盘上的关系,或许可以让你更多的理解这几个属性,更清楚的知道怎么去控制这几个值。

从图上我们可以看出,色盘由圆形和长方形2种,各自对应的关系我已经标注的比较清楚了。
圆形相对与长方形来说,它需要根据弧度来确定色调H,根据点的位置和利用勾股定理来确定饱和度S。
画这个UI图对应到各个HSVBT值还比较简单,可以通过Color来计算和转换,比如HSVToColor,colorToHSV,RGBToHSV等,比较繁琐的是灯具不同,硬件上的值和各自转换算法不同,下发到灯具上的颜色亮度也不同,这个保密就不做介绍了。

2. 动画

从最上面的操作录屏中,你可以看到,SeekBar亮度调节的时候还有一些动画,为了让用户有圆润的动画体验,这里使用了弹性动画。

弹性动画这里选择了interpolator插值器的实现方式。

public class SpringScaleInterpolator implements Interpolator {
    //弹性因数
    private float factor;

    public SpringScaleInterpolator(float factor) {
        this.factor = factor;
    }

    @Override
    public float getInterpolation(float input) {
        return (float) (Math.pow(2, -10 * input) * Math.sin((input - factor / 4) * (2 * Math.PI) / factor) + 1);
    }
}

从代码上看,可能你不清楚弹性因子和代码里的数字各自影响的动画效果是什么样的,那么就从这个网站查看效果吧。

关于差值器网站

在这个网站上可以在线看每种interpolator的效果,从而调节数字来选择所需要的interpolator。

factor的值越小,值来回变化的次数越多,对应到具体的动画就是:factor值越小,view来回缩放的次数越多,平移到指定位置后在指定位置上下或左右摆动的次数也越多

具体的弹性动画方面,还有其他的实现方式,可自行查看,这里不做详细介绍。

3. seekbar

从录屏中可以看一下,SeekBar的特点,除了动画效果外,还有圆角处理+颜色动态变换+透明度+色值渐变。每一个HSVBT值的变化,都需要计算color,来改变SeekBar和卡片的颜色,同时计算整体亮度。

  • SeekBar固定进度条颜色和圆角

SeekBar的进度条和滑块可以通过设置progressDrawable与thumb,这样设置会是固定的,从录屏上看我们需要的是动态的更改渐变颜色,100%的时候更改圆角。
我们先从progressDrawable和thumb开始吧。

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!--定义seekbar滑动条的底色-->
    <item android:id="@android:id/background">
        <shape>
            <solid android:color="#39000000" />
        </shape>
    </item>
<!--定义seekbar滑动条第二背景颜色-->
<!--    <item android:id="@android:id/secondaryProgress">-->
<!--        <clip>-->
<!--            <shape>-->
<!--                <corners android:radius="5dp" />-->
<!--                <gradient-->
<!--                    android:startColor="#80ffd300"-->
<!--                    android:centerColor="#80ffb600"-->
<!--                    android:centerY="0.75"-->
<!--                    android:endColor="#a0ffcb00"-->
<!--                    android:angle="270"/>-->
<!--            </shape>-->
<!--        </clip>-->
<!--    </item>-->
    <!--定义seekbar滑动条进度颜色-->
    <item android:id="@android:id/progress">
        <scale android:scaleWidth="100%" >
            <shape android:shape="rectangle">
                <gradient
                    android:angle="180"
                    android:endColor="@color/transparent"
                    android:startColor="@color/white" />
                <corners android:bottomRightRadius="7dp" android:topRightRadius="7dp"/>
            </shape>
        </scale>
    </item>
</layer-list>

UI图上没有第二背景,这里就把它添加上并写了注释,有个细节的点需要了解下,就是scale与clip的区别。
先使用clip对比看一下UI:

<clip>
    <shape android:shape="rectangle">
        <gradient
              android:angle="180"
              android:endColor="@color/transparent"
              android:startColor="@color/white" />
        <corners android:bottomRightRadius="@dimen/dp_7" android:topRightRadius="@dimen/dp_7"/>
    </shape>
</clip>

展示如下:

使用clip从截图来看,没有了圆角。

  • scale能够将drawable进行缩放,通过其scaleWidth和scaleHeight属性,可设置drawable缩放的比例。
  • 使用clip标签可实现对drawable的裁剪,用法与scale类似。通过在代码中调用getBackground()获取Drawable后,通过setLevel(int level)方法,可控制裁剪的程度。

滑块部分可自定查看文末的参考文档,在xml里设置android:thumb="@drawable/seekbar_thumb"

  • SeekBar动态设置颜色和圆角

我们可以使用getProgressDrawable得到进度背景LayerDrawable,它包含多个层次,对应上面layer-list设置的3个层次,可以通过Id获取各自的Drawable。

//获取seerbar层次drawable对象
LayerDrawable layerDrawable = (LayerDrawable) seekBar.getProgressDrawable();
// 有多少个层次(最多三个)
int layers = layerDrawable.getNumberOfLayers();
Drawable[] drawables = new Drawable[layers];
for (int i = 0; i < layers; i++) {
    switch (layerDrawable.getId(i)) {
        // seekbar背景
        case android.R.id.background:
            drawables[i] = layerDrawable.getDrawable(0);
            break;
        // 拖动条第一进度
        case android.R.id.progress:
            //动态设颜色值
            drawables[i] = new PaintDrawable(progressColor);
         drawables[i].setBounds(layerDrawable.getDrawable(0).getBounds());
            break;
        ...
    }
}
seekBar.setProgressDrawable(new LayerDrawable(drawables));
seekBar.invalidate();

由于我的UI只progress颜色在变化,可以直接取LayerDrawable的Drawable第1个Drawable,转变为GradientDrawable,进行设置渐变颜色和圆角

LayerDrawable layerDrawable = (LayerDrawable) seekBar.getProgressDrawable();
GradientDrawable gradientDrawable = (GradientDrawable)((ScaleDrawable)layerDrawable.getDrawable(1)).getDrawable();
//设置渐变颜色
int[] gradientColors = {mContext.getResources().getColor(R.color.white), mContext.getResources().getColor(R.color.transparent)};
gradientColors[1] = newColor;
gradientDrawable.setColors(gradientColors);

//设置圆角
float gradientRadius = mContext.getResources().getDimension((seekBar.getProgress() == 100) ? 0dp : 7dp);
float[] radius = {0f, 0f, gradientRadius, gradientRadius, gradientRadius, gradientRadius, 0f, 0f};
gradientDrawable.setCornerRadii(radius);

seekBar.setProgressDrawable(layerDrawable);
seekBar.invalidate();

这里需要了解下,用shape标签定义的xml,最终都是转化为GradientDrawable对象,而不是ShapeDrawable, 也不是类型对应的 OvalShape,RoundRectShape等。
GradientDrawable可以动态设置跟xml文件中android:shape的值一一对应的类型。

从UI上看,seekBar设置过颜色的同时,卡片的背景也需要设置,这里通过setColorFilter设置

Drawable itemBackground = itemBgView.getBackground();
itemBackground.setColorFilter(newColor, PorterDuff.Mode.SRC);

这里也有需要了解的点,PorterDuff.Mode.SRC,不做多介绍,详细的可以查看文末参考文档

从图上可以形象地说明了运用PorterDuff.Mode进行图像合成的作用,两个图形一圆一方通过一定的计算产生了不同的合成效果,我们在实际工作中需要做图片处理时可以参考这张图的示意快速地选择合适的Mode。

4. 缓存问题

单个使用SeekBar是没问题的,放在了RecyclerView里,动态的设置颜色发现是有问题的,他们相互影响显示同一个颜色,最终翻看源码,发现是Drawable资源缓存的问题。详细的源码可以查看ResourcesImpl里loadDrawable部分。
避免这个问题,可以通过setProgressDrawable设置SeekBar的LayerDrawable。

int progressDrawableRes = R.drawable.seekbar_progress_drawable;
LayerDrawable layerDrawable = (LayerDrawable)ContextCompat.getDrawable(mContext, progressDrawableRes).getConstantState().newDrawable().mutate();
seekBar.setProgressDrawable(layerDrawable);

5. notifyDataSetChanged问题

这里有个问题需要注意,从最上面的录屏看,滑动每一个SeekBar,动画执行的同时都需要动态的更新数据,实时的更新整体亮度,notifyDataSetChanged的时机需要跟动画结合,因为RecyclerView的缓存机制,可能动画执行一半,SeekBar对象就变了。

就这样吧,算是总结记录下最近搞的UI部分吧。

参考文档:
Android drawable资源分析
动态改变SeekBar进度条颜色与滑块颜色
GradientDrawable(shape标签定义) 静态使用和动态使用(圆角,渐变实现)
各个击破搞明白PorterDuff.Mode

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容