Android 5.0 实现水波扩散效果

 更新时间:2019年01月30日 10:23:20   作者:问剑孤鸣  
这篇文章主要为大家详细介绍了Android 5.0 实现水波扩散效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了Android 5.0 实现水波扩散效果的具体代码,供大家参考,具体内容如下

该效果是通过自定义界面来实现的

1、首先自定义属性,attrs.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
 
  <declare-styleable name="RippleView">
    <attr name="rippleColor" format="color" />
    <attr name="rippleAlpha" format="float" />
    <attr name="maskAlpha" format="float" />
    <attr name="rippleTime" format="integer" />
  </declare-styleable>
 
</resources>

其中属性rippleColor为水波动画的颜色,rippleAlpha为其透明度,rippleTime为动画持续时间,maskAlpha为触摸遮掩层的透明度。

2、自定义RippleView类继承RelativeLayout布局,也可以由需求所定继承于其它类,实现水波扩散效果主要的有两点:水波扩散的绘制和动画

1)水波的绘制其实就是绘制一个圆形

canvas.drawCircle(mDownX, mDownY, mRadius, mRipplePaint);

2)动画效果就是该圆形的绘制从小到大的过程,而该圆形到最大时的半径长度就是当前所在布局的对角线长度:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
 super.onSizeChanged(w, h, oldw, oldh);
 // 获取最大半径
 mMaxRadius = (float) Math.sqrt(w * w + h * h);
}

动画效果用属性动画ObjectAnimator来实现(ObjectAnimator是android3.0后出现的)如下

mRippleAnim = ObjectAnimator.ofFloat(this, "radius", 0, mMaxRadius);
mRippleAnim.setInterpolator(new AccelerateDecelerateInterpolator());
mRippleAnim.setDuration(mRippleTime);

其中的radius为自定义的属性,在动画执行时会回调对应的方法,只需要在该方法中更新圆形的半径就行了

public void setRadius(final float radius) {
 mRadius = radius;
 invalidate();
}

RippleView完整代码如下:

public class RippleView extends RelativeLayout {
 
 private int mRippleColor;// 水波颜色
 
 private float mMaskAlpha;// 遮掩透明度
 private float mRippleAlpha;// 水波透明度
 
 private int mRippleTime;// 水波动画时间
 private ObjectAnimator mRippleAnim;// 显示水波动画
 
 private float mRadius;// 当前的半径
 private float mMaxRadius;// 水波最大半径
 
 private float mDownX; // 记录手指按下的位置
 private float mDownY;
 private boolean mIsMask;
 private boolean mIsAnimating;// 是否正在播放动画
 
 private RectF mRect;
 private Path mPath;
 private Paint mMaskPaint;
 private Paint mRipplePaint;
 
 public RippleView(Context context) {
 this(context, null);
 }
 
 public RippleView(Context context, AttributeSet attrs) {
 this(context, attrs, 0);
 }
 
 public RippleView(Context context, AttributeSet attrs, int defStyle) {
 super(context, attrs, defStyle);
 init();
 // 获取自定义属性
 TypedArray ta = context.obtainStyledAttributes(attrs,
  R.styleable.RippleView);
 mRippleColor = ta.getColor(R.styleable.RippleView_rippleColor,
  mRippleColor);
 mMaskAlpha = ta.getFloat(R.styleable.RippleView_maskAlpha, mMaskAlpha);
 mRippleAlpha = ta.getFloat(R.styleable.RippleView_rippleAlpha,
  mRippleAlpha);
 mRippleTime = ta.getInt(R.styleable.RippleView_rippleTime, mRippleTime);
 ta.recycle();
 initLast();
 }
 
 // 初始化方法
 private void init() {
 mRippleColor = Color.BLACK;
 mMaskAlpha = 0.4f;
 mRippleAlpha = 0.8f;
 mRippleTime = 800;
 
 mPath = new Path();
 mMaskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 mRipplePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 }
 
 // 初始化
 private void initLast() {
 mMaskPaint.setColor(mRippleColor);
 mRipplePaint.setColor(mRippleColor);
 mMaskPaint.setAlpha((int) (mMaskAlpha * 255));
 mRipplePaint.setAlpha((int) (mRippleAlpha * 255));
 }
 
