天天看點

Android自定義滑動驗證條

*注:不知道為什麼,h5的标簽在這裡沒用了,是以我也隻能用Markdown的文法來寫了

項目位址:

https://github.com/994866755/handsomeYe.seekbar.github.io

需求:

在我們的某些應用中需要滑動驗證。比如說這個樣子的:

Android自定義滑動驗證條

image.png

剛開始我也很懵逼要怎麼去弄,結果我去看了一些人的代碼,有人是用自定義viewgroup去做,就是viewgroup包含滑動塊和滑動條。但我覺得太麻煩,直到我知道android有個控件seekbar可以實作這個效果。

一、使用SeekBar實作滑動條

<SeekBar
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:max="100"
        android:maxHeight="45dp"
        android:minHeight="45dp"
        android:progress="0"
        android:clickable="false"
        android:progressDrawable="@drawable/bg_forgotpassword_seekbar"
        android:thumb="@drawable/bg_seekbar_thumb"
        android:id="@+id/sb_progress"
        android:thumbOffset="-1dp"
        android:padding="1dp"
        />
           

這裡網上有很多介紹,我也順便解釋一下:

(1)android:max是總共的容量,這裡設100就行。
(2)android:progressDrawable是隻設定進度框的背景,就是整個條的背景,比如圖中的沒滑動的時候是灰色,滑動的地方是綠色。
(3)android:thumb這個屬性是設定滑塊的樣式,比如圖中的沒滑時是>>,滑到最右變成勾。預設的樣式是一個圓。
(4)android:thumbOffset這個是滑塊的起始位置,怎麼說呢,你可以當圖中第一條的滑塊是android:thumbOffset = “0dp”,如果你設定成正數,這個滑塊的位置會向左移動,設成負數會向右移動。我這裡設成-1是以為是0的時候會擋住左邊的邊框,這樣不好看,我設成-1為了讓滑塊向右移動一點。

二、設定SeekBar樣式

寫好布局後來寫下樣式,也就是上面的progressDrawable和thumb屬性。

(1)progressDrawable(滑動條)
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!--seekBar背景-->
    <item android:id="@android:id/background">
        <!--形狀-->
        <shape android:shape="rectangle">
            <!--大小-->
            <size android:height="29dp" />
            <!--圓角-->
            <corners android:radius="2dp" />
            <!--背景-->
            <solid android:color="#E7EAE9" />
            <!--邊框-->
            <stroke
                android:width="1dp"
                android:color="#C3C5C4" />
        </shape>
    </item>
    <!--seekBar的進度條-->
    <item android:id="@android:id/progress">
        <clip>
            <shape>
                <corners android:radius="2dp" />
                <solid android:color="#7AC23C" />
                <stroke
                    android:width="1dp"
                    android:color="#C3C5C4" />
            </shape>
        </clip>
    </item>
</layer-list>
           
(2)thumb(滑塊)
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 按下狀态-->
    <item android:drawable="@mipmap/seekbar_thumb" android:state_pressed="true" />

    <!-- 有焦點狀态 -->
    <item android:drawable="@mipmap/seekbar_thumb" android:state_focused="true" />

    <!-- 普通狀态 -->
    <item android:drawable="@mipmap/seekbar_thumb" />
</selector>
           

這裡就不多介紹了。

三、加入監聽

在activity中加入監聽,比如你可以讓滑塊不滑到最右時自動彈回原位等等。

sbProgress.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
  
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
         
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                if (seekBar.getProgress() != seekBar.getMax()) {
                    seekBar.setProgress(0);
                }else {
                    sbProgress.setEnabled(false);
                    setData();
                }
            }
        });
           

最後實作的效果是這樣:

Android自定義滑動驗證條

你可以自己加文字在中間,這個我就不在demo裡弄了。

四、剩下的處理

你以為這樣就完啦?那你太天真了,你會發現如果你按上面的步驟做,最後會有一個很蛋疼的效果:

你不滑動滑塊,隻點選滑動條中間,滑塊會馬上到中間。

也就是說我們想做的效果是隻滑動而不能點選,僅僅做成這樣是沒辦法實作這個需求。

那怎麼辦?我在網上找了很多文章,大多都是不能滑也不能點,而我要的是能滑不能點。難道SeekBar沒戲啦?我想了想,最後我用事件分發來解決。

既然是事件分發,我這裡也不想寫事件分發的内容,以後我們寫一篇專門關于事件分發的文章,這裡如果有小夥伴不了解事件分發的話,自己先去google一下。

既然是事件分發,那我們就需要自定義seekbar啦,其實很簡單。我先貼代碼,然後再講解。

(1)代碼君:
public class VerificationSeekBar extends SeekBar{
    //這兩個值為用算法使用的2空間複雜度
    private int index = 150;
    private boolean k = 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) {
        int x = (int) event.getX();
        if (event.getAction() == MotionEvent.ACTION_DOWN){
            k = true;
            if (x - index > 20) {
                k = false;
                return true;
            }
        }
        if (event.getAction() == MotionEvent.ACTION_MOVE){
            if (!k){
                return true;
            }
        }
        return super.dispatchTouchEvent(event);
    }
}
           
(2)講解

為了友善講解,我把其它的内容删掉,就留關鍵方法,沒錯,就是dispatchTouchEvent。但是如果我不說,可能dispatchTouchEvent裡面的代碼你會看得蒙。

先說說我的思想:簡單來說就是你點選的地方要在滑塊的範圍,才分發事件,不然retrun true不分發事件。是以有了x - index > 20,這裡的index =

150是我滑塊的大概寬度,是以要你點選的地方在我滑塊的寬度的20像素直接我才分發事件。是以x - index > 20的話不分發。

int x = (int) event.getX(); 擷取點選時的坐标,注意,是相對于view左上角的,而不是相對螢幕的。

我這裡分别按順序判斷了event.getAction() == MotionEvent.ACTION_DOWN和event.getAction() == MotionEvent.ACTION_MOVE,注意,是按順序。為什麼要按順序呢?首先你自己測試你會發現,點選seekbar時ACTION_DOWN和ACTION_MOVE都會執行,是以你不能光判定按下,還要判斷滑動。那為什麼不一起判斷而要按順序判定呢?因為一起判斷的話你可以試試,你會發現根本就滑不了。

而學過事件分發的都知道事件先執行ACTION_DOWN再執行ACTION_MOVE,是以先判斷點選的地方是否在滑塊+20像素的範圍内,如果不在,定義一個布爾值k記錄不在,然後執行 if (!k){return true;}

唉!感覺這個算法講得不是很好,聽不懂沒關系,你照抄就行,記得把index改成你滑塊的寬度就行。

繼續閱讀