亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android 高仿斗魚滑動驗證碼

 更新時間:2016年11月24日 15:46:04   作者:阿丶力  
這篇文章主要介紹了Android 高仿斗魚滑動驗證碼的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下

如下圖。在Android上實現(xiàn)起來就不太容易,有些效果還是不如web端酷炫。)



我們的Demo,Ac娘鎮(zhèn)樓

(圖很渣,也忽略底下的SeekBar,這不是重點)

一些動畫,效果錄不出來了,大家可以去斗魚web端看一下,然后下載Demo看一下,效果還是可以的。

代碼 傳送門:

https://github.com/mcxtzhang/SwipeCaptcha

我們的Demo和web端基本上一樣。

那么本控件包含不僅包含以下功能:

隨機區(qū)域起點(左上角x,y)生成一個驗證碼陰影。驗證碼拼圖 凹凸圖形會隨機變換。驗證碼區(qū)域寬高可自定義。摳圖驗證碼區(qū)域,繪制一個用于聯(lián)動滑動的驗證碼滑塊。驗證失敗,會閃爍幾下然后回到原點。驗證成功,會有白光掃過的動畫。

分解一下驗證碼核心實現(xiàn)思路:

控件繼承自ImageView。理由:

1 如果放在項目中用,驗證碼圖片希望可以是接口返回。ImageView以及其子類支持花式加載圖片。

2 繼承自ImageView,繪制圖片本身不用我們干預(yù),也不用我們操心scaleType,節(jié)省很多工作。在onSizeChanged()方法中

生成 和 控件寬高相關(guān)的屬性值:

1 初始化時隨機生成驗證碼區(qū)域起點

2 生成驗證碼區(qū)域Path

3 生成滑塊BitmaponDraw()時,依次繪制:

1 驗證碼陰影

2 滑塊

核心工作是以上,可是實現(xiàn)起來還是有很多坑的,下面一步一步來吧。

驗證碼區(qū)域的生成

這里我省略自定義View的幾個基礎(chǔ)步驟:

在attrs.xml定義屬性在View的構(gòu)造函數(shù)里獲取attrs屬性一些Paint,Path的初始化工作

完整代碼在

https://github.com/mcxtzhang/SwipeCaptcha

可以下載后對照閱讀,效果更佳。

首先思考,驗證碼區(qū)域包含:

繪制在圖片上的驗證碼陰影

可移動的驗證碼滑塊

1 生成驗證碼陰影

我們用Path存儲驗證碼區(qū)域,

所以這一步最重要是生成驗證碼區(qū)域的Path。

查看競品(斗魚web端)如下,


so,我們這里要繪制一個矩形+四邊可能會有隨機的凹凸,凹凸可以用半圓來替代。

我們?nèi)缦戮帉懀?br />

代碼配有注釋,gap是指凹凸的起點和頂點的距離。

//生成驗證碼Path
 private void createCaptchaPath() {
 //原本打算隨機生成gap,后來發(fā)現(xiàn) 寬度/3 效果比較好,
 int gap = mRandom.nextInt(mCaptchaWidth / 2);
 gap = mCaptchaWidth / 3;
 //隨機生成驗證碼陰影左上角 x y 點,
 mCaptchaX = mRandom.nextInt(mWidth - mCaptchaWidth - gap);
 mCaptchaY = mRandom.nextInt(mHeight - mCaptchaHeight - gap);
 mCaptchaPath.reset();
 mCaptchaPath.lineTo(0, 0);
 //從左上角開始 繪制一個不規(guī)則的陰影
 mCaptchaPath.moveTo(mCaptchaX, mCaptchaY);//左上角
 mCaptchaPath.lineTo(mCaptchaX + gap, mCaptchaY);
 //draw一個隨機凹凸的圓
 drawPartCircle(new PointF(mCaptchaX + gap, mCaptchaY),
 new PointF(mCaptchaX + gap * 2, mCaptchaY),
 mCaptchaPath, mRandom.nextBoolean());
 mCaptchaPath.lineTo(mCaptchaX + mCaptchaWidth, mCaptchaY);//右上角
 mCaptchaPath.lineTo(mCaptchaX + mCaptchaWidth, mCaptchaY + gap);
 //draw一個隨機凹凸的圓
 drawPartCircle(new PointF(mCaptchaX + mCaptchaWidth, mCaptchaY + gap),
 new PointF(mCaptchaX + mCaptchaWidth, mCaptchaY + gap * 2),
 mCaptchaPath, mRandom.nextBoolean());
 mCaptchaPath.lineTo(mCaptchaX + mCaptchaWidth, mCaptchaY + mCaptchaHeight);//右下角
 mCaptchaPath.lineTo(mCaptchaX + mCaptchaWidth - gap, mCaptchaY + mCaptchaHeight);
 //draw一個隨機凹凸的圓
 drawPartCircle(new PointF(mCaptchaX + mCaptchaWidth - gap, mCaptchaY + mCaptchaHeight),
 new PointF(mCaptchaX + mCaptchaWidth - gap * 2, mCaptchaY + mCaptchaHeight),
 mCaptchaPath, mRandom.nextBoolean());
 mCaptchaPath.lineTo(mCaptchaX, mCaptchaY + mCaptchaHeight);//左下角
 mCaptchaPath.lineTo(mCaptchaX, mCaptchaY + mCaptchaHeight - gap);
 //draw一個隨機凹凸的圓
 drawPartCircle(new PointF(mCaptchaX, mCaptchaY + mCaptchaHeight - gap),
 new PointF(mCaptchaX, mCaptchaY + mCaptchaHeight - gap * 2),
 mCaptchaPath, mRandom.nextBoolean());
 mCaptchaPath.close();
 }

