热门标签 | 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

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


推荐阅读
  • 本文基于对相关论文和开源代码的研究,详细介绍了LOAM(激光雷达里程计与建图)的工作原理,并对其关键技术进行了分析。 ... [详细]
  • 资源推荐 | TensorFlow官方中文教程助力英语非母语者学习
    来源:机器之心。本文详细介绍了TensorFlow官方提供的中文版教程和指南,帮助开发者更好地理解和应用这一强大的开源机器学习平台。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 构建基于BERT的中文NL2SQL模型:一个简明的基准
    本文探讨了将自然语言转换为SQL语句(NL2SQL)的任务,这是人工智能领域中一项非常实用的研究方向。文章介绍了笔者在公司举办的首届中文NL2SQL挑战赛中的实践,该比赛提供了金融和通用领域的表格数据,并标注了对应的自然语言与SQL语句对,旨在训练准确的NL2SQL模型。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 本文介绍如何使用 Sortable.js 库实现元素的拖拽和位置交换功能。Sortable.js 是一个轻量级、无依赖的 JavaScript 库,支持拖拽排序、动画效果和多种插件扩展。通过简单的配置和事件处理,可以轻松实现复杂的功能。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • 探讨一个显示数字的故障计算器,它支持两种操作:将当前数字乘以2或减去1。本文将详细介绍如何用最少的操作次数将初始值X转换为目标值Y。 ... [详细]
  • 如何在WPS Office for Mac中调整Word文档的文字排列方向
    本文将详细介绍如何使用最新版WPS Office for Mac调整Word文档中的文字排列方向。通过这些步骤,用户可以轻松更改文本的水平或垂直排列方式,以满足不同的排版需求。 ... [详细]
  • 本文总结了在使用Ionic 5进行Android平台APK打包时遇到的问题,特别是针对QRScanner插件的改造。通过详细分析和提供具体的解决方法,帮助开发者顺利打包并优化应用性能。 ... [详细]
  • Android LED 数字字体的应用与实现
    本文介绍了一种适用于 Android 应用的 LED 数字字体(digital font),并详细描述了其在 UI 设计中的应用场景及其实现方法。这种字体常用于视频、广告倒计时等场景,能够增强视觉效果。 ... [详细]
  • RecyclerView初步学习(一)
    RecyclerView初步学习(一)ReCyclerView提供了一种插件式的编程模式,除了提供ViewHolder缓存模式,还可以自定义动画,分割符,布局样式,相比于传统的ListVi ... [详细]
  • 扫描线三巨头 hdu1928hdu 1255  hdu 1542 [POJ 1151]
    学习链接:http:blog.csdn.netlwt36articledetails48908031学习扫描线主要学习的是一种扫描的思想,后期可以求解很 ... [详细]
  • 本文详细记录了在基于Debian的Deepin 20操作系统上安装MySQL 5.7的具体步骤,包括软件包的选择、依赖项的处理及远程访问权限的配置。 ... [详细]
  • QBlog开源博客系统:Page_Load生命周期与参数传递优化(第四部分)
    本教程将深入探讨QBlog开源博客系统的Page_Load生命周期,并介绍一种简洁的参数传递重构方法。通过视频演示和详细讲解,帮助开发者更好地理解和应用这些技术。 ... [详细]
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社区 版权所有