Android實(shí)現(xiàn)支付寶AR掃描動(dòng)畫效果
支付寶AR掃描效果動(dòng)畫實(shí)現(xiàn),具體內(nèi)容如下
之前一個(gè)網(wǎng)友說(shuō)想要一個(gè)支付寶掃描動(dòng)畫的效果demo,所以又花了點(diǎn)時(shí)間做了下這個(gè)東西,先看效果圖

說(shuō)一下實(shí)現(xiàn)的思路,如圖中最外圍的藍(lán)色的是用兩個(gè)相距180°的圓弧實(shí)現(xiàn)的,再往里又是兩個(gè)紅色的圓弧再往里面是一個(gè)紅色的圓,最里面的白色的是由4個(gè)間隔的白色圓弧組成的,其實(shí)說(shuō)明白的就是簡(jiǎn)單的圖形的堆積.然后通過控制繪制圓弧的起始角度進(jìn)行旋轉(zhuǎn)的動(dòng)畫.掃描的紅色線條是一張漸變的圖片,通過平移動(dòng)畫實(shí)現(xiàn)掃描的效果.
這個(gè)自定義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;//藍(lán)色圈的畫筆
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;//中心點(diǎn)X坐標(biāo)
private int centerY;//中心點(diǎn)Y坐標(biāo)
private Bitmap scanerbitmap;//條形掃描圖片
private int curState = STATE_SCANING;//初始狀態(tài)
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;
//獲取直徑和半徑以及中心帶你坐標(biāo)方便后面的計(jì)算
minLength = Math.min(width, height);
radius = minLength / 2;
}
/**
* 公開方法設(shè)置當(dāng)前的狀態(tài)值
* @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;//藍(lán)色圓圈的開始角度
private int blueCircleSpace = BLUE_CIRCLE_BORDER_WIDTH;//藍(lán)色弧距離最短邊的距離用于計(jì)算自身的半徑用
private static final int BLUE_CIRCLE_SWEP_ANGLE = 20;//藍(lán)色弧掃過的角度
/**
* 繪制藍(lán)色弧
* @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);
}
/**
* 根據(jù)當(dāng)前的狀態(tài)來(lái)更改變量達(dá)到動(dòng)畫的效果
*/
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;
/**
* 繪制內(nèi)部的紅色圓圈
* @param canvas
*/
private void drawInsiderRedCircle(Canvas canvas) {
canvas.drawCircle(centerX, centerY, radius - insideRedCircleSpace, redCirclePaint);
}
/**
* 繪制外部紅色的兩個(gè)弧
* @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);
}
}
下面是我偶讀github地址
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android Shader應(yīng)用開發(fā)之雷達(dá)掃描效果
- Android應(yīng)用中使用ContentProvider掃描本地圖片并顯示
- Android實(shí)現(xiàn)掃描和生成二維碼
- Android實(shí)現(xiàn)掃描二維碼功能
- Android studio 實(shí)現(xiàn)手機(jī)掃描二維碼功能
- Android如何實(shí)現(xiàn)掃描和生成二維碼
- Android銀行卡掃描獲取銀行卡號(hào)
- Android實(shí)現(xiàn)銀行卡號(hào)掃描識(shí)別功能
- Android 6.0 掃描不到 Ble 設(shè)備需開啟位置權(quán)限的方法
- Android手機(jī)(設(shè)備)連接掃描槍掃碼遇到的問題
- Android編程實(shí)現(xiàn)wifi掃描及連接的方法
- Android 二維碼掃描和生成二維碼功能
- Android 開機(jī)應(yīng)用掃描相關(guān)總結(jié)
相關(guān)文章
如何為RecyclerView添加Header和Footer
這篇文章主要為大家詳細(xì)介紹了如何為RecyclerView添加Header和Footer,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
Android 使用AlarmManager和NotificationManager來(lái)實(shí)現(xiàn)鬧鐘和通知欄
這篇文章主要介紹了Android 使用AlarmManager和NotificationManager來(lái)實(shí)現(xiàn)鬧鐘和通知欄,需要的朋友可以參考下2017-02-02
詳解Android如何設(shè)計(jì)一個(gè)全局可調(diào)用的ViewModel對(duì)象
很多時(shí)候我們需要維護(hù)一個(gè)全局可用的ViewModel,因?yàn)檫@樣可以維護(hù)全局同一份數(shù)據(jù)源,且方便使用協(xié)程綁定App的生命周期,那如何設(shè)計(jì)全局可用的ViewModel對(duì)象,文中介紹的非常詳細(xì),需要的朋友可以參考下2023-05-05

