一大早起床就看到推送一篇文章亥贸,關(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);
}
}
}
第二步:繪制陰影(設(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。