先上效果图
点击中间的按钮后,像外发散水波纹,再次点击水波纹消失。
实现原理
当点击按钮后,我们隔一段时间执行一个RippleCircleView的动画,动画包括扩大和透明度,通过PropertyValuesHolder将动画封装到一起,核心就是动画的实现
代码实现
首先是RippleCircleView,RippleCircleView啥也不干,就画一个圆,这个圆也是水波纹的灵魂,用来扩散的圆。
public class RippleCircleView extends View {
private RippleAnimationView mRippleAnimationView;
public RippleCircleView(RippleAnimationView rippleAnimationView) {
super(rippleAnimationView.getContext());
mRippleAnimationView = rippleAnimationView;
}
public RippleCircleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float radius = Math.min(getWidth(), getHeight()) / 2;
canvas.drawCircle(radius, radius, radius - RippleAnimationView.STROKE_WIDTH, mRippleAnimationView.getPaint());
}
}
有了水波纹的圆之后接下来我们实现水波纹的父布局,其父布局继承自相对布局,在初始化的时候将动画创建好,同时为了避免动画多次执行,添加一个标志位,并向外暴露开始和关闭动画的方法。
public class RippleAnimationView extends RelativeLayout {
private Paint mPaint;
int radius;
public static final int STROKE_WIDTH = 5;
int rippleColor;
List<View> views = new ArrayList<>();
private boolean animationRunning = false;
public RippleAnimationView(Context context) {
super(context);
}
public RippleAnimationView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
mPaint = new Paint();
mPaint.setAntiAlias(true);//抗锯齿
radius = 54;
rippleColor = ContextCompat.getColor(context, R.color.rippleColor);
mPaint.setStrokeWidth(STROKE_WIDTH);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(rippleColor);
// 延迟时间
int rippleDuration = 3500;
int singleDelay = rippleDuration / 4;//间隔时间 (上一个波纹 和下一个波纹的)
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(radius + STROKE_WIDTH, radius + STROKE_WIDTH);
params.addRule(CENTER_IN_PARENT);
for (int i = 0; i < 4; i++) {
RippleCircleView rippleCircleView = new RippleCircleView(this);
addView(rippleCircleView, params);
views.add(rippleCircleView);
PropertyValuesHolder aplhaHolder = PropertyValuesHolder.ofFloat("Alpha", 1, 0);
PropertyValuesHolder scaleXHolder = PropertyValuesHolder.ofFloat("scaleX", 10);
PropertyValuesHolder scaleYHolder = PropertyValuesHolder.ofFloat("scaleY", 10);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(rippleCircleView, aplhaHolder, scaleXHolder, scaleYHolder);
animator.setDuration(rippleDuration);
animator.setStartDelay(i * singleDelay);
animator.setRepeatMode(ObjectAnimator.RESTART);
animator.setRepeatCount(ObjectAnimator.INFINITE);
rippleCircleView.setTag(animator);
}
}
//启动动画 //停止动画
public void startRippleAnimation() {
if (!animationRunning) {
for (View rippleView : views) {
rippleView.setVisibility(VISIBLE);
((ObjectAnimator) rippleView.getTag()).start();
}
animationRunning = true;
}
}
public void stopRippleAnimation() {
if (animationRunning) {
Collections.reverse(views);
for (View rippleView : views) {
rippleView.setVisibility(INVISIBLE);
((ObjectAnimator) rippleView.getTag()).end();
((ObjectAnimator) rippleView.getTag()).cancel();
}
animationRunning = false;
}
}
public boolean isAnimationRunning() {
return animationRunning;
}
public Paint getPaint() {
return mPaint;
}
}
最后就是在activity中使用,先上布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary"
android:orientation="vertical">
<com.itzb.wangyimusicanimdemo.ripple.RippleAnimationView
android:id="@+id/layout_RippleAnimation"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/ImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:src="@mipmap/music" />
<TextView
android:id="@+id/tv1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/ImageView"
android:gravity="center"
android:text="点击识别音乐"
android:textColor="@android:color/white"
android:textSize="14sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tv1"
android:layout_marginTop="10dp"
android:gravity="center"
android:text="听歌识曲,识别你周围播放的歌"
android:textColor="@android:color/darker_gray"
android:textSize="12sp" />
</com.itzb.wangyimusicanimdemo.ripple.RippleAnimationView>
</RelativeLayout>
最后就是在点击按钮的时候播放动画即可,再次点击停止动画
public class RippleActivity extends AppCompatActivity {
private ImageView imageView;
RippleAnimationView rippleAnimationView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ripple);
imageView = (ImageView) findViewById(R.id.ImageView);
rippleAnimationView = (RippleAnimationView) findViewById(R.id.layout_RippleAnimation);
imageView.setOnClickListener(view -> {
if (rippleAnimationView.isAnimationRunning()) {
rippleAnimationView.stopRippleAnimation();
} else {
rippleAnimationView.startRippleAnimation();
}
});
}
}