热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android自定义View系列之Path绘制仿支付宝支付成功动画

这篇文章主要为大家详细介绍了Android自定义View系列之Path绘制仿支付宝支付成功动画,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

前言

使用支付宝付款时,我们可以看到成功或者失败都会有个动画提示,如果我们需要做这样的效果的话,当然,你可以让设计师给你做个GIF,但是我们知道图像比较耗内存的,我们自己可以用代码实现还是代码实现好点吧。

效果

实现方法

首先我们需要了解PathMeasure这个类,这个类我们可以理解为用来管理Path。我们主要看几个方法。

PathMeasure(): 构造方法 ,实例化一个对象

PathMeasure(Path path,boolean isClosed):传入Path对象和是否闭合,path对象不能为空

getLength():获取当前轮廓、外形的总长度, 如果没有设置Path对象,返回0

getSegment(float startD,float stopD,Path dst,boolean startWithMoveTo):调用这个方法,我们可以获取到指定范围内的一段轮廓,存入到dst参数中。所以,这个方法传入的参数分别为长度起始值、结束值、装这一段路径的Path对象、是否MoveTo。另外,这个方法返回值为Boolean类型,如果getLength为0的话,返回false,或者startD > stopD,同样返回false。

setPath(Path path , boolean isClosed):给当前PathMeasure对象设置Path

nextContour():移动到下一个轮廓

然后我们需要动起来,我们知道invalidate()方法可以刷新界面,也就是重新调用onDraw()方法,所以我们要不停调用invalidate方法,在onDraw方法中改变参数,这样实现动的效果。所以可以用到刚刚介绍的getSegment方法,不断改变获取的范围,从0 * getLength,到1 * getLength,最后绘制完整。所以我们需要一个在一秒内或两秒内一个从0到1的值的变化,so,我们使用ValueAnimator这个类来实现。

//实例化对象
mCircleAnimator = ValueAnimator.ofFloat(0, 1);
//设置时长为1000ms
mCircleAnimator.setDuration(1000);
//开始动画
mCircleAnimator.start();
//设置动画监听
mCircleAnimator.addUpdateListener(this);

动画开始后,在监听方法中获取当前进度并且重绘图像

mCirclePercent = (float)animation.getAnimatedValue();
invalidate();

在onDraw方法中,绘制图像

//画圆
mPathCircle.addCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2 - mLineWidth, Path.Direction.CW);
mPathMeasure.setPath(mPathCircle, false);
mPathMeasure.getSegment(0, mCirclePercent * mPathMeasure.getLength(), mPathCircleDst, true);
canvas.drawPath(mPathCircleDst, mPaint);

附上源码,欢迎点评

package com.mintmedical.wavedemo;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

/**
 * Created by MooreLi on 2016/12/12.
 */

public class ResultAnimation extends View implements ValueAnimator.AnimatorUpdateListener {
 private Context mContext;
 /**
  * paint对象
  */
 private Paint mPaint;
 /**
  * Path和对应的空Path用来填充
  */
 private Path mPathCircle;
 private Path mPathCircleDst;
 private Path mPathRight;
 private Path mPathRightDst;
 private Path mPathWrong1;
 private Path mPathWrong2;
 private Path mPathWrong1Dst;
 private Path mPathWrong2Dst;
 /**
  * Path管理
  */
 private PathMeasure mPathMeasure;
 /**
  * 动画
  */
 private ValueAnimator mCircleAnimator;
 private ValueAnimator mRightAnimator;
 private ValueAnimator mWrong1Animator;
 private ValueAnimator mWrong2Animator;
 /**
  * 当前绘制进度占总Path长度百分比
  */
 private float mCirclePercent;
 private float mRightPercent;
 private float mWrong1Percent;
 private float mWrong2Percent;
 /**
  * 线宽
  */
 private int mLineWidth;
 /**
  * 正确动画 错误动画
  */
 public static final int RESULT_RIGHT = 1;
 public static final int RESULT_WROnG= 2;
 /**
  * 当前结果类型
  */
 private int mResultType = RESULT_WRONG;

