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