实现Android支付宝声波支付时的波纹视图

转载 2016年08月31日 12:07:19

前言

自从支付宝声波支付的波纹效果出来以后,这种形式就慢慢流行开来,比如各种安全软件在扫描时会采用这种动画效果,这种波纹荡漾起来也是增加了动感十足呢,如图1。


                                 

图1 

今天我们就来学习如何实现这种波纹效果,以及最大限度的支持低版本的系统。

波纹实现

看到这种效果,最直接的感官就是波纹视图慢慢的变大、并且颜色变淡,因此我在第一次摸索的过程中直接继承自View,然后开启一个线程来计算这个视图的此时的大小以及颜色值,效果可以出来,但是有点卡。后面搜索了一些资料,发现有更好的方式可以实现。

新的方式就是使用属性动画,但是属性动画在api 11及其以上才支持,因此这里我们使用了NineOldAnimations动画库。基本原理就是自定义一个布局,在这个布局中会添加几个背景视图,也就是上述效果中的圆形视图,然后用户再指定一个自己的视图,如上如中的支付按钮。当用户点击支付按钮时,启动动画。此时,几个背景视图就会执行一个属性动画集,这些背景视图的x, y轴都会放大,同时视图的alpha属性会慢慢的变小。这样就产生了视图变大、颜色慢慢淡化的效果,如图2所示。



图 2 


