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

转载请注明出处,本文来自【 Mr.Simple的博客 】

我正在参加博客之星,点击这里投我一票吧,谢谢~

前言

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

图1

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

波纹实现

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

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

图 2

代码实现 :

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2015 [email protected]
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.simple.ripple;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.RelativeLayout;

import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorSet;
import com.nineoldandroids.animation.ObjectAnimator;

import org.simple.ripplelayout.R;

import java.util.ArrayList;

/**
 * 这是一个类似支付宝声波支付的波纹效果布局,该布局中默认添加了不可见的圆形的视图,启动动画时会启动缩放、颜色渐变动画使得产生波纹效果.
 * 这些动画都是无限循环的,并且每个View的动画之间都有时间间隔,这些时间间隔就会导致视图有大有小,从而产生波纹的效果.
 *
 * @author mrsimple
 */
public class RippleLayout extends RelativeLayout {

    /**
     * static final fields
     */
    private static final int DEFAULT_RIPPLE_COUNT = 6;
    private static final int DEFAULT_DURATION_TIME = 3000;
    private static final float DEFAULT_SCALE = 4.0f;
    private static final int DEFAULT_RIPPLE_COLOR = Color.rgb(0x33, 0x99, 0xcc);
    private static final int DEFAULT_STROKE_WIDTH = 0;
    private static final int DEFAULT_RADIUS = 60;

    /**
     *
     */
    private int mRippleColor = DEFAULT_RIPPLE_COLOR;
    private float mStrokeWidth = DEFAULT_STROKE_WIDTH;
    private float mRippleRadius = DEFAULT_RADIUS;
    private int mAnimDuration;
    private int mRippleViewNums;
    private int mAnimDelay;
    private float mRippleScale;
    private boolean animationRunning = false;
    /**
     *
     */
    private Paint mPaint = new Paint();

    /**
     * 动画集,执行缩放、alpha动画,使得背景色渐变
     */
    private AnimatorSet mAnimatorSet = new AnimatorSet();
    /**
     * 动画列表,保存几个动画
     */
    private ArrayList<Animator> mAnimatorList = new ArrayList<Animator>();
    /**
     * RippleView Params
     */
    private LayoutParams mRippleViewParams;

    /**
     * @param context
     */
    public RippleLayout(Context context) {
        super(context);
        init(context, null);
    }

    public RippleLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public RippleLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(final Context context, final AttributeSet attrs) {
        if (isInEditMode()) {
            return;
        }

        if (null != attrs) {
            initTypedArray(context, attrs);
        }

        initPaint();
        initRippleViewLayoutParams();
        generateRippleViews();

    }