關(guān)于drawPartCircle(),它的功能是傳入起點、終點坐標(biāo),以及需要凹還是凸,和繪制的Path。它會在Path上繪制一個凹、凸的半圓。

代碼如下:

/**
 * 傳入起點、終點 坐標(biāo)、凹凸和Path。
 * 會自動繪制凹凸的半圓弧
 *
 * @param start 起點坐標(biāo)
 * @param end 終點坐標(biāo)
 * @param path 半圓會繪制在這個path上
 * @param outer 是否凸半圓
 */
 public static void drawPartCircle(PointF start, PointF end, Path path, boolean outer) {
 float c = 0.551915024494f;
 //中點
 PointF middle = new PointF(start.x + (end.x - start.x) / 2, start.y + (end.y - start.y) / 2);
 //半徑
 float r1 = (float) Math.sqrt(Math.pow((middle.x - start.x), 2) + Math.pow((middle.y - start.y), 2));
 //gap值
 float gap1 = r1 * c;
 if (start.x == end.x) {
 //繪制豎直方向的
 //是否是從上到下
 boolean topToBottom = end.y - start.y > 0 ? true : false;
 //以下是我寫出了所有的計算公式后推的,不要問我過程,只可意會。
 int flag;//旋轉(zhuǎn)系數(shù)
 if (topToBottom) {
 flag = 1;
 } else {
 flag = -1;
 }
 if (outer) {
 //凸的 兩個半圓
 path.cubicTo(start.x + gap1 * flag, start.y,
  middle.x + r1 * flag, middle.y - gap1 * flag,
  middle.x + r1 * flag, middle.y);
 path.cubicTo(middle.x + r1 * flag, middle.y + gap1 * flag,
  end.x + gap1 * flag, end.y,
  end.x, end.y);
 } else {
 //凹的 兩個半圓
 path.cubicTo(start.x - gap1 * flag, start.y,
  middle.x - r1 * flag, middle.y - gap1 * flag,
  middle.x - r1 * flag, middle.y);
 path.cubicTo(middle.x - r1 * flag, middle.y + gap1 * flag,
  end.x - gap1 * flag, end.y,
  end.x, end.y);
 }
 } else {
 //繪制水平方向的
 //是否是從左到右
 boolean leftToRight = end.x - start.x > 0 ? true : false;
 //以下是我寫出了所有的計算公式后推的,不要問我過程,只可意會。
 int flag;//旋轉(zhuǎn)系數(shù)
 if (leftToRight) {
 flag = 1;
 } else {
 flag = -1;
 }
 if (outer) {
 //凸 兩個半圓
 path.cubicTo(start.x, start.y - gap1 * flag,
  middle.x - gap1 * flag, middle.y - r1 * flag,
  middle.x, middle.y - r1 * flag);
 path.cubicTo(middle.x + gap1 * flag, middle.y - r1 * flag,
  end.x, end.y - gap1 * flag,
  end.x, end.y);
 } else {
 //凹 兩個半圓
 path.cubicTo(start.x, start.y + gap1 * flag,
  middle.x - gap1 * flag, middle.y + r1 * flag,
  middle.x, middle.y + r1 * flag);
 path.cubicTo(middle.x + gap1 * flag, middle.y + r1 * flag,
  end.x, end.y + gap1 * flag,
  end.x, end.y);
 }
/*
 沒推導(dǎo)之前的公式在這里
 if (start.x < end.x) {
 if (outer) {
  //上左半圓 順時針
  path.cubicTo(start.x, start.y - gap1,
  middle.x - gap1, middle.y - r1,
  middle.x, middle.y - r1);
  //上右半圓:順時針
  path.cubicTo(middle.x + gap1, middle.y - r1,
  end.x, end.y - gap1,
  end.x, end.y);
 } else {
  //下左半圓 逆時針
  path.cubicTo(start.x, start.y + gap1,
  middle.x - gap1, middle.y + r1,
  middle.x, middle.y + r1);
  //下右半圓 逆時針
  path.cubicTo(middle.x + gap1, middle.y + r1,
  end.x, end.y + gap1,
  end.x, end.y);
 }
 } else {
 if (outer) {
  //下右半圓 順時針
  path.cubicTo(start.x, start.y + gap1,
  middle.x + gap1, middle.y + r1,
  middle.x, middle.y + r1);
  //下左半圓 順時針
  path.cubicTo(middle.x - gap1, middle.y + r1,
  end.x, end.y + gap1,
  end.x, end.y);
 }
 }*/
 }
 }