代码实现 : 

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /* 
  2.  * The MIT License (MIT) 
  3.  * 
  4.  * Copyright (c) 2014-2015 bboyfeiyu@gmail.com 
  5.  * 
  6.  * Permission is hereby granted, free of charge, to any person obtaining a copy 
  7.  * of this software and associated documentation files (the "Software"), to deal 
  8.  * in the Software without restriction, including without limitation the rights 
  9.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
  10.  * copies of the Software, and to permit persons to whom the Software is 
  11.  * furnished to do so, subject to the following conditions: 
  12.  * 
  13.  * The above copyright notice and this permission notice shall be included in 
  14.  * all copies or substantial portions of the Software. 
  15.  *  
  16.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
  17.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
  18.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
  19.  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
  20.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
  21.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
  22.  * THE SOFTWARE. 
  23.  */  
  24.   
  25. package org.simple.ripple;  
  26.   
  27. import android.content.Context;  
  28. import android.content.res.TypedArray;  
  29. import android.graphics.Canvas;  
  30. import android.graphics.Color;  
  31. import android.graphics.Paint;  
  32. import android.util.AttributeSet;  
  33. import android.view.View;  
  34. import android.view.animation.AccelerateDecelerateInterpolator;  
  35. import android.widget.RelativeLayout;  
  36.   
  37. import com.nineoldandroids.animation.Animator;  
  38. import com.nineoldandroids.animation.AnimatorSet;  
  39. import com.nineoldandroids.animation.ObjectAnimator;  
  40.   
  41. import org.simple.ripplelayout.R;  
  42.   
  43. import java.util.ArrayList;  
  44.   
  45. /** 
  46.  * 这是一个类似支付宝声波支付的波纹效果布局,该布局中默认添加了不可见的圆形的视图,启动动画时会启动缩放、颜色渐变动画使得产生波纹效果. 
  47.  * 这些动画都是无限循环的,并且每个View的动画之间都有时间间隔,这些时间间隔就会导致视图有大有小,从而产生波纹的效果. 
  48.  *  
  49.  * @author mrsimple 
  50.  */  
  51. public class RippleLayout extends RelativeLayout {  
  52.   
  53.     /** 
  54.      * static final fields 
  55.      */  
  56.     private static final int DEFAULT_RIPPLE_COUNT = 6;  
  57.     private static final int DEFAULT_DURATION_TIME = 3000;  
  58.     private static final float DEFAULT_SCALE = 4.0f;  
  59.     private static final int DEFAULT_RIPPLE_COLOR = Color.rgb(0x330x990xcc);  
  60.     private static final int DEFAULT_STROKE_WIDTH = 0;  
  61.     private static final int DEFAULT_RADIUS = 60;  
  62.   
  63.     /** 
  64.      * 
  65.      */  
  66.     private int mRippleColor = DEFAULT_RIPPLE_COLOR;  
  67.     private float mStrokeWidth = DEFAULT_STROKE_WIDTH;  
  68.     private float mRippleRadius = DEFAULT_RADIUS;  
  69.     private int mAnimDuration;  
  70.     private int mRippleViewNums;  
  71.     private int mAnimDelay;  
  72.     private float mRippleScale;  
  73.     private boolean animationRunning = false;  
  74.     /** 
  75.      * 
  76.      */  
  77.     private Paint mPaint = new Paint();  
  78.   
  79.     /** 
  80.      * 动画集,执行缩放、alpha动画,使得背景色渐变 
  81.      */  
  82.     private AnimatorSet mAnimatorSet = new AnimatorSet();  
  83.     /** 
  84.      * 动画列表,保存几个动画 
  85.      */  
  86.     private ArrayList<Animator> mAnimatorList = new ArrayList<Animator>();  
  87.     /** 
  88.      * RippleView Params 
  89.      */  
  90.     private LayoutParams mRippleViewParams;  
  91.   
  92.     /** 
  93.      * @param context 
  94.      */  
  95.     public RippleLayout(Context context) {  
  96.         super(context);  
  97.         init(context, null);  
  98.     }  
  99.   
  100.     public RippleLayout(Context context, AttributeSet attrs) {  
  101.         super(context, attrs);  
  102.         init(context, attrs);  
  103.     }  
  104.   
  105.     public RippleLayout(Context context, AttributeSet attrs, int defStyleAttr) {  
  106.         super(context, attrs, defStyleAttr);  
  107.         init(context, attrs);  
  108.     }  
  109.   
  110.     private void init(final Context context, final AttributeSet attrs) {  
  111.         if (isInEditMode()) {  
  112.             return;  
  113.         }  
  114.   
  115.         if (null != attrs) {  
  116.             initTypedArray(context, attrs);  
  117.         }  
  118.   
  119.         initPaint();  
  120.         initRippleViewLayoutParams();  
  121.         generateRippleViews();  
  122.   
  123.     }  
  124.   
  125.     private void initTypedArray(Context context, AttributeSet attrs) {  
  126.         final TypedArray typedArray = context.obtainStyledAttributes(attrs,  
  127.                 R.styleable.RippleLayout);  
  128.         //  
  129.         mRippleColor = typedArray.getColor(R.styleable.RippleLayout_color,  
  130.                 DEFAULT_RIPPLE_COLOR);  
  131.         mStrokeWidth =  
  132.                 typedArray.getDimension(R.styleable.RippleLayout_strokeWidth, DEFAULT_STROKE_WIDTH);  
  133.         mRippleRadius = typedArray.getDimension(R.styleable.RippleLayout_radius,  
  134.                 DEFAULT_RADIUS);  
  135.         mAnimDuration = typedArray.getInt(R.styleable.RippleLayout_duration,  
  136.                 DEFAULT_DURATION_TIME);  
  137.         mRippleViewNums = typedArray.getInt(R.styleable.RippleLayout_rippleNums,  
  138.                 DEFAULT_RIPPLE_COUNT);  
  139.         mRippleScale = typedArray.getFloat(R.styleable.RippleLayout_scale,  
  140.                 DEFAULT_SCALE);  
  141.   
  142.         // oh, baby, don't forget recycle the typedArray !!  
  143.         typedArray.recycle();  
  144.     }  
  145.   
  146.     private void initPaint() {  
  147.         mPaint = new Paint();  
  148.         mPaint.setAntiAlias(true);  
  149.         mStrokeWidth = 0;  
  150.         mPaint.setStyle(Paint.Style.FILL);  
  151.         mPaint.setColor(mRippleColor);  
  152.     }  
  153.   
  154.     private void initRippleViewLayoutParams() {  
  155.         // ripple view的大小为 半径 + 笔宽的两倍  
  156.         int rippleSide = (int) (2 * (mRippleRadius + mStrokeWidth));  
  157.         mRippleViewParams = new LayoutParams(rippleSide, rippleSide);  
  158.         // 居中显示  
  159.         mRippleViewParams.addRule(CENTER_IN_PARENT, TRUE);  
  160.     }  
  161.   
  162.     /** 
  163.      * 计算每个RippleView之间的动画时间间隔,从而产生波纹效果 
  164.      */  
  165.     private void calculateAnimDelay() {  
  166.         mAnimDelay = mAnimDuration / mRippleViewNums;  
  167.     }  
  168.   
  169.     /** 
  170.      * 初始化RippleViews,并且将动画设置到RippleView上,使之在x, y不断扩大,并且背景色逐渐淡化 
  171.      */  
  172.     private void generateRippleViews() {  
  173.   
  174.         calculateAnimDelay();  
  175.         initAnimSet();  
  176.         // 添加RippleView  
  177.         for (int i = 0; i < mRippleViewNums; i++) {  
  178.             RippleView rippleView = new RippleView(getContext());  
  179.             addView(rippleView, mRippleViewParams);  
  180.             // 添加动画  
  181.             addAnimToRippleView(rippleView, i);  
  182.         }  
  183.   
  184.         // x, y, alpha动画一块执行  
  185.         mAnimatorSet.playTogether(mAnimatorList);  
  186.     }  
  187.   
  188.     private void initAnimSet() {  
  189.         mAnimatorSet.setDuration(mAnimDuration);  
  190.         mAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator());  
  191.     }  
  192.   
  193.     /** 
  194.      * 为每个RippleView添加动画效果,并且设置动画延时,每个视图启动动画的时间不同,就会产生波纹 
  195.      *  
  196.      * @param rippleView 
  197.      * @param i 视图所在的索引 
  198.      */  
  199.     private void addAnimToRippleView(RippleView rippleView, int i) {  
  200.   
  201.         // x轴的缩放动画  
  202.         final ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(rippleView, "scaleX",  
  203.                 1.0f, mRippleScale);  
  204.         scaleXAnimator.setRepeatCount(ObjectAnimator.INFINITE);  
  205.         scaleXAnimator.setRepeatMode(ObjectAnimator.RESTART);  
  206.         scaleXAnimator.setStartDelay(i * mAnimDelay);  
  207.         scaleXAnimator.setDuration(mAnimDuration);  
  208.         mAnimatorList.add(scaleXAnimator);  
  209.   
  210.         // y轴的缩放动画  
  211.         final ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(rippleView, "scaleY",  
  212.                 1.0f, mRippleScale);  
  213.         scaleYAnimator.setRepeatMode(ObjectAnimator.RESTART);  
  214.         scaleYAnimator.setRepeatCount(ObjectAnimator.INFINITE);  
  215.         scaleYAnimator.setStartDelay(i * mAnimDelay);  
  216.         scaleYAnimator.setDuration(mAnimDuration);  
  217.         mAnimatorList.add(scaleYAnimator);  
  218.   
  219.         // 颜色的alpha渐变动画  
  220.         final ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(rippleView, "alpha"1.0f,  
  221.                 0f);  
  222.         alphaAnimator.setRepeatMode(ObjectAnimator.RESTART);  
  223.         alphaAnimator.setRepeatCount(ObjectAnimator.INFINITE);  
  224.         alphaAnimator.setDuration(mAnimDuration);  
  225.         alphaAnimator.setStartDelay(i * mAnimDelay);  
  226.         mAnimatorList.add(alphaAnimator);  
  227.     }  
  228.   
  229.     public void startRippleAnimation() {  
  230.         if (!isRippleAnimationRunning()) {  
  231.             makeRippleViewsVisible();  
  232.             mAnimatorSet.start();  
  233.             animationRunning = true;  
  234.         }  
  235.     }  
  236.   
  237.     private void makeRippleViewsVisible() {  
  238.         int childCount = this.getChildCount();  
  239.         for (int i = 0; i < childCount; i++) {  
  240.             View childView = this.getChildAt(i);  
  241.             if (childView instanceof RippleView) {  
  242.                 childView.setVisibility(VISIBLE);  
  243.             }  
  244.         }  
  245.     }  
  246.   
  247.     public void stopRippleAnimation() {  
  248.         if (isRippleAnimationRunning()) {  
  249.             mAnimatorSet.end();  
  250.             animationRunning = false;  
  251.         }  
  252.     }  
  253.   
  254.     public boolean isRippleAnimationRunning() {  
  255.         return animationRunning;  
  256.     }  
  257.   
  258.     /** 
  259.      * RippleView产生波纹效果, 默认不可见,当启动动画时才设置为可见 
  260.      *  
  261.      * @author mrsimple 
  262.      */  
  263.     private class RippleView extends View {  
  264.   
  265.         public RippleView(Context context) {  
  266.             super(context);  
  267.             this.setVisibility(View.INVISIBLE);  
  268.         }  
  269.   
  270.         @Override  
  271.         protected void onDraw(Canvas canvas) {  
  272.             int radius = (Math.min(getWidth(), getHeight())) / 2;  
  273.             canvas.drawCircle(radius, radius, radius - mStrokeWidth, mPaint);  
  274.         }  
  275.     }  
  276. }  