 public ResultAnimation(Context context) {
  super(context);
  mCOntext= context;
  init();
 }

 public ResultAnimation(Context context, AttributeSet attrs) {
  super(context, attrs);
  mCOntext= context;
  init();
 }

 public ResultAnimation(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  mCOntext= context;
  init();
 }

 private void init() {
  mLineWidth = dp2px(3);
  mPaint = new Paint();
  mPaint.setAntiAlias(true);
  mPaint.setStrokeWidth(mLineWidth);
  mPaint.setStyle(Paint.Style.STROKE);
  mPaint.setColor(Color.GREEN);

  initPath();
 }

 private void initPath() {
  mPathCircle = new Path();
  mPathCircleDst = new Path();
  mPathRight = new Path();
  mPathRightDst = new Path();
  mPathWrong1 = new Path();
  mPathWrong2 = new Path();
  mPathWrong1Dst = new Path();
  mPathWrong2Dst = new Path();

  mPathMeasure = new PathMeasure();

  //实例化对象
  mCircleAnimator = ValueAnimator.ofFloat(0, 1);
  //设置时长为1000ms
  mCircleAnimator.setDuration(1000);
  //开始动画
  mCircleAnimator.start();
  //设置动画监听
  mCircleAnimator.addUpdateListener(this);

  mRightAnimator = ValueAnimator.ofFloat(0, 1);
  mRightAnimator.setDuration(500);
  mRightAnimator.addUpdateListener(this);

  mWrong1Animator = ValueAnimator.ofFloat(0, 1);
  mWrong1Animator.setDuration(300);
  mWrong1Animator.addUpdateListener(this);
  mWrong2Animator = ValueAnimator.ofFloat(0, 1);
  mWrong2Animator.setDuration(300);
  mWrong2Animator.addUpdateListener(this);

 }

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  if (mResultType == RESULT_RIGHT) {
   mPaint.setColor(Color.GREEN);
  } else {
   mPaint.setColor(Color.RED);
  }

  //画圆
  mPathCircle.addCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2 - mLineWidth, Path.Direction.CW);
  mPathMeasure.setPath(mPathCircle, false);
  mPathMeasure.getSegment(0, mCirclePercent * mPathMeasure.getLength(), mPathCircleDst, true);
  canvas.drawPath(mPathCircleDst, mPaint);
  if (mResultType == RESULT_RIGHT) {
   //画对勾
   mPathRight.moveTo(getWidth() / 4, getWidth() / 2);
   mPathRight.lineTo(getWidth() / 2, getWidth() / 4 * 3);
   mPathRight.lineTo(getWidth() / 4 * 3, getWidth() / 4);
   if (mCirclePercent == 1) {
    mPathMeasure.nextContour();
    mPathMeasure.setPath(mPathRight, false);
    mPathMeasure.getSegment(0, mRightPercent * mPathMeasure.getLength(), mPathRightDst, true);
    canvas.drawPath(mPathRightDst, mPaint);
   }
  } else {
   mPathWrong1.moveTo(getWidth() / 4 * 3, getWidth() / 4);
   mPathWrong1.lineTo(getWidth() / 4, getWidth() / 4 * 3);

   mPathWrong2.moveTo(getWidth() / 4, getWidth() / 4);
   mPathWrong2.lineTo(getWidth() / 4 * 3, getWidth() / 4 * 3);

   if (mCirclePercent == 1) {
    mPathMeasure.nextContour();
    mPathMeasure.setPath(mPathWrong1, false);
    mPathMeasure.getSegment(0, mWrong1Percent * mPathMeasure.getLength(), mPathWrong1Dst, true);
    canvas.drawPath(mPathWrong1Dst, mPaint);
   }
   if (mWrong1Percent == 1) {
    mPathMeasure.nextContour();
    mPathMeasure.setPath(mPathWrong2, false);
    mPathMeasure.getSegment(0, mWrong2Percent * mPathMeasure.getLength(), mPathWrong2Dst, true);
    canvas.drawPath(mPathWrong2Dst, mPaint);
   }
  }
 }

 private int dp2px(int dp) {
  float scale = mContext.getResources().getDisplayMetrics().density;
  return (int) (scale * dp + 0.5f);
 }

 @Override
 public void onAnimationUpdate(ValueAnimator animation) {
  //圆形动画
  if (animation.equals(mCircleAnimator)) {
   mCirclePercent = (float) animation.getAnimatedValue();
   invalidate();
   Log.e("TEST","percent:"+mCirclePercent);
   if (mCirclePercent == 1) {
    if (mResultType == RESULT_RIGHT)
     mRightAnimator.start();
    else
     mWrong1Animator.start();
   }
  }
  //正确时 对勾动画
  else if (animation.equals(mRightAnimator)) {
   mRightPercent = (float) animation.getAnimatedValue();
   invalidate();
  }
  //错误时 右侧动画
  else if (animation.equals(mWrong1Animator)) {
   mWrong1Percent = (float) animation.getAnimatedValue();
   invalidate();
   if (mWrong1Percent == 1) {
    mWrong2Animator.start();
   }
  }
  //错误时 左侧动画
  else if (animation.equals(mWrong2Animator)) {
   mWrong2Percent = (float) animation.getAnimatedValue();
   invalidate();
  }
 }

 public void setmResultType(int mResultType) {
  this.mResultType = mResultType;
  invalidate();
 }

 /**
  * 固定写死了宽高,可重新手动调配
  *
  * @param widthMeasureSpec
  * @param heightMeasureSpec
  */
 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  setMeasuredDimension(dp2px(50), dp2px(50));
 }
}

