自定义View之网易云音乐听歌识曲水波纹动画

 

先上效果图

点击中间的按钮后,像外发散水波纹,再次点击水波纹消失。

实现原理

当点击按钮后,我们隔一段时间执行一个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();
            }
        });

    }
}

Demo地址:https://github.com/987570437/WangYiMusicAnimDemo

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现 Android 音乐播放器专辑图片旋转,可以使用自定义 View 来实现。以下是实现类似网易云音乐旋转专辑 View 的步骤: 1. 首先创建一个自定义 View,继承自 ImageView。 2. 在自定义 View 中添加一个属性 mRotateDegree 表示当前旋转的角度。 3. 在 onDraw 方法中,使用 Canvas 对象绘制出图片。 4. 在 onDraw 方法中,使用 Matrix 对象对图片进行旋转操作。 5. 在 onDraw 方法中,使用 ValueAnimator 对象控制旋转的速度和方向。 6. 在自定义 View 中添加一个方法 startRotate(),用于开始旋转。 7. 在自定义 View 中添加一个方法 stopRotate(),用于停止旋转。 以下是示例代码: ```java public class RotateImageView extends ImageView { private float mRotateDegree = 0; private ValueAnimator mRotateAnimator; public RotateImageView(Context context) { super(context); } public RotateImageView(Context context, AttributeSet attrs) { super(context, attrs); } public RotateImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onDraw(Canvas canvas) { Matrix matrix = new Matrix(); matrix.postRotate(mRotateDegree, getWidth() / 2f, getHeight() / 2f); canvas.setMatrix(matrix); super.onDraw(canvas); } public void startRotate() { if (mRotateAnimator != null && mRotateAnimator.isRunning()) { return; } mRotateAnimator = ValueAnimator.ofFloat(0f, 360f); mRotateAnimator.setDuration(10000); mRotateAnimator.setRepeatCount(ValueAnimator.INFINITE); mRotateAnimator.setInterpolator(new LinearInterpolator()); mRotateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mRotateDegree = (float) animation.getAnimatedValue(); invalidate(); } }); mRotateAnimator.start(); } public void stopRotate() { if (mRotateAnimator != null) { mRotateAnimator.cancel(); } } } ``` 在使用时,只需要将 RotateImageView 添加到布局中,然后调用 startRotate() 方法开始旋转,调用 stopRotate() 方法停止旋转即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值