自定义属性  attrs.xml: 

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.   
  4.     <declare-styleable name="RippleLayout">  
  5.         <attr name="color" format="color" />  
  6.         <attr name="strokeWidth" format="dimension" />  
  7.         <attr name="radius" format="dimension" />  
  8.         <attr name="duration" format="integer" />  
  9.         <attr name="rippleNums" format="integer" />  
  10.         <attr name="scale" format="float" />  
  11.     </declare-styleable>  
  12.   
  13. </resources>  


NineOldAnimations动画库

NineOldAnimations


使用示例

从github clone一份或者将上述代码和attrs.xml拷贝到你的工程中,在布局文件中添加如下:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <org.simple.ripple.RippleLayout  
  2.      xmlns:ripple="http://schemas.android.com/apk/org.simple.ripplelayout"  
  3.      android:id="@+id/ripple_layout"  
  4.      android:layout_width="match_parent"  
  5.      android:layout_height="match_parent"  
  6.      ripple:duration="3000"  
  7.      ripple:radius="32dp"  
  8.      ripple:rippleNums="1"  
  9.      ripple:scale="4"  
  10.      ripple:color="#8899CC" >  
  11.   
  12.      <ImageView  
  13.          android:id="@+id/centerImage"  
  14.          android:layout_width="64dp"  
  15.          android:layout_height="64dp"  
  16.          android:layout_centerInParent="true"  
  17.          android:contentDescription="@string/app_name"  
  18.          android:src="@drawable/phone2" />  
  19.  </org.simple.ripple.RippleLayout>  

