android實(shí)現(xiàn)支付寶咻一咻的幾種思路方法
支付寶咻一咻在過年的時(shí)候很火熱。那么咻一咻具體有哪些實(shí)現(xiàn)方式呢?下面我們將一一介紹這幾種思路的實(shí)現(xiàn)過程。
1.自定義View實(shí)現(xiàn)咻一咻
那么這種實(shí)現(xiàn)方法需要掌握Canvas以及Paint幾乎所有的方法。其對(duì)程序員的專業(yè)知識(shí)要求極高。
用該種方式實(shí)現(xiàn)的優(yōu)點(diǎn)有:
㈠這種是最復(fù)雜的實(shí)現(xiàn)方法,但其兼容性最高,其支持Android的所有設(shè)備。
㈡其對(duì)內(nèi)存要求不大,幾乎不占用任何內(nèi)存。
下面我們來看看是怎樣實(shí)現(xiàn)其效果的:
public class XiuYiXiuView extends View {
/***
* 中心圖片畫筆
*/
private Paint paint;
/***
* 水波圓圈畫筆
*/
private Paint circlePaint;
/***
* 用bitmap創(chuàng)建畫布
*/
private Bitmap bitmap;
/***
* 中心圖片
*/
private Bitmap imageBit;
/***
* 畫布
*/
private Canvas canvas;
/***
* 屏幕的寬
*/
private int screenWidth;
/***
* 屏幕的高
*/
private int screenHeight;
/***
* 圖片右上角坐標(biāo)
*/
private Point pointLeftTop;
/***
* 圖片右下角坐標(biāo)
*/
private Point pointRightBottom;
/***
* 記錄圓圈
*/
private List<LYJCircle> lyjCircleList;
/***
* 標(biāo)記是否按下按鈕,并且源泉是否擴(kuò)散消失
*/
private boolean isSpread=false;
/***
* 默認(rèn)沒有按動(dòng)時(shí)候的圓圈
*/
private LYJCircle defaultCircle;
public XiuYiXiuView(Context context, AttributeSet attrs) {
super(context, attrs);
this.lyjCircleList=new ArrayList<>();
screenWidth=LYJUtils.getScreenWidth((Activity) context);
screenHeight=LYJUtils.getScreenHeight((Activity) context);
bitmap = Bitmap.createBitmap(screenWidth, screenHeight, Bitmap.Config.ARGB_8888); // 設(shè)置位圖的寬高
canvas = new Canvas();
canvas.setBitmap(bitmap);
paint=new Paint(Paint.DITHER_FLAG);
paint.setAntiAlias(true);
circlePaint=new Paint(Paint.DITHER_FLAG);
circlePaint.setAntiAlias(true);
imageBit= BitmapFactory.decodeResource(getResources(), R.drawable.bwa_homepage_yuyin);
pointLeftTop=new Point((screenWidth/2)-(imageBit.getWidth()/2),(screenHeight/2)-(imageBit.getHeight()/2));
pointRightBottom=new Point(pointLeftTop.x+imageBit.getWidth(),pointLeftTop.y+imageBit.getHeight());
canvas.drawBitmap(imageBit,pointLeftTop.x,pointLeftTop.y,paint);
//取圖片上的顏色
Palette.generateAsync(imageBit, new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
Palette.Swatch swatch1 = palette.getVibrantSwatch(); //充滿活力的色板
circlePaint.setColor(swatch1.getRgb());
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeWidth(10);
circlePaint.setAlpha(100);
paint.setShadowLayer(15, 0, 0, swatch1.getRgb());//設(shè)置陰影效果
int[] mColors = new int[] {//渲染顏色
Color.TRANSPARENT,swatch1.getRgb()
};
//范圍,這里可以微調(diào),實(shí)現(xiàn)你想要的漸變
float[] mPositions = new float[] {
0f, 0.1f
};
Shader shader=new RadialGradient(screenWidth / 2,screenHeight / 2,imageBit.getWidth() / 2 + 10,mColors, mPositions,
Shader.TileMode.MIRROR);
circlePaint.setShader(shader);
defaultCircle=new LYJCircle(screenWidth / 2, screenHeight / 2, imageBit.getWidth() / 2 + 10);
clearScreenAndDrawList();
Message message = handler.obtainMessage(1);
handler.sendMessageDelayed(message, 1000); //發(fā)送message
}
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
isSpread=true;//是否按下圖片
lyjCircleList.add(new LYJCircle(screenWidth / 2, screenHeight / 2, imageBit.getWidth() / 2 + 10));
clearScreenAndDrawList();
invalidate();
break;
default:
break;
}
return true;
}
private Handler handler = new Handler(){
public void handleMessage(Message msg){
switch (msg.what) {
case 1:
//定時(shí)更新界面
clearScreenAndDrawList();
invalidate();
Message message = handler.obtainMessage(1);
handler.sendMessageDelayed(message, 200);
}
super.handleMessage(msg);
}
};
/**
* 清掉屏幕上所有的圓圈,然后畫出集合里面的圓圈
*/
private void clearScreenAndDrawList() {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
//判斷是否按下圖片,并且外圈執(zhí)行完成沒有。
if(!isSpread){
circlePaint.setMaskFilter(null);
canvas.drawCircle(defaultCircle.getRoundX(), defaultCircle.getRoundY(),defaultCircle.getRadiuLoop(), circlePaint);// 畫線
}else{
for (LYJCircle lyjCircle : lyjCircleList) {
if(lyjCircle.getSpreadRadiu()==0){
}else if(lyjCircle.getSpreadRadiu()>(lyjCircle.getRadiu()+99)){
//如果圓圈擴(kuò)散半徑大于圖片半徑+99,那么設(shè)置邊緣模糊,也就是淡出的效果
circlePaint.setMaskFilter(new BlurMaskFilter(5, BlurMaskFilter.Blur.OUTER));
canvas.drawCircle(lyjCircle.getRoundX(), lyjCircle.getRoundY(),lyjCircle.getSpreadRadiu(), circlePaint);// 畫線
}else{
//不是則按正常的環(huán)形渲染來
circlePaint.setMaskFilter(null);
canvas.drawCircle(lyjCircle.getRoundX(), lyjCircle.getRoundY(),lyjCircle.getSpreadRadiu(), circlePaint);// 畫線
}
}
}
canvas.drawBitmap(imageBit,pointLeftTop.x,pointLeftTop.y,paint);
//釋放小時(shí)了的圓圈
for(int i=0;i<lyjCircleList.size();i++){
if(lyjCircleList.get(i).getSpreadRadiu()==0){
lyjCircleList.remove(i);
}
}
//如果沒有點(diǎn)擊圖片發(fā)射出去的圓圈,那么就恢復(fù)默認(rèn)縮放。
if(lyjCircleList.size()<=0){
isSpread=false;
}
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap, 0, 0, null);
}
}
圓類:
package com.example.liyuanjing.model;
/**
* Created by liyuanjing on 2016/2/3.
*/
public class LYJCircle {
private int roundX;//圓中心點(diǎn)X坐標(biāo)
private int roundY;//圓中心點(diǎn)Y坐標(biāo)
private int radiu;//圓半徑
private int currentRadiu;//當(dāng)前radiu
private int lastRadiu;//歷史radiu
private int spreadRadiu;//加速半徑
private int[] speed=new int[]{6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6};//半徑擴(kuò)大速度。這里為勻速
private int speedLast=0;//記錄歷史值
public LYJCircle(int roundX,int roundY,int radiu){
this.roundX=roundX;
this.roundY=roundY;
this.radiu=radiu;
this.spreadRadiu=radiu;
this.currentRadiu=this.radiu;
this.lastRadiu=this.currentRadiu;
}
//獲取半徑
public int getRadiu() {
return radiu;
}
public void setRadiu(int radiu) {
this.radiu = radiu;
}
//獲取加速半徑
public int getSpreadRadiu(){
if(speedLast>=speed.length){
return 0;
}
spreadRadiu+=speed[speedLast];
++speedLast;
return spreadRadiu;
}
//獲取循環(huán)縮放半徑
public int getRadiuLoop() {
if(currentRadiu==lastRadiu){
++currentRadiu;
}else if(currentRadiu>lastRadiu){
if(currentRadiu>(radiu+20)){
currentRadiu=19+radiu;
lastRadiu=20+radiu;
}else{
lastRadiu=currentRadiu;
currentRadiu+=5;
}
}else{
if(currentRadiu<(radiu+9)){
currentRadiu=10+radiu;
lastRadiu=9+radiu;
}else{
lastRadiu=currentRadiu;
currentRadiu-=5;
}
}
return currentRadiu;
}
public int getRoundX() {
return roundX;
}
public int getRoundY() {
return roundY;
}
}
看看其效果圖:

