這個(gè)頁(yè)面主要功能是RecyclerView展示各個(gè)燈光卡片汗洒,選擇色盤(pán)珠漂,亮度調(diào)節(jié),整體算法聯(lián)動(dòng)控制的效果笼才。
1. 色盤(pán)
首先我們來(lái)講解下這個(gè)色盤(pán)吧漱受。這個(gè)頁(yè)面除了亮度算法,就是顏色的控制啦骡送。
想搞出色盤(pán)昂羡,能夠準(zhǔn)確的計(jì)算出色值絮记,需要搞清楚這幾個(gè)屬性:
色調(diào)H:0-360
從紅色開(kāi)始按逆時(shí)針?lè)较蛴?jì)算,紅色為0°虐先,綠色為120°怨愤,藍(lán)色為240°。它們的補(bǔ)色是:黃色為60°蛹批,青色為180°撰洗,紫色為300°飽和度S:0%-100%
飽和度高,顏色則深而艷腐芍。光譜色的白光成分為0差导,飽和度達(dá)到最高。通常取值范圍為0%~100%猪勇,值越大柿汛,顏色越飽和。明度V:0%-100%
明度表示顏色明亮的程度埠对,通常取值范圍為0%(黑)到100%(白)络断。亮度B:0%-100%
亮度表示顏色明亮的程度,通常取值范圍為0%(黑)到100%(白)项玛。色溫T:0%-100%
色溫表示光譜能量分布的指標(biāo)貌笨,色溫高,光譜能量分布于短波的成分略多襟沮,顏色偏藍(lán)锥惋。色溫低,光譜能量分布于長(zhǎng)波的成分略多开伏,顏色偏黃膀跌。
即便理解了這幾個(gè)屬性,是不是也還是一臉懵逼呀固灵?
日常生活中用到的捅伤,像有的燈具只有亮度可以調(diào)節(jié),有的燈具可以調(diào)節(jié)亮度和冷暖色溫巫玻,有的燈具可以調(diào)節(jié)色調(diào)丛忆,飽和度和亮度,功能再多點(diǎn)的燈仍秤,可以調(diào)節(jié)色調(diào)+飽和度+亮度+色溫熄诡。
對(duì)應(yīng)到UI色盤(pán)上的關(guān)系,或許可以讓你更多的理解這幾個(gè)屬性诗力,更清楚的知道怎么去控制這幾個(gè)值凰浮。
從圖上我們可以看出,色盤(pán)由圓形和長(zhǎng)方形2種,各自對(duì)應(yīng)的關(guān)系我已經(jīng)標(biāo)注的比較清楚了袜茧。
圓形相對(duì)與長(zhǎng)方形來(lái)說(shuō)屿良,它需要根據(jù)弧度來(lái)確定色調(diào)H,根據(jù)點(diǎn)的位置和利用勾股定理來(lái)確定飽和度S惫周。
畫(huà)這個(gè)UI圖對(duì)應(yīng)到各個(gè)HSVBT值還比較簡(jiǎn)單尘惧,可以通過(guò)Color來(lái)計(jì)算和轉(zhuǎn)換,比如HSVToColor递递,colorToHSV喷橙,RGBToHSV等,比較繁瑣的是燈具不同登舞,硬件上的值和各自轉(zhuǎn)換算法不同贰逾,下發(fā)到燈具上的顏色亮度也不同,這個(gè)保密就不做介紹了菠秒。
2. 動(dòng)畫(huà)
從最上面的操作錄屏中疙剑,你可以看到,SeekBar亮度調(diào)節(jié)的時(shí)候還有一些動(dòng)畫(huà)践叠,為了讓用戶有圓潤(rùn)的動(dòng)畫(huà)體驗(yàn)言缤,這里使用了彈性動(dòng)畫(huà)。
彈性動(dòng)畫(huà)這里選擇了interpolator插值器的實(shí)現(xiàn)方式禁灼。
public class SpringScaleInterpolator implements Interpolator {
//彈性因數(shù)
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);
}
}
從代碼上看管挟,可能你不清楚彈性因子和代碼里的數(shù)字各自影響的動(dòng)畫(huà)效果是什么樣的,那么就從這個(gè)網(wǎng)站查看效果吧弄捕。
關(guān)于差值器網(wǎng)站
在這個(gè)網(wǎng)站上可以在線看每種interpolator的效果僻孝,從而調(diào)節(jié)數(shù)字來(lái)選擇所需要的interpolator。
factor的值越小守谓,值來(lái)回變化的次數(shù)越多穿铆,對(duì)應(yīng)到具體的動(dòng)畫(huà)就是:factor值越小,view來(lái)回縮放的次數(shù)越多斋荞,平移到指定位置后在指定位置上下或左右擺動(dòng)的次數(shù)也越多
具體的彈性動(dòng)畫(huà)方面荞雏,還有其他的實(shí)現(xiàn)方式,可自行查看譬猫,這里不做詳細(xì)介紹讯檐。
3. seekbar
從錄屏中可以看一下,SeekBar的特點(diǎn)染服,除了動(dòng)畫(huà)效果外,還有圓角處理+顏色動(dòng)態(tài)變換+透明度+色值漸變叨恨。每一個(gè)HSVBT值的變化柳刮,都需要計(jì)算color,來(lái)改變SeekBar和卡片的顏色,同時(shí)計(jì)算整體亮度秉颗。
-
SeekBar固定進(jìn)度條顏色和圓角
SeekBar的進(jìn)度條和滑塊可以通過(guò)設(shè)置progressDrawable與thumb痢毒,這樣設(shè)置會(huì)是固定的,從錄屏上看我們需要的是動(dòng)態(tài)的更改漸變顏色蚕甥,100%的時(shí)候更改圓角哪替。
我們先從progressDrawable和thumb開(kāi)始吧。
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!--定義seekbar滑動(dòng)條的底色-->
<item android:id="@android:id/background">
<shape>
<solid android:color="#39000000" />
</shape>
</item>
<!--定義seekbar滑動(dòng)條第二背景顏色-->
<!-- <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滑動(dòng)條進(jìn)度顏色-->
<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圖上沒(méi)有第二背景菇怀,這里就把它添加上并寫(xiě)了注釋凭舶,有個(gè)細(xì)節(jié)的點(diǎn)需要了解下,就是scale與clip的區(qū)別爱沟。
先使用clip對(duì)比看一下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從截圖來(lái)看帅霜,沒(méi)有了圓角。
- scale能夠?qū)rawable進(jìn)行縮放呼伸,通過(guò)其scaleWidth和scaleHeight屬性身冀,可設(shè)置drawable縮放的比例。
- 使用clip標(biāo)簽可實(shí)現(xiàn)對(duì)drawable的裁剪括享,用法與scale類似搂根。通過(guò)在代碼中調(diào)用getBackground()獲取Drawable后,通過(guò)setLevel(int level)方法铃辖,可控制裁剪的程度兄墅。
滑塊部分可自定查看文末的參考文檔,在xml里設(shè)置android:thumb="@drawable/seekbar_thumb"
-
SeekBar動(dòng)態(tài)設(shè)置顏色和圓角
我們可以使用getProgressDrawable得到進(jìn)度背景LayerDrawable澳叉,它包含多個(gè)層次隙咸,對(duì)應(yīng)上面layer-list設(shè)置的3個(gè)層次,可以通過(guò)Id獲取各自的Drawable成洗。
//獲取seerbar層次drawable對(duì)象
LayerDrawable layerDrawable = (LayerDrawable) seekBar.getProgressDrawable();
// 有多少個(gè)層次(最多三個(gè))
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;
// 拖動(dòng)條第一進(jìn)度
case android.R.id.progress:
//動(dòng)態(tài)設(shè)顏色值
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個(gè)Drawable,轉(zhuǎn)變?yōu)镚radientDrawable瓶殃,進(jìn)行設(shè)置漸變顏色和圓角
LayerDrawable layerDrawable = (LayerDrawable) seekBar.getProgressDrawable();
GradientDrawable gradientDrawable = (GradientDrawable)((ScaleDrawable)layerDrawable.getDrawable(1)).getDrawable();
//設(shè)置漸變顏色
int[] gradientColors = {mContext.getResources().getColor(R.color.white), mContext.getResources().getColor(R.color.transparent)};
gradientColors[1] = newColor;
gradientDrawable.setColors(gradientColors);
//設(shè)置圓角
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標(biāo)簽定義的xml,最終都是轉(zhuǎn)化為GradientDrawable對(duì)象遥椿,而不是ShapeDrawable, 也不是類型對(duì)應(yīng)的 OvalShape基矮,RoundRectShape等。
GradientDrawable可以動(dòng)態(tài)設(shè)置跟xml文件中android:shape的值一一對(duì)應(yīng)的類型冠场。
從UI上看家浇,seekBar設(shè)置過(guò)顏色的同時(shí),卡片的背景也需要設(shè)置碴裙,這里通過(guò)setColorFilter設(shè)置
Drawable itemBackground = itemBgView.getBackground();
itemBackground.setColorFilter(newColor, PorterDuff.Mode.SRC);
這里也有需要了解的點(diǎn)钢悲,PorterDuff.Mode.SRC点额,不做多介紹,詳細(xì)的可以查看文末參考文檔
從圖上可以形象地說(shuō)明了運(yùn)用PorterDuff.Mode進(jìn)行圖像合成的作用莺琳,兩個(gè)圖形一圓一方通過(guò)一定的計(jì)算產(chǎn)生了不同的合成效果还棱,我們?cè)趯?shí)際工作中需要做圖片處理時(shí)可以參考這張圖的示意快速地選擇合適的Mode。
4. 緩存問(wèn)題
單個(gè)使用SeekBar是沒(méi)問(wèn)題的惭等,放在了RecyclerView里珍手,動(dòng)態(tài)的設(shè)置顏色發(fā)現(xiàn)是有問(wèn)題的,他們相互影響顯示同一個(gè)顏色辞做,最終翻看源碼琳要,發(fā)現(xiàn)是Drawable資源緩存的問(wèn)題。詳細(xì)的源碼可以查看ResourcesImpl里loadDrawable部分凭豪。
避免這個(gè)問(wèn)題焙蹭,可以通過(guò)setProgressDrawable設(shè)置SeekBar的LayerDrawable。
int progressDrawableRes = R.drawable.seekbar_progress_drawable;
LayerDrawable layerDrawable = (LayerDrawable)ContextCompat.getDrawable(mContext, progressDrawableRes).getConstantState().newDrawable().mutate();
seekBar.setProgressDrawable(layerDrawable);
5. notifyDataSetChanged問(wèn)題
這里有個(gè)問(wèn)題需要注意嫂伞,從最上面的錄屏看孔厉,滑動(dòng)每一個(gè)SeekBar,動(dòng)畫(huà)執(zhí)行的同時(shí)都需要?jiǎng)討B(tài)的更新數(shù)據(jù)帖努,實(shí)時(shí)的更新整體亮度撰豺,notifyDataSetChanged的時(shí)機(jī)需要跟動(dòng)畫(huà)結(jié)合,因?yàn)镽ecyclerView的緩存機(jī)制拼余,可能動(dòng)畫(huà)執(zhí)行一半污桦,SeekBar對(duì)象就變了。
就這樣吧匙监,算是總結(jié)記錄下最近搞的UI部分吧凡橱。
參考文檔:
Android drawable資源分析
動(dòng)態(tài)改變SeekBar進(jìn)度條顏色與滑塊顏色
GradientDrawable(shape標(biāo)簽定義) 靜態(tài)使用和動(dòng)態(tài)使用(圓角,漸變實(shí)現(xiàn))
各個(gè)擊破搞明白PorterDuff.Mode