大咖好,博主毕业工作半年多了。今天给大噶呈献博主博客处女作——Android拼图滑块验证码控件。由于初写博客,很多地方可能不够好,望各位多多给点意见。工作半年才送出第一篇博客很惭愧555。

概述

验证码是可以区分用户是人还是计算机。可以防止破解密码、刷票等恶意行为。客户端上多数用在关键操作上,比如购买、登录、注册等场景。本文将介绍Android拼图滑块验证码控件是如何一步一步编写形成的。希望能帮助到大家。
       当然,如果仅仅是实现效果如何实现就太没技术含量了。本文将通过介绍如何编写此控件的同时,介绍Android自定义控件的一些技巧。

效果图

分析

由效果图可以看到一张图片,一个可拖动滑块条,拖动滑块条,滑块拼图跟随滑动,滑动拼图到正确位置并松开手指,图下方会出现验证信息,滑动到错误位置并松开,也会出现验证信息。
      看到这样的效果,很多人估计都能看出来,这是一个ViewGroup包装了一个ImageView和Seekbar。没错,控件确实是LinearLayout包裹着一个ImageView和Seekar,然而Android自带的ImageView和Seekbar并不能实现效果,需要改造。如何改造后面再说。现在来看看整体控件的结构:

从图看到,控件是由PictureVertifyView和TextSeekbar组合在一个Captcha里面。也就是前面所说的一个LinearLayout包裹一个ImageView和Seekbar,并将这些形成一个小团体。其中团体的老大我就叫他Captcha,PictureVertifyView和TextSeekbar就是他的小跟班,社团跟客户谈业务只能由老大进行,老大将工作吩咐给跟班,跟班们根据自己的能力做自己的本职,PictureVertifyView就是做拼图这块的,TextSeekbar就是做滑动条这块的,最终组合成一个能完成客户需求的整体。
      废话不多说,开始写干货。

实现

1. 编写PictureVertifyView类

PictureVertifyView就是效果图上部分的拼图块,也是Captcha控件的核心。主要完成拼图的绘制,逻辑。

1.1PictureVertifyView类的状态

首先PictureVertifyView有6种状态(点击、滑动、松开手指、验证成功、验证失败、初始化),这些状态将影响PictureVertifyView的绘制。至于怎么影响,后面将会说明。

    private static final int STATE_DOWN = 1;private static final int STATE_MOVE = 2;private static final int STATE_LOOSEN = 3;private static final int STATE_IDEL = 4;private static final int STATE_ACCESS = 5;private static final int STATE_UNACCESS = 6;private int mState = STATE_IDEL;
 //手指按下void down(int progress) {startTouchTime = System.currentTimeMillis();mState = STATE_DOWN;currentPosition = (int) (progress / 100f * (getWidth() -                     Utils.dp2px(getContext(), 50)));invalidate();}//手指滑动,改变缺块图片位置void move(int progress) {mState = STATE_MOVE;currentPosition = (int) (progress / 100f * (getWidth() - Utils.dp2px(getContext(), 50)));invalidate();}//手指松开,进行验证void loose() {mState = STATE_LOOSEN;looseTime = System.currentTimeMillis();checkAccess();invalidate();}//复位到初始状态,当复位的初始状态,所有东西都会重置void reset() {mState = STATE_IDEL;verfityBlock.recycle();verfityBlock = null;info = null;blockShape = null;invalidate();}//验证不通过void unAccess() {mState = STATE_UNACCESS;invalidate();}//验证通过void access() {mState = STATE_ACCESS;invalidate();}
1.2拼图缺块位置信息生成

