实现支付宝AR扫描效果动画
之前一个网友说想要一个支付宝扫描动画的效果demo,所以又花了点时间做了下这个东西,先看效果图
说一下实现的思路,如图中最外围的蓝色的是用两个相距180°的圆弧实现的,再往里又是两个红色的圆弧再往里面是一个红色的圆,最里面的白色的是由4个间隔的白色圆弧组成的,其实说明白的就是简单的图形的堆积.然后通过控制绘制圆弧的起始角度进行旋转的动画.扫描的红色线条是一张渐变的图片,通过平移动画实现扫描的效果.
这个自定义View 的代码如下:
package cn.com.hadon.scanner;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by Mr.Wang on 2017/5/8.
*/
public class ScanView extends View {
private Paint blueCirclePaint;//蓝色圈的画笔
private Paint redCirclePaint;//红色圈的画笔
private Paint whiteCirclePaint;//白色圈的画笔
public static final int STATE_READY = 1;
public static final int STATE_SCANING = 2;
public static final int STATE_SUCCESS = 3;
//定义圆弧的宽度
private static final int BLUE_CIRCLE_BORDER_WIDTH = 8;
private static final int INSIDER_RED_CIRCLE_BORDER_WIDTH = 20;
private static final int OUTSIDER_CIRCLE_BORDER_WIDTH = 20;
private static final int WHITE_CIRCLE_BORDER_WIDTH = 20;
private int minLength;//中心最大圆的直径
private int radius;//中心最大圆的半径
private int centerX;//中心点X坐标
private int centerY;//中心点Y坐标
private Bitmap scanerbitmap;//条形扫描图片
private int curState = STATE_SCANING;//初始状态
public ScanView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//初始化一些变量
scanerbitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.scaner);
blueCirclePaint = new Paint();
redCirclePaint = new Paint();
whiteCirclePaint = new Paint();
blueCirclePaint.setColor(Color.BLUE);
blueCirclePaint.setAntiAlias(true);
blueCirclePaint.setStyle(Paint.Style.STROKE);
blueCirclePaint.setStrokeWidth(BLUE_CIRCLE_BORDER_WIDTH);
redCirclePaint.setColor(Color.RED);
redCirclePaint.setAntiAlias(true);
redCirclePaint.setStyle(Paint.Style.STROKE);
redCirclePaint.setStrokeWidth(INSIDER_RED_CIRCLE_BORDER_WIDTH);
whiteCirclePaint.setColor(Color.WHITE);
whiteCirclePaint.setAntiAlias(true);
whiteCirclePaint.setStyle(Paint.Style.STROKE);
whiteCirclePaint.setStrokeWidth(WHITE_CIRCLE_BORDER_WIDTH);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
centerX = width / 2;
centerY = height / 2;
//获取直径和半径以及中心带你坐标方便后面的计算
minLength = Math.min(width, height);
radius = minLength / 2;
}
/**
* 公开方法设置当前的状态值
* @param state
*/
public void setState(int state) {
this.curState = state;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.TRANSPARENT);
switch (curState) {
case STATE_READY:
drawWhiteCircle(canvas);
drawInsiderRedCircle(canvas);
drawOutsiderRedCircle(canvas);
break;
case STATE_SCANING:
drawWhiteCircle(canvas);
drawBlueCircle(canvas);
drawInsiderRedCircle(canvas);
drawOutsiderRedCircle(canvas);
drawScaner(canvas);
break;
case STATE_SUCCESS:
drawWhiteCircle(canvas);
drawInsiderRedCircle(canvas);
break;
}
updateValues();
invalidate();
}
private int blueStartAngle = 0;//蓝色圆圈的开始角度
private int blueCircleSpace = BLUE_CIRCLE_BORDER_WIDTH;//蓝色弧距离最短边的距离用于计算自身的半径用
private static final int BLUE_CIRCLE_SWEP_ANGLE = 20;//蓝色弧扫过的角度
/**
* 绘制蓝色弧
* @param canvas
*/
private void drawBlueCircle(Canvas canvas) {
canvas.drawArc(centerX - radius + blueCircleSpace, blueCircleSpace, centerX + radius - blueCircleSpace, minLength - blueCircleSpace, blueStartAngle, BLUE_CIRCLE_SWEP_ANGLE, false, blueCirclePaint);
canvas.drawArc(centerX - radius + blueCircleSpace, blueCircleSpace, centerX + radius - blueCircleSpace, minLength - blueCircleSpace, blueStartAngle + 180, BLUE_CIRCLE_SWEP_ANGLE, false, blueCirclePaint);
}
/**
* 根据当前的状态来更改变量达到动画的效果
*/
private void updateValues() {
switch (curState) {
case STATE_READY:
if (insideRedCircleSpace >= BLUE_CIRCLE_BORDER_WIDTH + INSIDER_RED_CIRCLE_BORDER_WIDTH+ OUTSIDER_CIRCLE_BORDER_WIDTH) {
insideRedCircleSpace -= 2;
}
whiteStartAngle = 5 / 2;
outsiderRedCircleStartAndle = -OUTSIDER_RED_CIRCLE_SWEP_ANGLE / 2;
break;
case STATE_SCANING:
if (insideRedCircleSpace >= BLUE_CIRCLE_BORDER_WIDTH + INSIDER_RED_CIRCLE_BORDER_WIDTH+ OUTSIDER_CIRCLE_BORDER_WIDTH) {
insideRedCircleSpace -= 2;
}
blueStartAngle += 4;
outsiderRedCircleStartAndle += 2;
if (is2Max) {
if (whiteStartAngle == 30) {
is2Max = false;
} else {
whiteStartAngle++;
}
} else {
if (whiteStartAngle == -30) {
is2Max = true;
} else {
whiteStartAngle--;
}
}
scanerY += 6;
if (scanerY > minLength) {
scanerY = 0;
}
break;
case STATE_SUCCESS:
whiteStartAngle = 5 / 2;
if (insideRedCircleSpace < whiteCircleSpace + INSIDER_RED_CIRCLE_BORDER_WIDTH) {
insideRedCircleSpace += 2;
}
break;
}
}
private int insideRedCircleSpace = BLUE_CIRCLE_BORDER_WIDTH + INSIDER_RED_CIRCLE_BORDER_WIDTH + OUTSIDER_CIRCLE_BORDER_WIDTH;
private int outsiderRedCircleSpace = BLUE_CIRCLE_BORDER_WIDTH + INSIDER_RED_CIRCLE_BORDER_WIDTH + OUTSIDER_CIRCLE_BORDER_WIDTH / 2;
private static final int OUTSIDER_RED_CIRCLE_SWEP_ANGLE = 30;
private int outsiderRedCircleStartAndle = -OUTSIDER_RED_CIRCLE_SWEP_ANGLE / 2;
/**
* 绘制内部的红色圆圈
* @param canvas
*/
private void drawInsiderRedCircle(Canvas canvas) {
canvas.drawCircle(centerX, centerY, radius - insideRedCircleSpace, redCirclePaint);
}
/**
* 绘制外部红色的两个弧
* @param canvas
*/
private void drawOutsiderRedCircle(Canvas canvas) {
canvas.drawArc(centerX - radius + outsiderRedCircleSpace, outsiderRedCircleSpace, centerX + radius - outsiderRedCircleSpace, minLength - outsiderRedCircleSpace, outsiderRedCircleStartAndle, OUTSIDER_RED_CIRCLE_SWEP_ANGLE, false, redCirclePaint);
canvas.drawArc(centerX - radius + outsiderRedCircleSpace, outsiderRedCircleSpace, centerX + radius - outsiderRedCircleSpace, minLength - outsiderRedCircleSpace, outsiderRedCircleStartAndle + 180, OUTSIDER_RED_CIRCLE_SWEP_ANGLE, false, redCirclePaint);
}
private int whiteStartAngle = 0;
private static final int WHITE_CIRCLE_SWEP_ANGLE = 85;
private int whiteCircleSpace = BLUE_CIRCLE_BORDER_WIDTH + INSIDER_RED_CIRCLE_BORDER_WIDTH + OUTSIDER_CIRCLE_BORDER_WIDTH + WHITE_CIRCLE_BORDER_WIDTH;
private RectF whiteCircleRect;
private boolean is2Max = true;
/**
* 绘制白色的弧
* @param canvas
*/
private void drawWhiteCircle(Canvas canvas) {
if (whiteCircleRect == null) {
whiteCircleRect = new RectF(centerX - radius + whiteCircleSpace, whiteCircleSpace, centerX + radius - whiteCircleSpace, minLength - whiteCircleSpace);
}
canvas.drawArc(whiteCircleRect, whiteStartAngle, WHITE_CIRCLE_SWEP_ANGLE, false, whiteCirclePaint);
canvas.drawArc(whiteCircleRect, whiteStartAngle + 90, WHITE_CIRCLE_SWEP_ANGLE, false, whiteCirclePaint);
canvas.drawArc(whiteCircleRect, whiteStartAngle + 180, WHITE_CIRCLE_SWEP_ANGLE, false, whiteCirclePaint);
canvas.drawArc(whiteCircleRect, whiteStartAngle + 270, WHITE_CIRCLE_SWEP_ANGLE, false, whiteCirclePaint);
}
private int scanerY = 0;
/**
* 绘制扫描图片
* @param canvas
*/
private void drawScaner(Canvas canvas) {
int p1, p2, hw;
if (scanerY >= radius) {
p1 = (scanerY - radius) * (scanerY - radius);
} else {
p1 = (radius - scanerY) * (radius - scanerY);
}
p2 = radius * radius;
hw = (int) Math.sqrt(p2 - p1);
Rect rect = new Rect(centerX - hw, scanerY - 10, centerX + hw, scanerY + 10);
canvas.drawBitmap(scanerbitmap, null, rect, null);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
下面是我偶读github地址