滑動驗證碼實踐

SamanLan發表於2017-12-13

一大早起床就看到推送一篇文章,關於仿鬥魚web端的滑動驗證碼,看了一下實現,挺有趣的,便自己順著思路擼一遍,改了一點實現和動畫什麼的,順帶鞏固一下繪製的程式碼。

這裡也貼一下原作者文章連結:http://qingmang.me/articles/-4771769944547152798

本文程式碼連結:程式碼。看得喜歡關係點個star。

先看一下效果圖

效果圖
效果還是不錯的。

用法,SlideValidationView 是繼承自ImageView,所以驗證碼圖片直接set就行。

SeekBar seekBar;
SlideValidationView slideValidationView;
slideValidationView = (SlideValidationView) findViewById(R.id.yzm);
// 設定監聽器,判斷驗證成功失敗時回撥
slideValidationView.setListener(new SlideListener() {    
    @Override    
    public void onSuccess() {        
        Toast.makeText(MainActivity.this, "驗證成功", Toast.LENGTH_SHORT).show();
        seekBar.setProgress(0);
    }
    @Override
    public void onFail() {
        Toast.makeText(MainActivity.this, "驗證失敗", Toast.LENGTH_SHORT).show();
        seekBar.setProgress(0);
    }
});
seekBar = (SeekBar) findViewById(R.id.seekBar);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        // 更新驗證滑塊的位置
        slideValidationView.setOffsetX(progress);
    }
    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {    }
    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        // 進行驗證碼的判斷
        slideValidationView.deal();
    }
});
複製程式碼

下面說一下實現,也可以選擇去看程式碼,程式碼裡面註釋應該很全了。程式碼連結:程式碼。看得喜歡關係點個star。 第一步:畫拼圖的path,上下左右四個半圓隨機凹凸

/**
 * 建立驗證區域path
 */
private void creatValidationPath() {
    validationPath = new Path();
    if (validationSize == 0) {
        validationSize = width/6;
    }
    circleSize = validationSize / 3;
    startX = new Random().nextInt(width - validationSize * 2 - circleSize * 2 - 10) + circleSize + validationSize + 10;
    startY = new Random().nextInt(height - validationSize - circleSize * 2) + circleSize;
    // 從左上畫path到右上
    validationPath.moveTo(startX, startY);
    validationPath.lineTo(startX + circleSize, startY);
    creatRandomArc(validationPath, startX + circleSize, startY, false, 0);
    validationPath.lineTo(startX + validationSize, startY);
    // 從右上畫path到右下
    validationPath.lineTo(startX + validationSize, startY + circleSize);
    creatRandomArc(validationPath, startX + validationSize, startY + circleSize, true, 0);
    validationPath.lineTo(startX + validationSize, startY + validationSize);
    // 從右下畫path到左下
    validationPath.lineTo(startX + circleSize * 2, startY + validationSize);
    creatRandomArc(validationPath, startX + circleSize, startY + validationSize, false, 1);
    validationPath.lineTo(startX, startY + validationSize);
    // 從左下畫path到左上
    validationPath.lineTo(startX, startY + circleSize * 2);
    creatRandomArc(validationPath, startX, startY + circleSize, true, 1);
    validationPath.lineTo(startX, startY);
}
/**
 * 驗證區域path四條邊的半圓弧度
 * @param validationPath 要操作的path
 * @param beginX         弧度的起始x座標(取弧度的左邊座標,即弧度的兩點,位於左邊的那個座標)
 * @param beginY         弧度的起始y座標(取弧度的上邊座標,即弧度的兩點,位於上邊的那個座標)
 * @param isleftRight    是否左右邊 
* @param type           右上邊為0,左下邊為1 
*/
private void creatRandomArc(Path validationPath, int beginX, int beginY, boolean isleftRight, int type) {
    RectF rectF;
    // 是左右邊還是上下邊
    if (isleftRight) {
        rectF = new RectF(beginX - circleSize / 2, beginY, beginX + circleSize / 2, beginY + circleSize);
    } else {
        rectF = new RectF(beginX, beginY - circleSize / 2, beginX + circleSize, beginY + circleSize / 2);
    } 
   // 隨機得到是突出還是凹入半圓,針對角度問題,用type來解決
    if (new Random().nextInt(10) > 5) {
        // 突出半圓
        if (isleftRight) {
            validationPath.arcTo(rectF, -90 + type * 180, 180);
        } else { 
           validationPath.arcTo(rectF, -180 + type * 180, 180); 
       }
    } else {
        // 凹入半圓 
       if (isleftRight) { 
           validationPath.arcTo(rectF, -90 + type * 180, -180);
        } else { 
           validationPath.arcTo(rectF, -180 + type * 180, -180);
        }
    }
}
複製程式碼

繪製拼圖path

第二步:繪製陰影(設定畫筆的setMaskFilter,應該要為這個view關閉硬體加速,否則陰影沒作用)

// 單獨為這個view關閉硬體加速
setLayerType(LAYER_TYPE_SOFTWARE, null);
複製程式碼

陰影

// 驗證塊的陰影畫筆
Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mPaint.setColor(0x99000000);
// 設定畫筆遮罩濾鏡mPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.SOLID));
複製程式碼

繪製陰影

第三步:繪製滑塊 因為上面我們得到了拼圖的path,我們就建立一個bitmap,在裡面分別繪製驗證碼原圖和這個path,通過setXfermode(不瞭解的可以去搜搜),取得他們的交集,即為我們的滑塊,由此我們通過一個變數來控制滑塊的繪製x軸就行了

// 以控制元件寬高 create一塊bitmap
Bitmap tempBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// 把建立的bitmap作為畫板
Canvas mCanvas = new Canvas(tempBitmap);
// 抗鋸齒
mCanvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
// 繪製用於遮罩的圓形
mCanvas.drawPath(mask, mMaskPaint);
// 設定遮罩模式(影象混合模式)
mMaskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
// 考慮到scaleType等因素,要用Matrix對Bitmap進行縮放
mCanvas.drawBitmap(mBitmap, getImageMatrix(), mMaskPaint);
複製程式碼

第四步 繪製滑塊的陰影,這裡面有個api我也是第一次接觸,bitmap.extractAlpha()拿到該bitmap的圖片大小等資訊,但只有透明度沒有顏色,返回一張新的bitmap。我們通過設定畫筆的陰影來繪製新的bitmap即可繪製出滑塊的陰影

// extractAlpha拿到原bitmap的區域,只有透明度Bitmap
 mMaskShadowBitmap = mMaskBitmap.extractAlpha();
複製程式碼

繪製滑塊和陰影

一些方法

方法名 用處
setOffsetX(float howMuch) 設定滑塊移動距離(@param howMuch 0-100內數字,表示百分比)
restore() 重置驗證區域位置(重新生成拼圖path)
deal() 判斷是否成功
setListener(SlideListener listener) 設定監聽器

詳細請去看程式碼,程式碼裡面註釋應該很全了。程式碼連結:程式碼。看得喜歡關係點個star。

相關文章