拼图缺块信息既是拼图缺块的位置,这里将封装成一个实体类PositionINfo如下:

 public class PositionInfo {//缺块在整张图片的左上角x轴位置int left;//缺块在整张图片的左上角y轴位置int top;public PositionInfo(int left, int top) {this.left = left;this.top = top;}}

对于验证码拼图缺块的位置是要在图片范围内随机生成的。代码如下:

 @Overridepublic PositionInfo getBlockPostionInfo(int width, int height) {Random random = new Random();int edge = Utils.dp2px(getContext(), 50);int left = random.nextInt(width - edge);//Avoid robot frequently and quickly click the start point to access the captcha.if (left < edge) {left = edge;}int top = random.nextInt(height - edge);if (top < 0) {top = 0;}return new PositionInfo(left, top);}

这里面对缺块作了边界限制,如下图:

从图上看到,图片的左上角的点坐标限制在里面的矩形中,这样做的目的是防止缺块超出图片边界。看到这里有人要疑惑,为了防止缺块超出整张图片范围,限制右、下边界可以理解,为什么还要限制左边界。原因很简单,Captcha是基于按下和松开TextSeekbar来判断是否要进行验证。万一机器人模拟快速点击滑动条的初始位置,而缺块正好又生成在图片最左边,这样不就不需要滑动就能通过验证了吗。虽然缺块生成在图片的最左边几率很小,但是我还是拒绝这种欧气的产生。

1.3缺块的形状

缺块的形状就是一个Path类,如何编写?这还用问吗?你弄个圆也行,弄个三角形也行,这里我就弄个不知道名字的形状好了,代码如下:

    public Path getBlockShape(int blockSize) {int gap = Utils.dp2px(getContext(), blockSize/5f);Path path = new Path();path.moveTo(0, gap);path.rLineTo(Utils.dp2px(getContext(), blockSize/2.5f), 0);path.rLineTo(0, -gap);path.rLineTo(gap, 0);path.rLineTo(0, gap);path.rLineTo(2 * gap, 0);path.rLineTo(0, 4 * gap);path.rLineTo(-5 * gap, 0);path.rLineTo(0, -1.5f * gap);path.rLineTo(gap, 0);path.rLineTo(0, -gap);path.rLineTo(-gap, 0);path.close();return path;}
//不过注意了,你设计的Path的狂傲要限制在blockSize(缺块大小)内。
1.4生成缺块图片

上面介绍了缺块的形状,这里我们讲讲如何生成缺块图片。缺块图片是要在原图根据缺块形状裁的,至于怎么裁,代码如下:

 private Bitmap createBlockBitmap() {//创建一张白纸Bitmap tempBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);//将这张白纸片放入画布中Canvas canvas = new Canvas(tempBitmap);//由于PictureVertifyView是继承ImageView,这里我们直接拿到ImageView的Drawable图片并限制其宽高以达到Drawable是和ImageView的宽高是一致的getDrawable().setBounds(0, 0, getWidth(), getHeight());//把画布的可画范围限制在缺块形状中,当然这个blockShape已经根据缺块位置进行偏移canvas.clipPath(blockShape);//画画getDrawable().draw(canvas);//先忽略下面这句好吗mStrategy.decoreateSwipeBlockBitmap(canvas,blockShape);return cropBitmap(tempBitmap);}

这时tempBitmap是这样的:外矩形是透明的,内矩形就是缺块的内容,而整张tempBitmap的大小跟ImageView的大小一样,很多无用面积,而我们需要的是里面那张。别急cropBitmp方法就是帮我们裁出里面那部分。

    private Bitmap cropBitmap(Bitmap bmp) {Bitmap result = null;int size = Utils.dp2px(getContext(), blockSize);//一句代码就裁出来了,就是这么简单result = Bitmap.createBitmap(bmp, info.left, info.top, size, size);bmp.recycle();return result;}
1.5 绘制

在看绘制代码前,我们先来看看下面这张图,看看怎么绘制。

实际上绘制并不复杂,就是在ImageView的基础上加上一个缺块的阴影遮盖原来图片的一部分,再加上裁出来的缺块图片。由于本控件的滑动条是水平滑动的,所以缺块图片和缺块阴影在同一水平线上,只是水平位置不同。验证是否通过是判断currentPostion和left是否大概相等。怎么个大概?我代码是写10个像素。
      好了,贴代码:

@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制前,我们要判断是否有缺块的位置信息,缺块形状和图片,没有的话要先获得它们的实例,其中mStrategy的作用,我们后面将会说到。if (info == null) {info = mStrategy.getBlockPostionInfo(getWidth(), getHeight());}if (blockShape == null) {blockShape = mStrategy.getBlockShape(blockSize);blockShape.offset(info.left, info.top);}if (verfityBlock == null) {verfityBlock = createBlockBitmap();}//上文已经说过,状态会影响绘制内容,其中当状态为非验证成功时,会绘制阴影,当状态为滑动或初始时绘制滑动的缺块图片(写代码最怕为改名字,有些名字很尴尬,请多多包涵)if (mState != STATE_ACCESS) {canvas.drawPath(blockShape, shadowPaint);}if (mState == STATE_MOVE || mState == STATE_IDEL) canvas.drawBitmap(verfityBlock, currentPosition, info.top, bitmapPaint);}}
1.6 验证算法

前面提到,当松开手指,缺块图片和缺块阴影重合的时候就是验证通过,否则失败,其代码很简单:

private void checkAccess() {
//判断currentPostion(滑块水平位置)和info.left(阴影水平位置)是否在容差以内if (Math.abs(currentPosition - info.left) < TOLERANCE) {access();//listener监听器用于监听验证成功失败事件if (listener != null) {//小操作,用于记录用户验证所需要的时间,详细看工程代码long deltaTime = looseTime - startTouchTime;listener.onAccess(deltaTime);}} else {unAccess();if (listener != null) {listener.onFailed();}}}

2.编写TextSeekbar

TextSeekbar就是效果图下面那个滑动条,它相当于团队里老板的市场调研,观察客户的喜好再告诉给老板,老板再叫PictureVertifyView做什么。TextSeekbar继承于Seekbar,功能很简单,就是在绘制完Seekbar的时候多绘制一行字,代码如下:

@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawText("向右滑动滑块完成拼图", getWidth() / 2, getHeight() / 2 + textPaint.getTextSize() / 2 - 4, textPaint);}
//注意,这个地方有点偷工减料了,文字垂直方向的偏移并没有准确计算,只是在手机上调试视觉上就是差不多这样,这里深感抱歉。

3.编写Captcha老大

前文说过,Captcha就是团队老大,外面客户谈业务只能跟它谈,再将工作分配给小弟。Captcha就相当于团队的门面,其实门面嘛是可有可无的。有了PictureVertifyView和TextSeekbar就可以进行业务工作,但是国不能一日无君,社团不能一日无老大。老大是对内统筹下属工作,对外是外界与团队进行沟通的对象。
      Captcha其实就是一个LinearLayout包裹PictureVertifyView和TextSeekbar的组合控件,当然也可以是FrameLayout等。
      Captcha的编写与编写组合控件的步骤一样,编写xml布局文件,编写控制逻辑。

3.1xml布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@drawable/container_backgroud"android:padding="10dp"android:orientation="vertical"><FrameLayout
        android:layout_width="match_parent"android:paddingLeft="10dp"android:paddingRight="10dp"android:layout_height="200dp"><com.luozm.captcha.PictureVertifyView
            android:id="@+id/vertifyView"android:layout_width="match_parent"android:layout_height="200dp"android:scaleType="fitXY" /><LinearLayout
            android:visibility="gone"android:id="@+id/accessRight"android:background="#7F000000"android:orientation="horizontal"android:layout_gravity="bottom"android:layout_width="match_parent"android:layout_height="28dp"><ImageView
                android:src="@drawable/right"android:layout_marginLeft="10dp"android:layout_width="20dp"android:layout_gravity="center"android:layout_height="20dp" /><TextView
                android:id="@+id/accessText"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="10dp"android:textColor="#FFFFFF"android:text="验证通过,耗时1000毫秒"android:textSize="14sp"/></LinearLayout><LinearLayout
            android:id="@+id/accessFailed"android:background="#7F000000"android:orientation="horizontal"android:visibility="gone"android:layout_gravity="bottom"android:layout_width="match_parent"android:layout_height="28dp"><ImageView
                android:src="@drawable/wrong"android:layout_marginLeft="10dp"android:layout_width="20dp"android:layout_gravity="center"android:layout_height="20dp" /><TextView
                android:id="@+id/accessFailedText"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="10dp"android:textColor="#FFFFFF"android:textSize="14sp"/></LinearLayout></FrameLayout><com.luozm.captcha.TextSeekbar
        android:id="@+id/seekbar"android:layout_gravity="center"style="@style/MySeekbarSytle"android:splitTrack="false"android:thumbOffset="0dp"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="16dp" /></LinearLayout>

布局很简单,除了前文介绍的TextSeekbar和PictureVertifyView外还有一个覆盖在PictureVertifyView底部的用于显示验证信息的布局。

3.2控制逻辑

Captcha的作用就是统筹子控件的逻辑,相当于一个Activity界面统筹各个控件工作完成一个功能。在这里,Captcha主要是通过TextSeekbar的拖动事件触发PictureVertifyView状态的改变。其代码如下:

seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {if (isDown) {isDown = false;if (progress > 10) {isResponse = false;} else {isResponse = true;accessFailed.setVisibility(GONE);vertifyView.down(0);}}if (isResponse) {vertifyView.move(progress);} else {seekBar.setProgress(0);isResponse = false;}}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {isDown = true;}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {if (isResponse) {vertifyView.loose();}}});

由于Android自带的Seekbar的progress值是随用户手指的位置改变,即使手指并非按下滑动块也能改变其值。因此,上面代码作了用户按下滑动块才能拖动,避免progress值的瞬变。

4.策略模式

刚写这个控件的时候,只是为了完成项目中登录验证的功能,PictureVertifyView的拼图形状,拼图效果都是写死的。而为了让控件的使用者可以自行定制想要的拼图效果,采用策略模式对控件进行大改造,将定义拼图效果的方法移至策略类当中。其类图大概如下图:

      读者在前文中应该留意到mStrategy这个引用。这就是PictureVertifyView通过引用CaptchaStrategy的一个实现类从而确定自己要用到的拼图效果。CaptchaStrategy的代码如下:

public abstract class CaptchaStrategy {protected Context mContext;public CaptchaStrategy(Context ctx) {this.mContext = ctx;}protected Context getContext() {return mContext;}/*** 定义缺块的形状** @param blockSize 单位dp,注意转化为px* @return path of the shape*/public abstract Path getBlockShape(int blockSize);/*** 定义缺块的位置信息生成算法** @param width  picture width* @param height picture height* @return position info of the block*/public abstract PositionInfo getBlockPostionInfo(int width, int height);/*** 获得缺块阴影的Paint*/public abstract Paint getBlockShadowPaint();/*** 获得滑块图片的Paint*/public abstract Paint getBlockBitmapPaint();/*** 装饰滑块图片,在绘制图片后执行,即绘制滑块前景*/public void decoreateSwipeBlockBitmap(Canvas canvas,Path shape) {}
}

它的默认实现类为DefaultCaptchaStrategy,代码如下:

public class DefaultCaptchaStrategy extends CaptchaStrategy {public DefaultCaptchaStrategy(Context ctx) {super(ctx);}@Overridepublic Path getBlockShape(int blockSize) {int gap = Utils.dp2px(getContext(), blockSize/5f);Path path = new Path();path.moveTo(0, gap);path.rLineTo(Utils.dp2px(getContext(), blockSize/2.5f), 0);path.rLineTo(0, -gap);path.rLineTo(gap, 0);path.rLineTo(0, gap);path.rLineTo(2 * gap, 0);path.rLineTo(0, 4 * gap);path.rLineTo(-5 * gap, 0);path.rLineTo(0, -1.5f * gap);path.rLineTo(gap, 0);path.rLineTo(0, -gap);path.rLineTo(-gap, 0);path.close();return path;}@Overridepublic PictureVertifyView.PositionInfo getBlockPostionInfo(int width, int height) {Random random = new Random();int edge = Utils.dp2px(getContext(), 50);int left = random.nextInt(width - edge);//Avoid robot frequently and quickly click the start point to access the captcha.if (left < edge) {left = edge;}int top = random.nextInt(height - edge);if (top < 0) {top = 0;}return new PositionInfo(left, top);}@Overridepublic Paint getBlockShadowPaint() {Paint shadowPaint = new Paint();shadowPaint.setColor(Color.parseColor("#000000"));shadowPaint.setAlpha(165);return shadowPaint;}@Overridepublic Paint getBlockBitmapPaint() {Paint paint = new Paint();return paint;}@Overridepublic void decoreateSwipeBlockBitmap(Canvas canvas, Path shape) {Paint paint = new Paint();paint.setColor(Color.parseColor("#FFFFFF"));paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(10);paint.setPathEffect(new DashPathEffect(new float[]{20,20},10));Path path = new Path(shape);canvas.drawPath(path,paint);}
}

总结

通过拼图滑块验证码控件的编写,了解到自定义控件的一些技巧和步骤。此外还能免费在项目中用到拼图验证码(因为市面上网易的云盾验证码,极验都是付费的)。
      最后,给伸手党一个福利,控件已上传到jcenter。使用者可移至gayhub查看使用。
Captcha Github

Android拼图滑块验证码控件相关推荐

  1. Android滑块拼图验证码控件

    转自  王小冉的文章:https://blog.csdn.net/w690333243/article/details/90311187 https://github.com/luozhanming/ ...

  2. Android 滑动拼图验证码控件

    Android 滑动拼图验证码控件 简介: 很多软件为了安全防止恶意攻击,会在登录/注册时进行人机验证,常见的人机验证方式有:谷歌点击复选框进行验证,输入验证码验证,短信验证码,语音验证,文字按顺序选 ...

  3. 小视频app源码,Android 滑动拼图验证码控件

    小视频app源码,Android 滑动拼图验证码控件 代码实现: 滑块视图类:SlideImageView.java.实现小视频APP源码随机选取拼图位置,对拼图位置进行验证等功能. public c ...

  4. Android开源库集合(控件)

    RecycleView: RecycleView功能增强 https://github.com/Malinskiy/SuperRecyclerView RecycleView功能增强(拖拽,滑动删除, ...

  5. [ASP.NET 控件实作 Day28] 图形验证码控件

    在网页上常把图形验证码应用在登入或贴文的页面中,因为图形验证码具有机器不易识别的特性,可以防止机器人程序恶意的存取网页.在本文中将实作一个图形验证码的服务器控件,透过简单的属性设定就可以轻易地在网页上 ...

  6. Android中的基础控件TextView、Button、ImageView、EditText、ProgressBar

    文章目录 1 Android中的基础控件 1.1 控件的通用属性 2 TextView 2.1 TextView的继承关系 2.2 TextView的常用属性 3 EditText 3.1 常用属性 ...

  7. android中的标题栏是什么意思,Android通用标题栏组合控件

    原标题:Android通用标题栏组合控件 快,点击蓝色"字体"关注这个公众号,一起涨姿势 由于项目中经常用到此种组合控件,就封装了下,具体效果看下图,老司机可以绕道哈! 一.主要功 ...

  8. Android中进度条控件使用

    android中进度条控件使用 ProgressBar pb = findViewById(R.id.pb);pb.setMax(100);pb.setProgress(33); 转载于:https: ...

  9. ux.form.field.Verify 验证码控件

    1 //验证码控件 2 Ext.define('ux.form.field.Verify', { 3 extend: 'Ext.container.Container', 4 alias: ['wid ...

最新文章

  1. SAP MM 给合同的ITEM上传附件以及附件查询
  2. 微信公众平台开发(55)刮刮乐
  3. Win7下Android模拟器中没有3G网络信号的解决办法
  4. gitee 从 拉取新分支到本地_Hexo博客详细教程(一)| 建立本地站点
  5. hive安装需要安装mysql区别_HIVE安装系列之一:在Linux中安装mysql,为其作为hive的metastore做准备...
  6. 计算机应用基础理论,计算机应用基础理论A卷.doc
  7. 190906二级刷题水果与小女孩
  8. Jquery 触发器之treigger()方法简介
  9. 新一代的桌面虚拟化技术
  10. FreeNAS存储+iscsi
  11. verilog之编程应该注意的事项
  12. 下载silverlight官网的全部视频教程
  13. nyoj1110 一个简单数学题
  14. linux之SVN安装
  15. 捷联惯导算法 matlab,捷联惯导算法与组合导航原理资料及更正
  16. 2019中国国际大数据产业博览会将5月26-29日贵阳举行
  17. 未将对象引用设置到对象的实例 解决办法
  18. Unity 计算屏幕视口UV坐标的几种方法
  19. 梧桐树王牌产品金玉满堂增额终身寿险下架在即,资产焦虑就买它
  20. Gitee的第一次使用(图文)

热门文章

  1. nRF52832之硬件I2C
  2. 人工智能是什么?人工智能行业前景如何
  3. sin cos tan c语言,sin cos tan在前端的应用
  4. 4.28time和OS系统
  5. 【C++初阶】简析拷贝构造、赋值运算符重载
  6. 【Python】Python简单的图片识别
  7. java 金额比较大小写_Java金额大小写的转换方法
  8. 选下拉框的的值对应上传相应的图片_excel表格下拉菜单调用对应数据,如何在excel中实现,选择下拉菜单某一项,该表格中就出现选项对应的数据?...
  9. 数据分析师需要学习哪些技能?
  10. 利用百度地图采集大量某一区域经纬度信息