滑動驗(yàn)證碼實(shí)踐

一大早起床就看到推送一篇文章亥贸,關(guān)于仿斗魚web端的滑動驗(yàn)證碼,看了一下實(shí)現(xiàn)浇垦,挺有趣的炕置,便自己順著思路擼一遍,改了一點(diǎn)實(shí)現(xiàn)和動畫什么的男韧,順帶鞏固一下繪制的代碼朴摊。

這里也貼一下原作者文章鏈接:http://qingmang.me/articles/-4771769944547152798

本文代碼鏈接:代碼〈寺牵看得喜歡關(guān)系點(diǎn)個(gè)star甚纲。

先看一下效果圖


效果圖

效果還是不錯(cuò)的。

用法朦前,SlideValidationView 是繼承自ImageView介杆,所以驗(yàn)證碼圖片直接set就行。

SeekBar seekBar;
SlideValidationView slideValidationView;
slideValidationView = (SlideValidationView) findViewById(R.id.yzm);
// 設(shè)置監(jiān)聽器韭寸,判斷驗(yàn)證成功失敗時(shí)回調(diào)
slideValidationView.setListener(new SlideListener() {    
    @Override    
    public void onSuccess() {        
        Toast.makeText(MainActivity.this, "驗(yàn)證成功", Toast.LENGTH_SHORT).show();
        seekBar.setProgress(0);
    }
    @Override
    public void onFail() {
        Toast.makeText(MainActivity.this, "驗(yàn)證失敗", 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) {
        // 更新驗(yàn)證滑塊的位置
        slideValidationView.setOffsetX(progress);
    }
    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {    }
    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        // 進(jìn)行驗(yàn)證碼的判斷
        slideValidationView.deal();
    }
});

下面說一下實(shí)現(xiàn)春哨,也可以選擇去看代碼,代碼里面注釋應(yīng)該很全了恩伺。代碼鏈接:代碼赴背。看得喜歡關(guān)系點(diǎn)個(gè)star。
第一步:畫拼圖的path癞尚,上下左右四個(gè)半圓隨機(jī)凹凸

/**
 * 創(chuàng)建驗(yàn)證區(qū)域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);
}
/**
 * 驗(yàn)證區(qū)域path四條邊的半圓弧度
 * @param validationPath 要操作的path
 * @param beginX         弧度的起始x坐標(biāo)(取弧度的左邊坐標(biāo)耸三,即弧度的兩點(diǎn)乱陡,位于左邊的那個(gè)坐標(biāo))
 * @param beginY         弧度的起始y坐標(biāo)(取弧度的上邊坐標(biāo)浇揩,即弧度的兩點(diǎn),位于上邊的那個(gè)坐標(biāo))
 * @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);
    } 
   // 隨機(jī)得到是突出還是凹入半圓胳徽,針對角度問題,用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

第二步:繪制陰影(設(shè)置畫筆的setMaskFilter爽彤,應(yīng)該要為這個(gè)view關(guān)閉硬件加速养盗,否則陰影沒作用)

// 單獨(dú)為這個(gè)view關(guān)閉硬件加速
setLayerType(LAYER_TYPE_SOFTWARE, null);

陰影

// 驗(yàn)證塊的陰影畫筆
Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mPaint.setColor(0x99000000);
// 設(shè)置畫筆遮罩濾鏡mPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.SOLID));
繪制陰影

第三步:繪制滑塊
因?yàn)樯厦嫖覀兊玫搅似磮D的path,我們就創(chuàng)建一個(gè)bitmap适篙,在里面分別繪制驗(yàn)證碼原圖和這個(gè)path往核,通過setXfermode(不了解的可以去搜搜),取得他們的交集嚷节,即為我們的滑塊聂儒,由此我們通過一個(gè)變量來控制滑塊的繪制x軸就行了

// 以控件寬高 create一塊bitmap
Bitmap tempBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// 把創(chuàng)建的bitmap作為畫板
Canvas mCanvas = new Canvas(tempBitmap);
// 抗鋸齒
mCanvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
// 繪制用于遮罩的圓形
mCanvas.drawPath(mask, mMaskPaint);
// 設(shè)置遮罩模式(圖像混合模式)
mMaskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
// 考慮到scaleType等因素,要用Matrix對Bitmap進(jìn)行縮放
mCanvas.drawBitmap(mBitmap, getImageMatrix(), mMaskPaint);

第四步
繪制滑塊的陰影硫痰,這里面有個(gè)api我也是第一次接觸衩婚,bitmap.extractAlpha()拿到該bitmap的圖片大小等信息,但只有透明度沒有顏色效斑,返回一張新的bitmap非春。我們通過設(shè)置畫筆的陰影來繪制新的bitmap即可繪制出滑塊的陰影

// extractAlpha拿到原bitmap的區(qū)域,只有透明度Bitmap
 mMaskShadowBitmap = mMaskBitmap.extractAlpha();
繪制滑塊和陰影

一些方法

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

詳細(xì)請去看代碼奇昙,代碼里面注釋應(yīng)該很全了。代碼鏈接:代碼敌完〈⒛停看得喜歡關(guān)系點(diǎn)個(gè)star。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蠢挡,一起剝皮案震驚了整個(gè)濱河市弧岳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌业踏,老刑警劉巖禽炬,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異勤家,居然都是意外死亡腹尖,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門伐脖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來热幔,“玉大人乐设,你說我怎么就攤上這事∫锞蓿” “怎么了近尚?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長场勤。 經(jīng)常有香客問我戈锻,道長,這世上最難降的妖魔是什么和媳? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任格遭,我火速辦了婚禮,結(jié)果婚禮上留瞳,老公的妹妹穿的比我還像新娘拒迅。我一直安慰自己,他們只是感情好她倘,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布璧微。 她就那樣靜靜地躺著,像睡著了一般帝牡。 火紅的嫁衣襯著肌膚如雪往毡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天靶溜,我揣著相機(jī)與錄音开瞭,去河邊找鬼。 笑死罩息,一個(gè)胖子當(dāng)著我的面吹牛嗤详,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瓷炮,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼葱色,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了娘香?” 一聲冷哼從身側(cè)響起苍狰,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎烘绽,沒想到半個(gè)月后淋昭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡安接,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年翔忽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡歇式,死狀恐怖驶悟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情材失,我是刑警寧澤痕鳍,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站豺憔,受9級特大地震影響额获,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜恭应,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望耘眨。 院中可真熱鬧昼榛,春花似錦、人聲如沸剔难。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽偶宫。三九已至非迹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纯趋,已是汗流浹背憎兽。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吵冒,地道東北人纯命。 一個(gè)月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像痹栖,于是被迫代替她去往敵國和親亿汞。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容