這里用的是推導(dǎo)之后的公式,沒推導(dǎo)前的也在注釋里。

簡單說,先計算出中點和半徑,利用三次貝塞爾曲線繪制一個圓(c和gap1 都是和三次貝塞爾曲線相關(guān))。關(guān)于三次貝塞爾曲線就不展開了,網(wǎng)上很多資料,我也是現(xiàn)學(xué)的。

這里關(guān)于繪制驗證碼陰影Path,還有一段曲折心路歷程,繪制出來的效果如下:


左邊是滑塊,右邊是陰影

心路歷程(可以不看)

驗證碼Path,猛的一看,似乎很簡單,不就是一個矩形+上四個邊可能出現(xiàn)的凹凸嘛。
凹凸的話,我們就是繪制一個半圓好了。

利用PathlineTo()+addCircle()似乎可以很輕松的實現(xiàn)?

最開始我是這么做的,結(jié)果發(fā)現(xiàn)畫出來的Path是多段的Path,閉合后,無法形成一個完整陰影區(qū)域。更無法用于下一步驗證碼滑塊bitmap的生成。

好,看來是addCircle()的鍋,導(dǎo)致了Path被分割成多段。那我用arcTo()好了,結(jié)果發(fā)現(xiàn)arcTo不像addCircle()那樣可以設(shè)置繪圖的方向,(順時針,逆時針),這當(dāng)時可把我難住了,因為不能逆時針的話,上、右邊的凹就畫不出來。所以我放棄了,我轉(zhuǎn)用貝塞爾曲線繪制這個凹凸。

文章寫到這里,我突然發(fā)現(xiàn)自己智障了,sweepAngle傳入負(fù)值不就可以逆時針了嗎。如:arcTo(oval, 180, -180);
所以說寫博客是有很大好處的,寫博客時大腦也是高速旋轉(zhuǎn),因為生怕寫出錯誤,一是誤導(dǎo)別人,二是丟人。大腦高速運轉(zhuǎn)說不定就想通了以前想不通的問題。

于是我就腦殘的用sin+二階貝爾賽曲線去繪制這個半圓了,為什么用它們呢?因為當(dāng)初我繪制波浪滾動的時候用的sin函數(shù)+二階貝塞爾模擬波浪,于是我就慣性思維的也這么解決了。結(jié)果呢?繪制出來的凹凸不夠圓啊,sin函數(shù)還是比不過圓是不是。

于是我就走上了用三節(jié)貝塞爾曲線模擬圓的路。

看來我當(dāng)初寫這一塊代碼的時候,腦子確實不太清醒,不過也有收獲。又復(fù)習(xí)了一遍Path的幾個函數(shù)和貝塞爾曲線。

2 摳圖:驗證碼滑塊的生成