    private void initTypedArray(Context context, AttributeSet attrs) {
        final TypedArray typedArray = context.obtainStyledAttributes(attrs,
                R.styleable.RippleLayout);
        //
        mRippleColor = typedArray.getColor(R.styleable.RippleLayout_color,
                DEFAULT_RIPPLE_COLOR);
        mStrokeWidth =
                typedArray.getDimension(R.styleable.RippleLayout_strokeWidth, DEFAULT_STROKE_WIDTH);
        mRippleRadius = typedArray.getDimension(R.styleable.RippleLayout_radius,
                DEFAULT_RADIUS);
        mAnimDuration = typedArray.getInt(R.styleable.RippleLayout_duration,
                DEFAULT_DURATION_TIME);
        mRippleViewNums = typedArray.getInt(R.styleable.RippleLayout_rippleNums,
                DEFAULT_RIPPLE_COUNT);
        mRippleScale = typedArray.getFloat(R.styleable.RippleLayout_scale,
                DEFAULT_SCALE);

        // oh, baby, don‘t forget recycle the typedArray !!
        typedArray.recycle();
    }

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mStrokeWidth = 0;
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(mRippleColor);
    }

    private void initRippleViewLayoutParams() {
        // ripple view的大小为 半径 + 笔宽的两倍
        int rippleSide = (int) (2 * (mRippleRadius + mStrokeWidth));
        mRippleViewParams = new LayoutParams(rippleSide, rippleSide);
        // 居中显示
        mRippleViewParams.addRule(CENTER_IN_PARENT, TRUE);
    }

    /**
     * 计算每个RippleView之间的动画时间间隔,从而产生波纹效果
     */
    private void calculateAnimDelay() {
        mAnimDelay = mAnimDuration / mRippleViewNums;
    }

    /**
     * 初始化RippleViews,并且将动画设置到RippleView上,使之在x, y不断扩大,并且背景色逐渐淡化
     */
    private void generateRippleViews() {

        calculateAnimDelay();
        initAnimSet();
        // 添加RippleView
        for (int i = 0; i < mRippleViewNums; i++) {
            RippleView rippleView = new RippleView(getContext());
            addView(rippleView, mRippleViewParams);
            // 添加动画
            addAnimToRippleView(rippleView, i);
        }

        // x, y, alpha动画一块执行
        mAnimatorSet.playTogether(mAnimatorList);
    }

    private void initAnimSet() {
        mAnimatorSet.setDuration(mAnimDuration);
        mAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
    }

    /**
     * 为每个RippleView添加动画效果,并且设置动画延时,每个视图启动动画的时间不同,就会产生波纹
     *
     * @param rippleView
     * @param i 视图所在的索引
     */
    private void addAnimToRippleView(RippleView rippleView, int i) {

        // x轴的缩放动画
        final ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(rippleView, "scaleX",
                1.0f, mRippleScale);
        scaleXAnimator.setRepeatCount(ObjectAnimator.INFINITE);
        scaleXAnimator.setRepeatMode(ObjectAnimator.RESTART);
        scaleXAnimator.setStartDelay(i * mAnimDelay);
        scaleXAnimator.setDuration(mAnimDuration);
        mAnimatorList.add(scaleXAnimator);

        // y轴的缩放动画
        final ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(rippleView, "scaleY",
                1.0f, mRippleScale);
        scaleYAnimator.setRepeatMode(ObjectAnimator.RESTART);
        scaleYAnimator.setRepeatCount(ObjectAnimator.INFINITE);
        scaleYAnimator.setStartDelay(i * mAnimDelay);
        scaleYAnimator.setDuration(mAnimDuration);
        mAnimatorList.add(scaleYAnimator);

        // 颜色的alpha渐变动画
        final ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(rippleView, "alpha", 1.0f,
                0f);
        alphaAnimator.setRepeatMode(ObjectAnimator.RESTART);
        alphaAnimator.setRepeatCount(ObjectAnimator.INFINITE);
        alphaAnimator.setDuration(mAnimDuration);
        alphaAnimator.setStartDelay(i * mAnimDelay);
        mAnimatorList.add(alphaAnimator);
    }

    public void startRippleAnimation() {
        if (!isRippleAnimationRunning()) {
            makeRippleViewsVisible();
            mAnimatorSet.start();
            animationRunning = true;
        }
    }

    private void makeRippleViewsVisible() {
        int childCount = this.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = this.getChildAt(i);
            if (childView instanceof RippleView) {
                childView.setVisibility(VISIBLE);
            }
        }
    }

    public void stopRippleAnimation() {
        if (isRippleAnimationRunning()) {
            mAnimatorSet.end();
            animationRunning = false;
        }
    }

    public boolean isRippleAnimationRunning() {
        return animationRunning;
    }

    /**
     * RippleView产生波纹效果, 默认不可见,当启动动画时才设置为可见
     *
     * @author mrsimple
     */
    private class RippleView extends View {

        public RippleView(Context context) {
            super(context);
            this.setVisibility(View.INVISIBLE);
        }

        @Override
        protected void onDraw(Canvas canvas) {
            int radius = (Math.min(getWidth(), getHeight())) / 2;
            canvas.drawCircle(radius, radius, radius - mStrokeWidth, mPaint);
        }
    }
}

自定义属性  attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="RippleLayout">
        <attr name="color" format="color" />
        <attr name="strokeWidth" format="dimension" />
        <attr name="radius" format="dimension" />
        <attr name="duration" format="integer" />
        <attr name="rippleNums" format="integer" />
        <attr name="scale" format="float" />
    </declare-styleable>

</resources>

NineOldAnimations动画库

NineOldAnimations

使用示例

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

   <org.simple.ripple.RippleLayout
        xmlns:ripple="http://schemas.android.com/apk/org.simple.ripplelayout"
        android:id="@+id/ripple_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        ripple:duration="3000"
        ripple:radius="32dp"
        ripple:rippleNums="1"
        ripple:scale="4"
        ripple:color="#8899CC" >

        <ImageView
            android:id="@+id/centerImage"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:layout_centerInParent="true"
            android:contentDescription="@string/app_name"
            android:src="@drawable/phone2" />
    </org.simple.ripple.RippleLayout>

注意,这里引入了xmlns:ripple,也就是自定义RippleLayout属性生成的R的包路径,如果你是以拷贝代码的形式使用该视图,那么这里的org.simple.ripplelayout需要修改成你的工程的包路径。