 // 初始化动画
 private void initAnim() {
 mRippleAnim = ObjectAnimator.ofFloat(this, "radius", 0, mMaxRadius);
 mRippleAnim.setInterpolator(new AccelerateDecelerateInterpolator());
 mRippleAnim.setDuration(mRippleTime);
 mRippleAnim.addListener(new Animator.AnimatorListener() {
  @Override
  public void onAnimationStart(Animator animator) {
  mIsAnimating = true;
  }
 
  @Override
  public void onAnimationEnd(Animator animator) {
  setRadius(0);
  mIsMask = false;
  mIsAnimating = false;
  }
 
  @Override
  public void onAnimationCancel(Animator animator) {
 
  }
 
  @Override
  public void onAnimationRepeat(Animator animator) {
 
  }
 });
 }
 
 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
 super.onSizeChanged(w, h, oldw, oldh);
 // 获取最大半径
 mMaxRadius = (float) Math.sqrt(w * w + h * h);
 // 获取该类布局范围
 mRect = new RectF(0f, 0f, getMeasuredWidth() * 1.0f,
  getMeasuredHeight() * 1.0f);
 // 初始化动画
 initAnim();
 }
 
 @Override
 public boolean onTouchEvent(MotionEvent event) {
 // 按下事件
 if (event.getAction() == MotionEvent.ACTION_DOWN) {
  mIsMask = true;
  invalidate();
 } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
  invalidate();
 }
 // 抬起事件
 else if (event.getAction() == MotionEvent.ACTION_UP) {
  mDownX = event.getX();
  mDownY = event.getY();
  if (mIsAnimating) {
  mRippleAnim.cancel();
  }
  boolean isInView = mRect.contains(mDownX, mDownY);
  if (isInView) {
  mRippleAnim.start();
  } else {
  mIsMask = false;
  invalidate();
  }
 
 }
 
 return true;
 }
 
 @Override
 protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 // 绘制遮掩
 if (mIsMask) {
  canvas.save(Canvas.CLIP_SAVE_FLAG);
  mPath.reset();
  mPath.addRect(mRect, Path.Direction.CW);
  canvas.clipPath(mPath);
  canvas.drawRect(mRect, mMaskPaint);
  canvas.restore();
 
 }
 
 // 绘制水波
 canvas.save(Canvas.CLIP_SAVE_FLAG);
 mPath.reset();
 mPath.addCircle(mDownX, mDownY, mRadius, Path.Direction.CW);
 canvas.clipPath(mPath);
 canvas.drawCircle(mDownX, mDownY, mRadius, mRipplePaint);
 canvas.restore();
 
 }
 
 // 属性动画回调的方法
 public void setRadius(final float radius) {
 mRadius = radius;
 invalidate();
 }
}

界面布局代码

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  xmlns:ripple="http://schemas.android.com/apk/res/com.example.rippleview"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#000000" >
 
  <com.example.rippleview.RippleView
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:layout_centerInParent="true"
    android:background="#ffffff"
    android:clickable="true"
    ripple:rippleColor="#ababab" >
 
    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_centerInParent="true"
      android:text="click me"
      android:textSize="18sp" />
  </com.example.rippleview.RippleView>
 
</RelativeLayout>

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

相关文章

  • android实现筛选菜单效果

    android实现筛选菜单效果

    这篇文章主要为大家详细介绍了android实现筛选菜单效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • android教程viewpager自动循环和手动循环

    android教程viewpager自动循环和手动循环

    这篇文章主要介绍了android的viewpager自动循环和手动循环示例,需要的朋友可以参考下
    2014-02-02
  • Android模拟用户点击的实现方法

    Android模拟用户点击的实现方法

    这篇文章主要给大家介绍了关于Android模拟用户点击的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学学习学习吧。
    2018-02-02
  • Android程序开发之自定义设置TabHost,TabWidget样式

    Android程序开发之自定义设置TabHost,TabWidget样式

    这篇文章主要介绍了Android程序开发之自定义设置TabHost,TabWidget样式的相关资料,需要的朋友可以参考下
    2016-03-03
  • Android Studio中引入Lambda表达式的方法

    Android Studio中引入Lambda表达式的方法

    这篇文章主要给大家介绍了在Android Studio中引入Lambda表达式的方法,文中通过图文介绍的非常详细,对大家具有一定的参考价值,需要的朋友们下面来一起看看吧。
    2017-03-03
  • Android再探全面屏适配示例详解

    Android再探全面屏适配示例详解

    这篇文章主要为大家介绍了Android再探全面屏适配示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • Android WebView实现顶部进度条

    Android WebView实现顶部进度条

    这篇文章主要为大家详细介绍了Android WebView实现顶部进度条,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-11-11
  • 使用androidx BiometricPrompt实现指纹验证功能

    使用androidx BiometricPrompt实现指纹验证功能

    这篇文章主要介绍了使用androidx BiometricPrompt实现指纹验证功能,对android指纹验证相关知识感兴趣的朋友跟随小编一起看看吧
    2021-07-07
  • Flutter 构建一个常用的页面框架

    Flutter 构建一个常用的页面框架

    大多数 App 中都会有底部导航栏,通过底部导航栏切换实现不同页面之间的切换。在Flutter 中提供了 BottomNavigationBar组件实现底部导航。本篇介绍通过 BottomNavigationBar和 IndexedStack构建最为常见的 App 页面框架。
    2021-05-05
  • Android自定义View实现字母导航栏

    Android自定义View实现字母导航栏

    通常手机通讯录都会有索引栏,这篇文章主要介绍了Android自定义View实现字母导航栏,现在分享给大家。
    2016-10-10

最新评论