驗證碼Path生成好了后,我要根據(jù)Path去生成驗證碼滑塊。那么第一步就是要摳圖了。
代碼如下:

 //生成滑塊
 private void craeteMask() {
 mMaskBitmap = getMaskBitmap(((BitmapDrawable) getDrawable()).getBitmap(), mCaptchaPath);
 //滑塊陰影
 mMaskShadowBitmap = mMaskBitmap.extractAlpha();
 //拖動的位移重置
 mDragerOffset = 0;
 //isDrawMask 繪制失敗閃爍動畫用
 isDrawMask = true;
 }
 //摳圖
 private Bitmap getMaskBitmap(Bitmap mBitmap, Path mask) {
 //以控件寬高 create一塊bitmap
 Bitmap tempBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
 //把創(chuàng)建的bitmap作為畫板
 Canvas mCanvas = new Canvas(tempBitmap);
 //有鋸齒 且無法解決,所以換成XFermode的方法做
 //mCanvas.clipPath(mask);
 // 抗鋸齒
 mCanvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
 //繪制用于遮罩的圓形
 mCanvas.drawPath(mask, mMaskPaint);
 //設(shè)置遮罩模式(圖像混合模式)
 mMaskPaint.setXfermode(mPorterDuffXfermode);
 //★考慮到scaleType等因素,要用Matrix對Bitmap進行縮放
 mCanvas.drawBitmap(mBitmap, getImageMatrix(), mMaskPaint);
 mMaskPaint.setXfermode(null);
 return tempBitmap;
 }

其實這里我也走了一些曲折的路,我先是用canvas.clipPath(path)摳的圖,結(jié)果發(fā)現(xiàn)有鋸齒,搜了很多資料也沒搞定。于是我又回到了Xfermode的路上,將其設(shè)置為mPorterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);

先繪制dst,即遮罩驗證碼Path,然后再繪制src:Bitmap,取交集即可完成摳圖。

這里有一些需要注意的地方:

src的Bitmap是取ImageView本身的bitmap。

創(chuàng)建的新Bitmap的寬高取控件的寬高

它們兩者的寬高很大可能是不同的,這就是ImageView參數(shù)scaleType的作用。所以我們?nèi)〕鯥mageView的Matrix 用于繪制src的Bitmap。這樣摳出來的Bitmap區(qū)域就和第1步遮蓋住的區(qū)域是一樣的了。

mMaskShadowBitmap = mMaskBitmap.extractAlpha();這句話是為了在繪制出的滑塊周圍也繪制一圈陰影,加強立體效果。

仔細(xì)看下圖效果,周邊又一圈立體陰影的效果:

onDraw()方法其實比較簡單,只不過在其中加入了一些布爾類型的flag,都是和動畫相關(guān)的:

代碼如下:

 @Override
 protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 //繼承自ImageView,所以Bitmap,ImageView已經(jīng)幫我們draw好了。
 //我只在上面繪制和驗證碼相關(guān)的部分,
 //是否處于驗證模式,在驗證成功后 為false,其余情況為true
 if (isMatchMode) {
 //首先繪制驗證碼陰影
 if (mCaptchaPath != null) {
 canvas.drawPath(mCaptchaPath, mPaint);
 }
 //繪制滑塊
 // isDrawMask 繪制失敗閃爍動畫用
 if (null != mMaskBitmap && null != mMaskShadowBitmap && isDrawMask) {
 // 先繪制陰影
 canvas.drawBitmap(mMaskShadowBitmap, -mCaptchaX + mDragerOffset, 0, mMaskShadowPaint);
 canvas.drawBitmap(mMaskBitmap, -mCaptchaX + mDragerOffset, 0, null);
 }
 //驗證成功,白光掃過的動畫,這一塊動畫感覺不完美,有提高空間
 if (isShowSuccessAnim) {
 canvas.translate(mSuccessAnimOffset, 0);
 canvas.drawPath(mSuccessPath, mSuccessPaint);
 }
 }
 }

mPaint如下定義: 所以繪制出陰影也有一些陰影效果。

mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
 mPaint.setColor(0x77000000);
 //mPaint.setStyle(Paint.Style.STROKE);
 // 設(shè)置畫筆遮罩濾鏡
 mPaint.setMaskFilter(new BlurMaskFilter(20, BlurMaskFilter.Blur.SOLID));

值得說的就是,配合滑塊滑動,是利用mDragerOffset,默認(rèn)是0,滑動時mDragerOffset增加,滑塊右移,反之亦然。