注意,这里引入了xmlns:ripple,也就是自定义RippleLayout属性生成的R的包路径.

代码中启动动画:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. ImageView imageview;  
  2.  RippleLayout layout;  
  3.   
  4.  @Override  
  5.  protected void onCreate(Bundle savedInstanceState) {  
  6.      super.onCreate(savedInstanceState);  
  7.      setContentView(R.layout.activity_main);  
  8.   
  9.      layout = (RippleLayout) findViewById(R.id.ripple_layout);  
  10.      imageview = (ImageView) findViewById(R.id.centerImage);  
  11.      imageview.setOnClickListener(new OnClickListener() {  
  12.   
  13.          @Override  
  14.          public void onClick(View v) {  
  15.              if (layout.isRippleAnimationRunning()) {  
  16.                  layout.stopRippleAnimation();  
  17.              } else {  
  18.                  layout.startRippleAnimation();  
  19.              }  
  20.          }  
  21.      });  
  22.  }  

效果图 




github地址

猛击此进入

举报

相关文章推荐

Android自定义仿Siri曲线View

Android自定义仿Siri曲线View效果图 波浪其实是由4条贝塞尔曲线组成的,可以在自定义View的onDraw函数中,用Path.quadTo函数画出4条曲线。Path.quadTo(f...

android自定义控件:根据声音波动的声波控件

直接上图,有两个效果,如下: 效果一: 效果二: 大家可能觉得效果二难看,但其实 ,在项目中的运行以后,是这样的: 因为这个控件的许多东西,...

我是如何成为一名python大咖的?

人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..

Android 录音声波图

图像类:package com.akm.test;/** * Created by toge on 15/12/9. */ import android.content.Context; impo...

水波纹效果之仿支付宝咻一咻

先上效果图: /** * Created by liuye on 2016/11/30 0030. * 水波纹特效 类似于支付宝咻一咻 */ public class WaveView e...

实现支付宝的咻一咻波纹扩散效果

思路 : 将每一个波纹作为单独的一个View,使用canvas+ValueAnimator绘制. 然后外部父布局继承FrameLayout,利用addView+开始动画 -> 结束动画+removeV...

Android支付宝咻咻水波纹效果的实现

概述最近看到支付宝咻咻的页面就想模仿一下,话不多说,先看效果图。(录制的有点渣) 先说说这个效果: 1.点击中间图标开始搜索附近的人。 2.开始搜索后水波纹一圈圈的加速向外扩张。 3...

实现Android支付宝声波支付时的波纹视图

转载请注明出处,本文来自【 Mr.Simple的博客 】。 我正在参加博客之星,点击这里投我一票吧,谢谢~前言 自从支付宝声波支付的波纹效果出来以后,这种形式就慢慢流行开来,比如各种安全软件在扫描时会...

Android仿支付宝咻一咻动画

Android特效专辑(十二)——仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View 先来看看这个效果 这是我的在Only上添加的效果,说实话,Only现在都还只是半成品,台面都上...

android 类似于支付宝咻咻咻界面的按钮波纹效果

放支付宝咻咻咻的那个波纹按钮
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)