你可以修改如下兩個(gè)地方,會(huì)產(chǎn)生視覺上真真的波紋效果:
①支付寶的背景圖片是淡紅色,襯托了紅色的波紋。當(dāng)然了你也可以將畫布設(shè)置為透明淡紅色。
②其為填充圓圈渲染,不是我的邊框渲染效果,你可以將circlePaint.setStyle(Paint.Style.STROKE);換成Paint.Style.FILL.然后,微調(diào)shader的mPositions實(shí)現(xiàn)環(huán)形填充漸變。你也許會(huì)覺得,你看支付寶咻一咻圓圈彈開的時(shí)候內(nèi)圈有波紋也像外彈開,其實(shí)那就是環(huán)形漸變,當(dāng)你圓圈變大后,其漸變的范圍也就變大了,自然你看到有顏色周圍擴(kuò)散的跡象。
2.屬性動(dòng)畫實(shí)現(xiàn)咻一咻
其要掌握的只是基本只需要屬性動(dòng)畫,在加一點(diǎn)線程方面有關(guān)的知識(shí)而已。
下面我們看看其實(shí)現(xiàn)步驟:
㈠自定義View實(shí)現(xiàn)一個(gè)圓即可,代碼如下:
public class LYJCircleView extends View {
private Bitmap bitmap;
private Paint paint;
private Canvas canvas;
private int screenWidth;
private int screenHeight;
private boolean isSpreadFlag=false;//標(biāo)記是否發(fā)射完成
public boolean isSpreadFlag() {
return isSpreadFlag;
}
public void setIsSpreadFlag(boolean isSpreadFlag) {
this.isSpreadFlag = isSpreadFlag;
}
public LYJCircleView(Context context,int width,int height,int statusHeight) {
super(context);
screenWidth= LYJUtils.getScreenWidth((Activity) context);
screenHeight=LYJUtils.getScreenHeight((Activity) context);
bitmap = Bitmap.createBitmap(screenWidth, screenHeight, Bitmap.Config.ARGB_8888); // 設(shè)置位圖的寬高
canvas = new Canvas();
canvas.setBitmap(bitmap);
paint=new Paint(Paint.DITHER_FLAG);
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
paint.setAlpha(100);
paint.setShadowLayer(10, 0, 0, Color.RED);
int[] mColors = new int[] {
Color.TRANSPARENT,Color.RED
};
float[] mPositions = new float[] {
0f, 0.1f
};
Shader shader=new RadialGradient(screenWidth / 2,screenHeight / 2,width / 2 + 10,mColors, mPositions,
Shader.TileMode.MIRROR);
paint.setShader(shader);
canvas.drawCircle(screenWidth / 2, (screenHeight - statusHeight) / 2, width / 2 + 10, paint);
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap,0,0,null);
}
}
代碼與上面差不多,就不注釋了。
㈡實(shí)現(xiàn)Activity即可
public class XiuYiXiuActivity extends AppCompatActivity {
private ImageButton mImageButton;
private LYJCircleView lyjCircleView;
private RelativeLayout relativeLayout;
private List<LYJCircleView> lyjCircleViewList;
private int statusBarHeight;
private Animator anim;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.xiuyixiu_activity_main);
this.mImageButton=(ImageButton)findViewById(R.id.xiuyixiu_imagebutton);
this.relativeLayout=(RelativeLayout)findViewById(R.id.xiuyixiu_relativelayout);
this.lyjCircleViewList=new ArrayList<>();
this.mImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
lyjCircleView.setVisibility(View.GONE);//發(fā)射圓圈,即將循環(huán)動(dòng)畫View隱藏
final LYJCircleView item=new LYJCircleView(XiuYiXiuActivity.this, mImageButton.getWidth(), mImageButton.getHeight(), statusBarHeight);
Animator spreadAnim = AnimatorInflater.loadAnimator(XiuYiXiuActivity.this, R.animator.circle_spread_animator);
spreadAnim.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
item.setIsSpreadFlag(true);//動(dòng)畫執(zhí)行完成,標(biāo)記一下
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
spreadAnim.setTarget(item);
spreadAnim.start();
lyjCircleViewList.add(item);
relativeLayout.addView(item);
relativeLayout.invalidate();
Message message = handler.obtainMessage(1);
handler.sendMessageDelayed(message, 10); //發(fā)送message,定時(shí)釋放LYJCircleView
}
});
}
private Handler handler = new Handler(){
public void handleMessage(Message msg){
switch (msg.what) {
case 1:
for(int i=0;i<lyjCircleViewList.size();i++){
if(lyjCircleViewList.get(i).isSpreadFlag()){
relativeLayout.removeView(lyjCircleViewList.get(i));
lyjCircleViewList.remove(i);
relativeLayout.invalidate();
}
}
if(lyjCircleViewList.size()<=0){
lyjCircleView.setVisibility(View.VISIBLE);
}
Message message = handler.obtainMessage(1);
handler.sendMessageDelayed(message, 10);
}
super.handleMessage(msg);
}
};
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
//獲取狀態(tài)欄高度
Rect frame = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
statusBarHeight = frame.top;
this.mImageButton.post(new Runnable() {
@Override
public void run() {
lyjCircleView = new LYJCircleView(XiuYiXiuActivity.this, mImageButton.getWidth(), mImageButton.getHeight(), statusBarHeight);
relativeLayout.addView(lyjCircleView);
relativeLayout.postInvalidate();
// 加載動(dòng)畫
anim = AnimatorInflater.loadAnimator(XiuYiXiuActivity.this, R.animator.circle_scale_animator);
anim.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
anim.start();//循環(huán)執(zhí)行動(dòng)畫
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
anim.setTarget(lyjCircleView);
anim.start();
}
});
}
}
㈢布局文件代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/xiuyixiu_relativelayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageButton
android:id="@+id/xiuyixiu_imagebutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/bwa_homepage_yuyin"/>
</RelativeLayout>
當(dāng)然上面兩個(gè)實(shí)現(xiàn)方法,我都只設(shè)置圓邊框,沒有填充,你可以設(shè)置為填充后,在微調(diào)漸變值。
其屬性動(dòng)畫文件circle_scale_animator.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="together">
<objectAnimator
android:duration="1000"
android:propertyName="scaleX"
android:valueFrom="1.0"
android:valueTo="1.2"
android:valueType="floatType">
</objectAnimator>
<objectAnimator
android:duration="1000"
android:propertyName="scaleY"
android:valueFrom="1.0"
android:valueTo="1.2"
android:valueType="floatType">
</objectAnimator>
<objectAnimator
android:startOffset="1000"
android:duration="1000"
android:propertyName="scaleX"
android:valueFrom="1.2"
android:valueTo="1.0"
android:valueType="floatType">
</objectAnimator>
<objectAnimator
android:startOffset="1000"
android:duration="1000"
android:propertyName="scaleY"
android:valueFrom="1.2"
android:valueTo="1.0"
android:valueType="floatType">
</objectAnimator>
</set>
另一個(gè)circle_spread_animator.xml為:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="1000"
android:propertyName="scaleY"
android:valueFrom="1.0"
android:valueTo="2.0"
android:valueType="floatType">
</objectAnimator>
<objectAnimator
android:duration="1000"
android:propertyName="scaleX"
android:valueFrom="1.0"
android:valueTo="2.0"
android:valueType="floatType">
</objectAnimator>
</set>
其效果圖如下:

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android中ListView下拉刷新的實(shí)現(xiàn)方法
這篇文章主要為大家詳細(xì)介紹了Android中ListView下拉刷新的實(shí)現(xiàn)方法,感興趣的小伙伴們可以參考一下2016-03-03
詳談Android中onTouch與onClick事件的關(guān)系(必看)
下面小編就為大家?guī)硪黄斦凙ndroid中onTouch與onClick事件的關(guān)系(必看)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-03-03
Android8.1原生系統(tǒng)網(wǎng)絡(luò)感嘆號(hào)消除的方法
這篇文章主要介紹了Android8.1原生系統(tǒng)網(wǎng)絡(luò)感嘆號(hào)消除的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-05-05
Android progressbar實(shí)現(xiàn)帶底部指示器和文字的進(jìn)度條
這篇文章主要為大家詳細(xì)介紹了Android progressbar實(shí)現(xiàn)帶底部指示器和文字的進(jìn)度條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
實(shí)例講解Android中ContentProvider組件的使用方法
這篇文章主要介紹了Android中ContentProvider組件的使用方法,包括ContentProvider使用單元測(cè)試的步驟,需要的朋友可以參考下2016-04-04
Android AlertDialog對(duì)話框用法示例
這篇文章主要介紹了Android AlertDialog對(duì)話框用法,結(jié)合實(shí)例形式分析了AlertDialog對(duì)話框的功能及常見使用技巧,需要的朋友可以參考下2016-06-06
Android四種數(shù)據(jù)存儲(chǔ)的應(yīng)用方式
這篇文章主要介紹了Android四種數(shù)據(jù)存儲(chǔ)的應(yīng)用方式的相關(guān)資料,希望通過本文能幫助到大家,讓大家理解掌握Android存儲(chǔ)數(shù)據(jù)的方法,需要的朋友可以參考下2017-10-10
Android獲取手機(jī)通訊錄、sim卡聯(lián)系人及調(diào)用撥號(hào)界面方法
這篇文章主要介紹了Android獲取手機(jī)通訊錄、sim卡聯(lián)系人及調(diào)用撥號(hào)界面方法,本文分別給出實(shí)現(xiàn)代碼實(shí)現(xiàn)獲取通訊錄和sim卡的聯(lián)系人,以及權(quán)限配置和調(diào)用系統(tǒng)撥打電話的界面的實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-04-04