驗證成功的白光掃過動畫,是利用canvas.translate()做的,mSuccessPath和mSuccessPaint如下:

mSuccessPaint = new Paint();
 mSuccessPaint.setShader(new LinearGradient(0, 0, width, 0, new int[]{
 0x11ffffff, 0x88ffffff}, null,
 Shader.TileMode.MIRROR));
 //模仿斗魚 是一個平行四邊形滾動過去
 mSuccessPath = new Path();
 mSuccessPath.moveTo(0, 0);
 mSuccessPath.rLineTo(width, 0);
 mSuccessPath.rLineTo(width / 2, mHeight);
 mSuccessPath.rLineTo(-width, 0);
 mSuccessPath.close();

滑動、驗證、動畫

上一節(jié)完成后,我們的滑動驗證碼View已經(jīng)可以正常繪制出來了,現(xiàn)在我們?yōu)樗黾右恍┓椒?,讓它可以?lián)動滑動、驗證功能和動畫。

聯(lián)動滑動:

上一節(jié)也提到,滑動主要是改變mDragerOffset的值,然后重繪自己->ondraw(),根據(jù)mDragerOffset偏移滑塊Bitmap的繪制。

 /**
 * 重置驗證碼滑動距離,(一般用于驗證失敗)
 */
 public void resetCaptcha() {
 mDragerOffset = 0;
 invalidate();
 }
 /**
 * 最大可滑動值
 * @return
 */
 public int getMaxSwipeValue() {
 //return ((BitmapDrawable) getDrawable()).getBitmap().getWidth() - mCaptchaWidth;
 //返回控件寬度
 return mWidth - mCaptchaWidth;
 }
 /**
 * 設(shè)置當(dāng)前滑動值
 * @param value
 */
 public void setCurrentSwipeValue(int value) {
 mDragerOffset = value;
 invalidate();
 }

校驗:

校驗的話,需要引入一個回調(diào)接口:

public interface OnCaptchaMatchCallback {
 void matchSuccess(SwipeCaptchaView swipeCaptchaView);
 void matchFailed(SwipeCaptchaView swipeCaptchaView);
 }
 /**
 * 驗證碼驗證的回調(diào)
 */
 private OnCaptchaMatchCallback onCaptchaMatchCallback;
 public OnCaptchaMatchCallback getOnCaptchaMatchCallback() {
 return onCaptchaMatchCallback;
 }
 /**
 * 設(shè)置驗證碼驗證回調(diào)
 *
 * @param onCaptchaMatchCallback
 * @return
 */
 public SwipeCaptchaView setOnCaptchaMatchCallback(OnCaptchaMatchCallback onCaptchaMatchCallback) {
 this.onCaptchaMatchCallback = onCaptchaMatchCallback;
 return this;
 }
 /**
 * 校驗
 */
 public void matchCaptcha() {
 if (null != onCaptchaMatchCallback && isMatchMode) {
 //這里驗證邏輯,是通過比較,拖拽的距離 和 驗證碼起點x坐標(biāo)。 默認(rèn)3dp以內(nèi)算是驗證成功。
 if (Math.abs(mDragerOffset - mCaptchaX) < mMatchDeviation) {
 //成功的動畫
 mSuccessAnim.start();
 } else {
 mFailAnim.start();
 }
 }
 }

成功、失敗的回調(diào)是在動畫結(jié)束時通知的。

動畫:

動畫里要用到寬高,所以它是在onSizeChanged()方法里被調(diào)用的。

