1.概述
最近看到网易云音乐的听歌识曲的页面,这次仿网易云音乐听歌识曲效果。
2.效果
1.水波纹效果
水波纹效果.gif
3.实现思路
1.我们通过自定义一个容器,以及自定义水波纹的圆。
2.自定义的容器添加几个自定义属性,水波纹颜色,半径,边宽等。
3.在我们自定义容器中获取自定义属性,并创建添加水波纹的圆。我这里设置为4个。
4.处理水波纹的圆是有X/Y轴的缩放动画。以及透明度的动画。
4.代码实现
4.1自定义属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--
ripple_anim_color : 水波纹的颜色
ripple_anim_type :水波纹的类型,填充,描边
radius :半径
strokWidth :描边宽度 -->
<declare-styleable name="RippleAnimationView">
<attr name="ripple_anim_color" format="color"/>
<attr name="ripple_anim_type" format="enum">
<enum name="fillRipple" value="0"/>
<enum name="strokeRipple" value="1"/>
</attr>
<attr name="radius" format="integer"/>
<attr name="strokWidth" format="integer"/>
</declare-styleable>
</resources>
4.2自定义容器
/**
* TODO:自定义容器,动态添加水波纹的圆。并通过动画实现
* 实现步骤
* 1.通过自定义属性来设置背景,以及水波纹颜色等
* 2.动态添加水波纹个数
* 3.通过动画实现水波纹的XY轴缩放,以及透明度的变化
*/
public class RippleAnimationView extends RelativeLayout {
//画笔
public Paint paint;
//水波纹半径
private int radius;
//水波纹颜色
private int rippleColor;
//水波纹描边宽度
private int strokWidth;
//水波纹的类型,0-实心充满 1-空心描边
private int rippleType;
//动画集合
private AnimatorSet animatorSet;
//水波纹圆的集合
private ArrayList<RippleCircleView> viewList = new ArrayList<>();
//动画执行的标志位
private boolean animationRunning = false;
public RippleAnimationView(Context context) {
this(context,null);
}
public RippleAnimationView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public RippleAnimationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context,attrs);
}
private void init(Context context, AttributeSet attrs) {
//解析自定义属性
TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.RippleAnimationView);
radius = array.getInteger(R.styleable.RippleAnimationView_radius, 54);
rippleColor = array.getColor(R.styleable.RippleAnimationView_ripple_anim_color, ContextCompat.getColor(context, R.color.rippleColor));
strokWidth = array.getInteger(R.styleable.RippleAnimationView_strokWidth,2);
rippleType = array.getInt(R.styleable.RippleAnimationView_ripple_anim_type, 0);
//初始化画笔
paint = new Paint();
paint.setAntiAlias(true); //设置抗锯齿
paint.setStrokeWidth(UIUtils.getInstance().getWidth(strokWidth)); //设置描边宽度
if (rippleType == 0){
paint.setStyle(Paint.Style.FILL);//设置样式
}else {
paint.setStyle(Paint.Style.STROKE);
}
paint.setColor(rippleColor); //设置颜色
//动态添加水波纹,设置它的大小
LayoutParams rippleParams = new LayoutParams(UIUtils.getInstance().getWidth(radius + strokWidth),UIUtils.getInstance().getWidth(radius + strokWidth));
rippleParams.addRule(CENTER_IN_PARENT, TRUE);
//设置最大缩放系数
float maxScale = 10;//UIUtils.getInstance().displayMetricsWidth / (float) ( (UIUtils.getInstance().getWidth(radius + strokWidth)));
//动画执行时间
int rippleDuration = 3500;
//间隔时间 (上一个波纹 和下一个波纹的)
int singleDelay = rippleDuration / 4;
//动画的集合,使用AnimatorSet
ArrayList<Animator> animatorList = new ArrayList<>();
//实例化一个波纹=view
for (int i = 0; i < 4; i++) {
//创建一个水波纹的View,添加到当前容器
RippleCircleView rippleCircleView = new RippleCircleView(this);
viewList.add(rippleCircleView);
//设置属性动画X轴,Y轴缩放动画,以及透明度动画
ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(rippleCircleView, View.SCALE_X,maxScale,0);
scaleXAnimator.setRepeatCount(ObjectAnimator.INFINITE); //无线重复
scaleXAnimator.setRepeatMode(ObjectAnimator.RESTART);
scaleXAnimator.setStartDelay(i * singleDelay);
scaleXAnimator.setDuration(rippleDuration);
animatorList.add(scaleXAnimator);
//Y轴缩放
ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(rippleCircleView, View.SCALE_Y,maxScale,0);
scaleYAnimator.setRepeatCount(ObjectAnimator.INFINITE);
scaleYAnimator.setRepeatMode(ObjectAnimator.RESTART);
scaleYAnimator.setStartDelay(i * singleDelay);
scaleYAnimator.setDuration(rippleDuration);
animatorList.add(scaleYAnimator);
//透明度
ObjectAnimator alphaYAnimator = ObjectAnimator.ofFloat(rippleCircleView, View.ALPHA,0f,1f);
alphaYAnimator.setRepeatCount(ObjectAnimator.INFINITE);
alphaYAnimator.setRepeatMode(ObjectAnimator.RESTART);
alphaYAnimator.setStartDelay(i * singleDelay);
alphaYAnimator.setDuration(rippleDuration);
animatorList.add(alphaYAnimator);
addView(rippleCircleView,rippleParams);
}
animatorSet = new AnimatorSet();
animatorSet.setInterpolator(new AccelerateDecelerateInterpolator()); //先加速后减速
//同时执行动画
animatorSet.playTogether(animatorList);
array.recycle();
}
public int getStrokWidth() {
return strokWidth;
}
/**
* TODO:开始动画
*/
public void startRippleAnimation(){
if (!animationRunning){ //如果没有执行动画的时候需要隐藏水波纹
for (RippleCircleView rippleView : viewList) {
rippleView.setVisibility(VISIBLE);
}
animatorSet.start();
animationRunning = true;
}
}
/**
* TODO:开始动画
*/
public void stopRippleAnimation(){
if (animationRunning){
Collections.reverse(viewList); //将它反序
for (RippleCircleView rippleView : viewList) {
rippleView.setVisibility(INVISIBLE);
}
animatorSet.end();
animationRunning = false;
}
}
public boolean isAnimationRunning() {
return animationRunning;
}
public void setAnimationRunning(boolean animationRunning) {
this.animationRunning = animationRunning;
}
}
4.3自定义水波纹的圆
/**
* TODO:自定义水波纹的圆
*/
public class RippleCircleView extends View {
//持有父容器的RippleAnimationView的引用
private RippleAnimationView rippleAnimationView;
public RippleCircleView(RippleAnimationView rippleAnimationView) {
this(rippleAnimationView.getContext(),null);
this.rippleAnimationView = rippleAnimationView;
this.setVisibility(View.INVISIBLE); //默认隐藏
}
public RippleCircleView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public RippleCircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 画圆
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
int radius = (Math.min(getWidth(), getHeight())) / 2;
canvas.drawCircle(radius,radius,radius - rippleAnimationView.getStrokWidth(),rippleAnimationView.paint);
}
}
5.使用
5.1.布局文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#000000"
tools:context="com.ych.ripple.RippleActivity">
<com.ych.ripple.view.RippleAnimationView
android:id="@+id/layout_RippleAnimation"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:radius="150"
app:ripple_anim_type="fillRipple"
app:strokWidth="18"
app:ripple_anim_color="#FF0000">
<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="@drawable/music" />
</com.ych.ripple.view.RippleAnimationView>
</LinearLayout>
</layout>
5.2.Activity中使用
/**
* TODO:仿网易云音乐听歌识曲水波纹效果
*/
public class RippleActivity extends AppCompatActivity {
private ActivityRippleBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this,R.layout.activity_ripple);
//这个是用于做屏幕适配的,设置中间图片的大小
ViewCalculateUtil.setViewLayoutParam(binding.ImageView, 300,300,0,0,0,0);
//设置点击事件
binding.ImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//如果动画正在执行,那么就暂停,否则开启
if (binding.layoutRippleAnimation.isAnimationRunning()){
binding.layoutRippleAnimation.stopRippleAnimation();
}else {
binding.layoutRippleAnimation.startRippleAnimation();
}
}
});
}
}