一、說(shuō)明
??筆記主要是記錄一些本人在開發(fā)當(dāng)中的學(xué)習(xí)和使用筆記掏愁。筆記內(nèi)容包含一些本人覺(jué)得重要的知識(shí)點(diǎn)歇由、本人易犯的錯(cuò)誤等。
??由于本人水平有限果港,其中出現(xiàn)的錯(cuò)誤或者不合理的地方望各位讀者多多包含沦泌,并指出其中不合理和錯(cuò)誤的地方,以便我來(lái)修改正辛掠。謝謝谢谦!
二释牺、筆記時(shí)間
??2019年06月27日:首次編輯
??2019年10月23日:第一次修改
三、簡(jiǎn)述
??本文主要講述如何把 SeekBar 變成滑動(dòng)驗(yàn)證的控件回挽。
四没咙、詳情
??由于工作和生活的原因,很長(zhǎng)一段時(shí)間沒(méi)有寫博客了千劈,最近稍閑一點(diǎn)點(diǎn)祭刚,決定把這段時(shí)間工作中遇到的一些問(wèn)題和解決方案記錄下來(lái)。供自己和有需要的朋友借鑒墙牌。
??SeekBar 是我們工作當(dāng)中經(jīng)常使用到的控件涡驮,SeekBar 在畫板中可以用他來(lái)調(diào)節(jié)畫筆、畫布的大秀竟拧遮怜;可以用來(lái)調(diào)節(jié)圖片顏色淋袖、顯示下載進(jìn)度鸿市;還可以制作滑動(dòng)驗(yàn)證條等等〖赐耄總的來(lái)說(shuō)有很多場(chǎng)景我們都可以用SeekBar來(lái)實(shí)現(xiàn)焰情。今天主要講我的用 SeekBar 做滑動(dòng)驗(yàn)證條的實(shí)現(xiàn)方式。
1剥懒、自定義 SeekBar 樣式
??要實(shí)現(xiàn)美工小姐姐設(shè)計(jì)圖的效果内舟,以便與美工小姐姐產(chǎn)生共鳴,我們無(wú)法避免對(duì)SeekBar的樣子進(jìn)行自定義初橘。下面介紹幾個(gè) SeekBar 的常用屬性:
- android:thumb //滑塊樣式
- android:progressDrawable //進(jìn)度條樣式
- android:max //拖動(dòng)條的最大值
- android:min //拖動(dòng)條的最小值
- android:progress //當(dāng)前的進(jìn)度值
- android:thumbOffset //滑塊偏移量
- android:splitTrack //是否拆分繪制(滑塊透明區(qū)域透明)
- android:paddingStart //盒子內(nèi)左邊距
- android:paddingEnd //盒子內(nèi)右邊距
??上面對(duì)幾個(gè)重要屬性進(jìn)行了簡(jiǎn)短說(shuō)明验游,下面一一說(shuō)明怎么通過(guò)設(shè)置以上屬性實(shí)現(xiàn)滑動(dòng)驗(yàn)證效果。
1.1 android:thumb
??設(shè)置 thumb 可以實(shí)現(xiàn)自定義滑塊樣式保檐。我們可以使用美工提供的圖片耕蝉,也可以用 shape 等來(lái)自定義滑塊樣式。我這里為了在不同機(jī)型上的通用性夜只,是使用 shape 來(lái)自定義的樣式垒在,如下:
<!-- seekbar_thumb.xml -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="11dp"
android:height="11dp" />
<corners android:radius="3dp" />
<gradient
android:endColor="#FF44A3FF"
android:startColor="#FF9CDCFF" />
</shape>
??樣式效果是四角為圓角、顏色漸變扔亥、固定大小的正方形滑塊场躯。效果如下圖:
1.2 android:progressDrawable
??設(shè)置 progressDrawable 可以實(shí)現(xiàn)自定義進(jìn)度條樣式。和 thumb 不一樣的是旅挤,我們可以使用美工提供的圖片踢关,但是還是要用 layer-list 來(lái)進(jìn)行組合,因?yàn)檫M(jìn)度條有選中未選擇等狀態(tài)粘茄;當(dāng)然也可以使用 shape 自定義耘成。同樣我這里也是使用 shape 自定義的,具體代碼如下:
<!-- seekbar_progress.xml -->
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@android:id/background"
android:height="11dp"
android:gravity="center_vertical">
<shape>
<corners android:radius="3dp" />
<solid android:color="@android:color/transparent" />
</shape>
</item>
<item
android:id="@android:id/secondaryProgress"
android:height="11dp"
android:gravity="center_vertical">
<clip>
<shape>
<corners android:radius="3dp" />
<solid android:color="#FF9CDCFF" />
</shape>
</clip>
</item>
<item
android:id="@android:id/progress"
android:height="11dp"
android:gravity="center_vertical">
<clip>
<shape>
<corners android:radius="3dp" />
<solid android:color="#FF9CDCFF" />
</shape>
</clip>
</item>
</layer-list>
??先簡(jiǎn)要說(shuō)明一下我自定義的效果,我實(shí)現(xiàn)的效果是選中狀態(tài)為:未選中(滑塊右側(cè))透明瘪菌,選中和過(guò)渡區(qū)域?yàn)楣潭ㄉɑ瑝K漸變的開始顏色)撒会。效果如下圖:
??細(xì)心的同學(xué)回發(fā)現(xiàn)里面有三個(gè) @android 的 ID,那個(gè)這幾個(gè) ID到底是做什么用的勒师妙,下面對(duì)他們說(shuō)明一下诵肛。
- @android:id/background //這個(gè)不難理解,從命名上我們就可以看出來(lái)默穴,他是設(shè)置進(jìn)度條的背景
- @android:id/secondaryProgress //這個(gè)咋一看難以從命名上直接看出來(lái)怔檩,其實(shí)他是第二進(jìn)度選中樣式,和 android:secondaryProgress 一起看就不難理解了蓄诽。
- @android:id/progress //這個(gè)自然就是第一進(jìn)度選中樣式了薛训。
1.3 android:max、android:min
??這兩個(gè)屬性就不需要多講了仑氛,相信大家都明白乙埃,他就是設(shè)置進(jìn)度條的最大、最小進(jìn)度值锯岖。
1.4 android:progress
??這個(gè)屬性應(yīng)該大家也都明白介袜,就是設(shè)置當(dāng)前選中的進(jìn)度值。當(dāng)然還有 android:secondaryProgress 這個(gè)屬性出吹,這個(gè)屬性就是設(shè)置第二進(jìn)度的角度值遇伞。
1.5 android:thumbOffset
??這個(gè)屬性從命名可以看出是設(shè)置滑塊的偏移量,但是設(shè)置不同的值是什么效果勒捶牢?下面我們來(lái)看看設(shè)置為10dp鸠珠、0dp、-10dp 的效果:
- android:thumbOffset="10dp" //向左偏移
- android:thumbOffset="0dp" //向右偏移一個(gè)滑塊寬度
- android:thumbOffset="-10dp" //向右偏移
1.6 android:splitTrack
??這個(gè)屬性還是很有用的秋麸,尤其是自定義滑塊的時(shí)候渐排。我自定義的滑塊是圓角,當(dāng)我沒(méi)設(shè)置這個(gè)屬性的時(shí)候(默認(rèn) android:splitTrack="true")竹勉,發(fā)現(xiàn)滑動(dòng)的時(shí)候圓角處不是透明的飞盆,而是有一個(gè)白色的角,這明顯不是我們想要的效果次乓。
??出現(xiàn)這種不透明的現(xiàn)象我們就可以設(shè)置 android:splitTrack="false" 吓歇,這樣就是透明的了。
1.6 android:paddingStart票腰、android:paddingEnd
??我們知道固定 SeekBar 寬高之后城看,滑塊左右都默認(rèn)會(huì)偏移半個(gè)滑塊的寬度,以便滑動(dòng)到最左和最右的時(shí)候能夠完整的顯示滑塊杏慰。其實(shí)這個(gè)偏移位置我們是可以自己設(shè)定的测柠,就是通過(guò) paddingStart炼鞠、paddingEnd 來(lái)設(shè)置兩邊的偏移。當(dāng)然 paddingLeft轰胁、paddingRight 也可以達(dá)到一樣的效果谒主。
1.7 滑動(dòng)驗(yàn)證最終配置
??雖然上面介紹了那么多屬性,不過(guò)這里實(shí)現(xiàn)滑動(dòng)驗(yàn)證效果赃阀,我并沒(méi)有全部使用霎肯,最終的配置如下:
<SeekBar
android:id="@+id/seekbar"
android:layout_width="50dp"
android:layout_height="11dp"
android:paddingStart="6dp"
android:paddingEnd="6dp"
android:progressDrawable="@drawable/seekbar_progress"
android:splitTrack="false"
android:thumb="@drawable/seekbar_thumb" />
??以上配置的效果如下圖:
2、滑動(dòng)驗(yàn)證功能實(shí)現(xiàn)
??第一大點(diǎn)主要是自定義滑動(dòng)條的樣式榛斯,也就是實(shí)現(xiàn)我們美工小姐姐的奇思異想观游。剩下的滑動(dòng)驗(yàn)證功能就需要我們這些攻城獅一句一句代碼敲出來(lái)了!下面我介紹一下我的實(shí)現(xiàn)吧驮俗。
2.1 滑動(dòng)停止回到初始位置
??這并不是什么復(fù)雜的功能懂缕,我們快速過(guò)。只需要給 SeekBar 設(shè)置 setOnSeekBarChangeListener 監(jiān)聽(tīng)王凑,然后實(shí)現(xiàn)監(jiān)聽(tīng)搪柑,在監(jiān)聽(tīng)中的 onStopTrackingTouch 方法里出現(xiàn)設(shè)置 progres 為 0 就好了。還是貼一下代碼荤崇,如下:
SeekBar seekBar = findViewById(R.id.seekbar);
seekBar.setOnSeekBarChangeListener(this);
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
seekBar.setProgress(0);
}
2.1 屏蔽滑動(dòng)條滑塊外的點(diǎn)擊事件
??原本以為做完以上工作拌屏,滑動(dòng)驗(yàn)證就算完成了潮针∪踉簦可偏偏又出現(xiàn)了另外一個(gè)問(wèn)題衔憨,說(shuō)好的滑動(dòng)驗(yàn)證,居然點(diǎn)擊滑塊的其它位置滑塊也會(huì)設(shè)置過(guò)去。不過(guò)這是 SeekBar 的基本功能汪榔,我們不能說(shuō)啥,現(xiàn)在我們看看怎么屏蔽滑塊外的點(diǎn)擊事件仓蛆。我自定義了 SeekBar 睬涧,對(duì)觸摸事件進(jìn)行了處理,代碼如下:
public class VerificationSeekBar extends AppCompatSeekBar {
private boolean isInterception = true;
public VerificationSeekBar(Context context) {
super(context);
}
public VerificationSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public VerificationSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
isInterception = true;
if (event.getX() > getThumb().getMinimumWidth()) {
isInterception = false;
return true;
}
}
if (event.getAction() == MotionEvent.ACTION_MOVE && !isInterception) {
return true;
}
return super.dispatchTouchEvent(event);
}
}
??上面代碼的邏輯也很簡(jiǎn)單矗晃,就是判斷點(diǎn)擊位置是否在滑塊外仑嗅,在滑塊外就屏蔽啥也不做,但是消費(fèi)掉滑動(dòng)事件张症。
??現(xiàn)在替換 SeekBar 為我們自定義的 VerificationSeekBar 仓技,就能夠達(dá)到滑動(dòng)驗(yàn)證的效果了。
2.2 解決點(diǎn)擊滑動(dòng)條尾部直接驗(yàn)證成功的問(wèn)題
??我在做畫板的時(shí)候俗他,利用該方式來(lái)做滑動(dòng)清屏脖捻,結(jié)果發(fā)現(xiàn)了一個(gè)隱藏bug。我們程序員往往最怕這種隱藏bug了兆衅,但是怕歸怕地沮,問(wèn)題我們還是要解決了嗜浮。當(dāng)然了,這個(gè)bug并不是很難解摩疑。
??下面說(shuō)一下這個(gè)具體的bug:在白板當(dāng)中危融,我設(shè)置的是滑動(dòng)到90%以上,松手即觸發(fā)清屏雷袋。結(jié)果當(dāng)我們點(diǎn)擊滑動(dòng)條末尾位置時(shí)专挪,同樣觸發(fā)了清屏。說(shuō)好的滑動(dòng)清屏片排,可是實(shí)際使用結(jié)果卻可以點(diǎn)擊清屏寨腔,這很明顯和我們的設(shè)計(jì)方案不符合。因此我做了以下小修改率寡,來(lái)完善該功能迫卢。
??我把該問(wèn)題記錄下來(lái),一個(gè)是給自己做筆記冶共,另外一個(gè)我相信滑動(dòng)驗(yàn)證也會(huì)有類似的問(wèn)題乾蛤,希望讀者能夠直接解決好問(wèn)題。以下就是我的解決方案捅僵。
2.2.1 把 isInterception 的值提供出來(lái)
??把 isInterception 的值提供出來(lái)其實(shí)就是在 VerificationSeekBar 中添加一個(gè)獲取該值的方法家卖,具體如下:
// VerificationSeekBar.java
/**
* 獲取是否滑動(dòng)清屏
*
* @return true:滑動(dòng)驗(yàn)證 false:非滑動(dòng)驗(yàn)證
*/
public boolean isInterception() {
return isInterception;
}
2.2.2 判斷是否是滑動(dòng)驗(yàn)證
??這個(gè)就需要我們?cè)趯?shí)現(xiàn)SeekBar的監(jiān)聽(tīng)事件時(shí),利用 isInterception 方法來(lái)判斷是否觸發(fā)驗(yàn)證成功庙楚。具體如下:
VerificationSeekBar mySeekBar;
mySeekBar = findViewById(R.id.seekbar);
mySeekBar.setOnSeekBarChangeListener(this);
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (mySeekBar.isInterception()) {
succeed(); //滑動(dòng)驗(yàn)證成功
} else {
failed(); //滑動(dòng)驗(yàn)證失敗
}
seekBar.setProgress(0);
}
2.2.3 其它解決方案
??我以上的解決方案并不是唯一的解決方案上荡。我再提供兩種解決思路,因?yàn)轫?xiàng)目時(shí)間緊張我就沒(méi)一一驗(yàn)證了馒闷。讀者可以驗(yàn)證一下酪捡,把驗(yàn)證結(jié)果加到評(píng)論區(qū),讓所有讀者共勉纳账。
第一種解決方案和我上面提供的解決方案一樣逛薇,只是我們可以把 setOnSeekBarChangeListener 監(jiān)聽(tīng)的邏輯在 VerificationSeekBar 中實(shí)現(xiàn),并向外提供一個(gè) succeed疏虫、failed 接口永罚,來(lái)保證封裝的完整性。本人推薦該方法卧秘。
第二中解決方案呢袱,我們可以利用 onStartTrackingTouch ,通過(guò)開始點(diǎn)擊的值(位置)來(lái)判斷是否是滑動(dòng)驗(yàn)證斯议,非點(diǎn)擊滑動(dòng)條末尾产捞,但是要增加變量,不建議使用哼御。用我畫板90%觸發(fā)清屏為例坯临,我們可以修改為如下:
int startProgress = 0;
SeekBar seekBar = findViewById(R.id.seekbar);
seekBar.setOnSeekBarChangeListener(this);
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
startProgress = seekBar.getProgress();
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (startProgress < 90 && seekBar.getProgress() >= 90) {
succeed(); //滑動(dòng)驗(yàn)證成功
} else {
failed(); //滑動(dòng)驗(yàn)證失敗
}
seekBar.setProgress(0);
}
注:第二中方案要著重驗(yàn)證焊唬,從滑動(dòng)條中間拖到滿足條件,是否會(huì)被判斷為 succeed看靠?若滿足驗(yàn)證成功赶促,建議和第一種犯案一樣封裝一下;若驗(yàn)證失敗挟炬,建議把 <90 的條件改為鸥滨,<= getThumb().getMinimumWidth()