github地址: https://github.com/lizebinbin

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 本文深入探讨了原型模式在软件设计中的应用与实现。原型模式通过使用已有的实例作为原型来创建新对象,而不是直接通过类实例化。这种方式不仅简化了对象的创建过程,还提高了系统的灵活性和效率。具体来说,原型模式涉及一个支持克隆功能的接口或基类,子类通过实现该接口来提供具体的克隆方法,从而实现对象的快速复制。此外,文章还详细分析了原型模式的优缺点及其在实际项目中的应用场景,为开发者提供了实用的指导和建议。 ... [详细]
  • BZOJ4240 Gym 102082G:贪心算法与树状数组的综合应用
    BZOJ4240 Gym 102082G 题目 "有趣的家庭菜园" 结合了贪心算法和树状数组的应用,旨在解决在有限时间和内存限制下高效处理复杂数据结构的问题。通过巧妙地运用贪心策略和树状数组,该题目能够在 10 秒的时间限制和 256MB 的内存限制内,有效处理大量输入数据,实现高性能的解决方案。提交次数为 756 次,成功解决次数为 349 次,体现了该题目的挑战性和实际应用价值。 ... [详细]
  • 在CentOS上部署和配置FreeSWITCH
    在CentOS系统上部署和配置FreeSWITCH的过程涉及多个步骤。本文详细介绍了从源代码安装FreeSWITCH的方法,包括必要的依赖项安装、编译和配置过程。此外,还提供了常见的配置选项和故障排除技巧,帮助用户顺利完成部署并确保系统的稳定运行。 ... [详细]
  • 本文深入探讨了算法进阶中的多个核心主题,包括最大似然估计在统计建模中的应用、赔率计算在风险评估中的重要性、FuzzyWuzzy库在字符串相似度匹配中的高效使用、主成分分析(PCA)在数据降维与特征提取中的关键作用,以及One-Hot编码在处理分类变量时的技术细节。通过这些内容,读者将获得对算法应用的全面理解。 ... [详细]
  • 本项目在Java Maven框架下,利用POI库实现了Excel数据的高效导入与导出功能。通过优化数据处理流程,提升了数据操作的性能和稳定性。项目已发布至GitHub,当前最新版本为0.0.5。该项目不仅适用于小型应用,也可扩展用于大型企业级系统,提供了灵活的数据管理解决方案。GitHub地址:https://github.com/83945105/holygrail,Maven坐标:`com.github.83945105:holygrail:0.0.5`。 ... [详细]
  • 结语 | 《探索二进制世界:软件安全与逆向分析》读书笔记:深入理解二进制代码的逆向工程方法
    结语 | 《探索二进制世界:软件安全与逆向分析》读书笔记:深入理解二进制代码的逆向工程方法 ... [详细]
  • 新年伊始,正是学习的最佳时机。本文全面解析了CK1957-Zookeeper的核心概念与实践技巧,旨在帮助初学者快速掌握这一深度学习工具。通过详细的理论讲解和实际操作示例,读者可以更好地理解Zookeeper的工作原理及其在分布式系统中的应用。无论是新手还是有一定基础的学习者,都能从中受益匪浅。 ... [详细]
  • 本文介绍了如何通过掌握 IScroll 技巧来实现流畅的上拉加载和下拉刷新功能。首先,需要按正确的顺序引入相关文件:1. Zepto;2. iScroll.js;3. scroll-probe.js。此外,还提供了完整的代码示例,可在 GitHub 仓库中查看。通过这些步骤,开发者可以轻松实现高效、流畅的滚动效果,提升用户体验。 ... [详细]
  • 深入解析PowerShell Stable与Preview版的区别与应用
    在研究 PowerShell 的 GitHub 发布页面时,我们注意到除了稳定的 6.2.0 版本外,还推出了 6.2.0 的预览版。本文将详细探讨这两个版本之间的主要区别及其应用场景,帮助用户更好地选择适合自身需求的版本。我们将分析稳定版的成熟度、性能和安全性,以及预览版的新功能、改进和潜在风险,为用户提供全面的参考信息。 ... [详细]
  • 深入解析:使用C++实现Python字节数组(struct)的高效处理方法 ... [详细]
  • 本文详细介绍了如何在Linux系统中搭建51单片机的开发与编程环境,重点讲解了使用Makefile进行项目管理的方法。首先,文章指导读者安装SDCC(Small Device C Compiler),这是一个专为小型设备设计的C语言编译器,适合用于51单片机的开发。随后,通过具体的实例演示了如何配置Makefile文件,以实现代码的自动化编译与链接过程,从而提高开发效率。此外,还提供了常见问题的解决方案及优化建议,帮助开发者快速上手并解决实际开发中可能遇到的技术难题。 ... [详细]
  • 【前端开发】深入探讨 RequireJS 与性能优化策略
    随着前端技术的迅速发展,RequireJS虽然不再像以往那样吸引关注,但其在模块化加载方面的优势仍然值得深入探讨。本文将详细介绍RequireJS的基本概念及其作为模块加载工具的核心功能,并重点分析其性能优化策略,帮助开发者更好地理解和应用这一工具,提升前端项目的加载速度和整体性能。 ... [详细]
  • 抖音AI特效风靡网络,真人瞬间变身动漫角色,吴亦凡、PDD和戚薇纷纷沉迷其中
    近期,抖音推出的一款名为“变身漫画”的AI特效在社交媒体上迅速走红,吸引了大量用户尝试。不仅普通网友积极参与,连吴亦凡、PDD和戚薇等明星也纷纷加入,体验将真人瞬间转化为动漫角色的神奇效果。这一特效凭借其高度的趣味性和创新性,迅速成为网络热议的话题。 ... [详细]
  • 题目描述:小K不幸被LL邪教洗脑,洗脑程度之深使他决定彻底脱离这个邪教。在最终离开前,他计划再进行一次亚瑟王游戏。作为最后一战,他希望这次游戏能够尽善尽美。众所周知,亚瑟王游戏的结果很大程度上取决于运气,但通过合理的策略和算法优化,可以提高获胜的概率。本文将详细解析洛谷P3239 [HNOI2015] 亚瑟王问题,并提供具体的算法实现方法,帮助读者更好地理解和应用相关技术。 ... [详细]
  • 分享一款高效的Python验证码识别代码实例 ... [详细]
author-avatar
年轻人创事业的美丽家园
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有