//驗證動畫初始化區(qū)域
 private void createMatchAnim() {
 mFailAnim = ValueAnimator.ofFloat(0, 1);
 mFailAnim.setDuration(100)
 .setRepeatCount(4);
 mFailAnim.setRepeatMode(ValueAnimator.REVERSE);
 //失敗的時候先閃一閃動畫 斗魚是 隱藏-顯示 -隱藏 -顯示
 mFailAnim.addListener(new AnimatorListenerAdapter() {
 @Override
 public void onAnimationEnd(Animator animation) {
 onCaptchaMatchCallback.matchFailed(SwipeCaptchaView.this);
 }
 });
 mFailAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
 @Override
 public void onAnimationUpdate(ValueAnimator animation) {
 float animatedValue = (float) animation.getAnimatedValue();
 if (animatedValue < 0.5f) {
  isDrawMask = false;
 } else {
  isDrawMask = true;
 }
 invalidate();
 }
 });
 int width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics());
 mSuccessAnim = ValueAnimator.ofInt(mWidth + width, 0);
 mSuccessAnim.setDuration(500);
 mSuccessAnim.setInterpolator(new FastOutLinearInInterpolator());
 mSuccessAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
 @Override
 public void onAnimationUpdate(ValueAnimator animation) {
 mSuccessAnimOffset = (int) animation.getAnimatedValue();
 invalidate();
 }
 });
 mSuccessAnim.addListener(new AnimatorListenerAdapter() {
 @Override
 public void onAnimationStart(Animator animation) {
 isShowSuccessAnim = true;
 }
 @Override
 public void onAnimationEnd(Animator animation) {
 onCaptchaMatchCallback.matchSuccess(SwipeCaptchaView.this);
 isShowSuccessAnim = false;
 isMatchMode = false;
 }
 });
 mSuccessPaint = new Paint();
 mSuccessPaint.setShader(new LinearGradient(0, 0, width, 0, new int[]{
 0x11ffffff, 0x88ffffff}, null,
 Shader.TileMode.MIRROR));
 //模仿斗魚 是一個平行四邊形滾動過去
 mSuccessPath = new Path();
 mSuccessPath.moveTo(0, 0);
 mSuccessPath.rLineTo(width, 0);
 mSuccessPath.rLineTo(width / 2, mHeight);
 mSuccessPath.rLineTo(-width, 0);
 mSuccessPath.close();
 }

代碼很簡單,修改的一些布爾值flag,在onDraw()方法里會用到,結(jié)合onDraw()一看便懂。

Demo

這一節(jié),我們聯(lián)動SeekBar滑動起來。

xml如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
 ......
>
 <com.mcxtzhang.captchalib.SwipeCaptchaView
 android:id="@+id/swipeCaptchaView"
 android:layout_width="300dp"
 android:layout_height="150dp"
 android:layout_centerHorizontal="true"
 android:scaleType="centerCrop"
 android:src="@drawable/pic11"
 app:captchaHeight="30dp"
 app:captchaWidth="30dp"/>
 <SeekBar
 android:id="@+id/dragBar"
 android:layout_width="320dp"
 android:layout_height="60dp"
 android:layout_below="@id/swipeCaptchaView"
 android:layout_centerHorizontal="true"
 android:layout_marginTop="30dp"
 android:progressDrawable="@drawable/dragbg"
 android:thumb="@drawable/thumb_bg"/>
 <Button
 android:id="@+id/btnChange"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_alignParentRight="true"
 android:text="老板換碼"/>
</RelativeLayout>

UI就是文首那張圖的樣子,

完整Activity代碼:

public class MainActivity extends AppCompatActivity {
 SwipeCaptchaView mSwipeCaptchaView;
 SeekBar mSeekBar;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 mSwipeCaptchaView = (SwipeCaptchaView) findViewById(R.id.swipeCaptchaView);
 mSeekBar = (SeekBar) findViewById(R.id.dragBar);
 findViewById(R.id.btnChange).setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 mSwipeCaptchaView.createCaptcha();
 mSeekBar.setEnabled(true);
 mSeekBar.setProgress(0);
 }
 });
 mSwipeCaptchaView.setOnCaptchaMatchCallback(new SwipeCaptchaView.OnCaptchaMatchCallback() {
 @Override
 public void matchSuccess(SwipeCaptchaView swipeCaptchaView) {
 Toast.makeText(MainActivity.this, "恭喜你啊 驗證成功 可以搞事情了", Toast.LENGTH_SHORT).show();
 mSeekBar.setEnabled(false);
 }
 @Override
 public void matchFailed(SwipeCaptchaView swipeCaptchaView) {
 Toast.makeText(MainActivity.this, "你有80%的可能是機器人,現(xiàn)在走還來得及", Toast.LENGTH_SHORT).show();
 swipeCaptchaView.resetCaptcha();
 mSeekBar.setProgress(0);
 }
 });
 mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
 @Override
 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
 mSwipeCaptchaView.setCurrentSwipeValue(progress);
 }
 @Override
 public void onStartTrackingTouch(SeekBar seekBar) {
 //隨便放這里是因為控件
 mSeekBar.setMax(mSwipeCaptchaView.getMaxSwipeValue());
 }
 @Override
 public void onStopTrackingTouch(SeekBar seekBar) {
 Log.d("zxt", "onStopTrackingTouch() called with: seekBar = [" + seekBar + "]");
 mSwipeCaptchaView.matchCaptcha();
 }
 });
 //從網(wǎng)絡(luò)加載圖片也ok
 Glide.with(this)
 .load("http://www.investide.cn/data/edata/image/20151201/20151201180507_281.jpg")
 .asBitmap()
 .into(new SimpleTarget<Bitmap>() {
  @Override
  public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
  mSwipeCaptchaView.setImageBitmap(resource);
  mSwipeCaptchaView.createCaptcha();
  }
 });
 }
}

