//生成验证码Path
private void createCaptchaPath() {
//原本打算随机生成gap,后来发现 宽度/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);
//从左上角开始 绘制一个不规则的阴影
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();
}
/**
* 传入起点、终点 坐标、凹凸和Path。
* 会自动绘制凹凸的半圆弧
*
* @param start 起点坐标
* @param end 终点坐标
* @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;//旋转系数
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;//旋转系数
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);
}
/*
没推导之前的公式在这里
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);
}
}*/
}
}
//生成滑块
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);
//把创建的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);
//设置遮罩模式(图像混合模式)
mMaskPaint.setXfermode(mPorterDuffXfermode);
//★考虑到scaleType等因素,要用Matrix对Bitmap进行缩放
mCanvas.drawBitmap(mBitmap, getImageMatrix(), mMaskPaint);
mMaskPaint.setXfermode(null);
return tempBitmap;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//继承自ImageView,所以Bitmap,ImageView已经帮我们draw好了。
//我只在上面绘制和验证码相关的部分,
//是否处于验证模式,在验证成功后 为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 = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); mPaint.setColor(0x77000000); //mPaint.setStyle(Paint.Style.STROKE); // 设置画笔遮罩滤镜 mPaint.setMaskFilter(new BlurMaskFilter(20, BlurMaskFilter.Blur.SOLID));
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();
/**
* 重置验证码滑动距离,(一般用于验证失败)
*/
public void resetCaptcha() {
mDragerOffset = 0;
invalidate();
}
/**
* 最大可滑动值
* @return
*/
public int getMaxSwipeValue() {
//return ((BitmapDrawable) getDrawable()).getBitmap().getWidth() - mCaptchaWidth;
//返回控件宽度
return mWidth - mCaptchaWidth;
}
/**
* 设置当前滑动值
* @param value
*/
public void setCurrentSwipeValue(int value) {
mDragerOffset = value;
invalidate();
}
public interface OnCaptchaMatchCallback {
void matchSuccess(SwipeCaptchaView swipeCaptchaView);
void matchFailed(SwipeCaptchaView swipeCaptchaView);
}
/**
* 验证码验证的回调
*/
private OnCaptchaMatchCallback onCaptchaMatchCallback;
public OnCaptchaMatchCallback getOnCaptchaMatchCallback() {
return onCaptchaMatchCallback;
}
/**
* 设置验证码验证回调
*
* @param onCaptchaMatchCallback
* @return
*/
public SwipeCaptchaView setOnCaptchaMatchCallback(OnCaptchaMatchCallback onCaptchaMatchCallback) {
this.onCaptchaMatchCallback = onCaptchaMatchCallback;
return this;
}
/**
* 校验
*/
public void matchCaptcha() {
if (null != onCaptchaMatchCallback && isMatchMode) {
//这里验证逻辑,是通过比较,拖拽的距离 和 验证码起点x坐标。 默认3dp以内算是验证成功。
if (Math.abs(mDragerOffset - mCaptchaX) < mMatchDeviation) {
//成功的动画
mSuccessAnim.start();
} else {
mFailAnim.start();
}
}
}
//验证动画初始化区域
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();
}
<?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>
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%的可能是机器人,现在走还来得及", 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();
}
});
//从网络加载图片也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();
}
});
}
}
机械节能产品生产企业官网模板...
大气智能家居家具装修装饰类企业通用网站模板...
礼品公司网站模板
宽屏简约大气婚纱摄影影楼模板...
蓝白WAP手机综合医院类整站源码(独立后台)...苏ICP备2024110244号-2 苏公网安备32050702011978号 增值电信业务经营许可证编号:苏B2-20251499 | Copyright 2018 - 2025 源码网商城 (www.ymwmall.com) 版权所有