代码中启动动画:

   ImageView imageview;
    RippleLayout layout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        layout = (RippleLayout) findViewById(R.id.ripple_layout);
        imageview = (ImageView) findViewById(R.id.centerImage);
        imageview.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                if (layout.isRippleAnimationRunning()) {
                    layout.stopRippleAnimation();
                } else {
                    layout.startRippleAnimation();
                }
            }
        });
    }

效果图

github地址

猛击此进入

我正在参加博客之星,点击这里投我一票吧,谢谢~

时间: 2024-08-26 18:12:48

实现Android支付宝声波支付时的波纹视图的相关文章

Android 支付宝快捷支付集成及ALI64错误的有效解决

支付宝开放平台採用了RSA安全签名机制,开发人员能够通过支付宝公钥验证消息来源.同一时候可使用自己的私钥对信息进行加密. RSA算法及数字签名机制是支付宝开放平台与开发人员网关安全通信的基础.若开发人员不熟悉RSA及数字签名.请先查阅相关文档. 1.RSA私钥及公钥生成 官方提供的开发文档存在一些误导,这里简要概括为这几句话: RSA密钥生成命令 生成RSA私钥 openssl>genrsa -out rsa_private_key.pem 1024 生成RSA公钥 openssl>rsa -

Android 支付宝 移动支付接口 快速配置

RSA签名 RSA密钥生成命令生成RSA私钥openssl>genrsa -out rsa_private_key.pem 1024生成RSA公钥openssl>rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem将RSA私钥转换成PKCS8格式openssl>pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt 注意:“>”符号

Android 支付宝移动支付SDK集成

上传公钥

(转载)Android支付宝支付封装代码

Android支付宝支付封装代码 投稿:lijiao 字体:[增加 减小] 类型:转载 时间:2015-12-22我要评论 这篇文章主要介绍了Android支付宝支付封装代码,Android支付的时候肯定会使用支付宝进行支付,封装可以简化操作步骤,感兴趣的小伙伴们可以参考一下 在做Android支付的时候肯定会用到支付宝支付, 根据官方给出的demo做起来非常费劲,所以我们需要一次简单的封装. 封装的代码也很简单,就是将官网给的demo提取出一个类来方便使用. ? 1 2 3 4 5 6 7 8

android支付宝支付开发过程

支付宝开发流程: 1.在支付宝申请一个账号并开通开发者功能和移动支付的功能. 支付宝地址:https://auth.alipay.com/login/index.htm?goto=https%3A%2F%2Fopenhome.alipay.com%2Fdeveloper%2FisvApply.htm%3Fflag%3Daop 支付宝移动支付地址:https://b.alipay.com/order/productDetail.htm?productId=2014110308141993 2.上面

android 集成支付宝app支付(原生态)-包括android前端与java后台

本文讲解了 android开发的原生态app集成了支付宝支付, 还提供了java后台服务器处理支付宝支付的加密代码, app前端与java后台服务器使用json数据格式交互信息,java后台服务主要用来对支付数据进行加密和接受支付宝服务器的回调 注意: 本文即涉及到 android前端, 也涉及到 Java后台 准备条件: 到支付宝官网上注册用户, 打开开放平台,支付宝默认生成沙箱环境,用来测试支付流程 安装Android Studio[下载], 安装 Eclipse mars  [下载],  

【android仿系列进阶篇】android 支付宝手机网页支付

最近在做android-,恩,就说这么多吧 1,准备工作 支付宝相关文档下载地址:https://b.alipay.com/order/productDetail.htm?productId=2013080604609688&tabId=4#ps-tabinfo-hash 当然了,假设你已经有了pid(partner)和商户账户(seller),并且开通了手机网页支付功能. 下载的包里面,打开[手机网页即时到账接口]文件夹,看到里面的可以看看里面的pdf文档,[手机网页支付接入与使用规则.pdf

支付宝安卓机型支付时,只有第一次可以唤醒支付窗的bug(有支付宝交易号)

占坑 之前公司的支付宝支付时相关项目代码一直是 document.addEventListener('AlipayJSBridgeReady', function() { AlipayJSBridge.call('tradePay',{tradeNO:tradeNo}, function(result){ } }) 这样的,但是前两天突然报了bug出来 安卓的只可以支付第一次,再支付就弹不出支付窗了 经过一系列查找也没找到问题 最后按照官方的方法写 function ready(callback

Android客户端集成支付宝快捷支付

一. 第三方支付开放接口 1. 支付宝 面向用户:  支付宝.微信 面向企业:  连连支付.东方支付 2. 官网 https://doc.open.alipay.com/doc2/detail?treeId=59&articleId=103563&docType=1 3. 请求参数说明 https://doc.open.alipay.com/doc2/detail.htm?treeId=59&articleId=103663&docType=1 4. 支付宝集成 1) 安全