總結(jié)

代碼傳送門 喜歡的話,隨手點個star。多謝

https://github.com/mcxtzhang/SwipeCaptcha

包含完整Demo和SwipeCaptchaView。

利用一些工具發(fā)現(xiàn)web端斗魚,驗證碼圖片和滑塊圖片都是接口返回的。

推測前端其實只返回后臺:用戶移動的距離或者距離的百分比。

本例完全由前端實現(xiàn)驗證碼生成、驗證功能,是因為:

1 練習(xí)自定義VIew,自己全部實現(xiàn)摳圖 驗證 繪制,感覺很酷。

2 我不會做后臺,手動微笑。

核心點:

1 不規(guī)則圖形Path的生成。

2 指定Path對Bitmap摳圖,抗鋸齒。

3 適配ImageView的ScaleType。

4 成功、失敗的動畫

以上所述是小編給大家介紹的Android 高仿斗魚滑動驗證碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關(guān)文章

  • Android中okhttp3使用詳解

    Android中okhttp3使用詳解

    這篇文章主要介紹了Android中okhttp3使用詳解,非常具有實用價值,需要的朋友可以參考下
    2017-09-09
  • Kotlin條件控制語句匯總講解

    Kotlin條件控制語句匯總講解

    條件控制是每門編程語言中必不可少的,一般就是使用我們所熟知的 ifelse ,來作為我們代碼邏輯選擇條件控制。 在 Java 中一般使用 ifelse 和 switch-case 來作為條件控制,而在 Kotlin 中則是使用 if-else 和 when 來作為條件控制
    2022-09-09
  • Android中解決WebView上下滑動監(jiān)聽問題

    Android中解決WebView上下滑動監(jiān)聽問題

    本篇文章主要介紹了Android中解決WebView滑動監(jiān)聽問題,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • 詳解Android客戶端與服務(wù)器交互方式

    詳解Android客戶端與服務(wù)器交互方式

    這篇文章主要介紹了詳解Android客戶端與服務(wù)器交互方式-小結(jié),具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-01-01
  • Android ViewPager小圓點指示器

    Android ViewPager小圓點指示器

    這篇文章主要為大家詳細(xì)介紹了Android ViewPager小圓點指示器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • Android WebView 詳解及簡單實例

    Android WebView 詳解及簡單實例

    這篇文章主要介紹了Android WebView 詳解及簡單實例的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • Android View 事件防抖的兩種方案

    Android View 事件防抖的兩種方案

    這篇文章主要介紹了Android View 事件防抖的兩種方案,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下
    2021-04-04
  • Android圓角設(shè)置方法看著一篇文章就夠了

    Android圓角設(shè)置方法看著一篇文章就夠了

    我們在實際工作中,android經(jīng)常有需要實現(xiàn)圓角的場景,下面這篇文章主要給大家介紹了關(guān)于Android圓角設(shè)置方法的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì)需要的朋友可以參考下
    2023-02-02
  • Flutter開發(fā)之動態(tài)權(quán)限的使用

    Flutter開發(fā)之動態(tài)權(quán)限的使用

    眾所周知,Android在6.0版本后將權(quán)限修改成了動態(tài)權(quán)限,而iOS則一直使用的是動態(tài)權(quán)限,所以在Flutter應(yīng)用開發(fā)中如果涉及到一些危險權(quán)限,就需要進行動態(tài)申請,本文就詳細(xì)的介紹一下,感興趣的可以了解一下
    2021-09-09
  • Android支持國際化多語言那點事(支持8.0+)

    Android支持國際化多語言那點事(支持8.0+)

    我們在開發(fā)app可能會拓展國外市場,那么對包含英語在內(nèi)的其它語言支持就很有必要了。這篇文章主要介紹了Android支持國際化多語言那點事(支持8.0+),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-06-06

最新評論