一、目标:
实现点击“喜欢”按钮时有向上飘逸的气泡效果。如图所示:
二、核心规律分析:
飘动效果近似于sin函数的活动规律,但为了飘动比较自然,需要引入随机变量。因此我决定把w设置180,加随机波动180;相位为[0, 90]范围内的随机值;振幅为[0, mWidth / 2]的随机值(mWidth为控件的宽度)。并且创建ValueAnimator,duration设置为[2100, 2800]内随机波动。
最后可以得到随y从小到大变化的随机x飘动规律为:
其中,curVal / 100f用于表达当前时刻占设定动画时长的百分比。
double x = (Math.sin(Math.toRadians(mW * (1 - curVal / 100f) + mOffset))) * mAmplitude;
三、实现代码:
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
import com.tencent.edu.R;
import com.tencent.edu.common.utils.PixelUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class BubbleViewLike extends FrameLayout {
private int mWidth;
private int mHeight;
private List<Integer> mBubbleIcons = new ArrayList<>();
public BubbleViewLike(Context context) {
super(context);
init();
}
public BubbleViewLike(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public BubbleViewLike(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mBubbleIcons.add(R.drawable.bubble_awesome);
mBubbleIcons.add(R.drawable.bubble_champ_cup);
mBubbleIcons.add(R.drawable.bubble_cow);
mBubbleIcons.add(R.drawable.bubble_gift);
mBubbleIcons.add(R.drawable.bubble_hear);
mBubbleIcons.add(R.drawable.bubble_love);
mBubbleIcons.add(R.drawable.bubble_red_pack);
mBubbleIcons.add(R.drawable.bubble_rocket);
}
public void click() {
AnimatorBean b = new AnimatorBean();
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 100f);
valueAnimator.addUpdateListener(b);
valueAnimator.addListener(b);
valueAnimator.setDuration((int) (2100 + Math.random() * 700));
valueAnimator.start();
}
private class AnimatorBean implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
private View mBubbleView;
private double mAmplitude;
private double mOffset;
private double mW;
@Override
public void onAnimationStart(Animator animation, boolean isReverse) {
//一半空间宽度的振幅 × 随机缩放系数
mAmplitude = Math.random() * mWidth / 2;
//随机相位
mOffset = Math.random() * 90;
//随机频率
mW = 180f + Math.random() * 180f;
//创建小泡泡
mBubbleView = new Button(getContext());
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(PixelUtil.dp2px(30), PixelUtil.dp2px(30));
mBubbleView.setLayoutParams(params);
mBubbleView.setX(mWidth / 2);
mBubbleView.setY(mHeight);
mBubbleView.setBackgroundResource(mBubbleIcons.get(new Random().nextInt(mBubbleIcons.size())));
addView(mBubbleView);
}
@Override
public void onAnimationEnd(Animator animation, boolean isReverse) {
//消失了,可以销毁了
removeView(mBubbleView);
}
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (mBubbleView != null) {
float curVal = (float) animation.getAnimatedValue();
float scale = (50f - Math.abs(50f - curVal)) / 50f;
float y = ((1 - curVal / 100f) * mHeight);
double x = (Math.sin(Math.toRadians(mW * (1 - curVal / 100f) + mOffset))) * mAmplitude; //通过sin函数结合随机参数和进度值,产生随y值增大而产生不同的波浪x值
mBubbleView.setX((float) x + mWidth / 2);
mBubbleView.setY(y);
mBubbleView.setScaleX(scale * 1.6f);
mBubbleView.setScaleY(scale * 1.6f);
mBubbleView.setAlpha(scale);
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int w = MeasureSpec.getSize(widthMeasureSpec);
int h = MeasureSpec.getSize(heightMeasureSpec);
if (w != mWidth || h != mHeight) { //已经onMeasuer过一次,除非界面大小改动否则不重新初始化view
mWidth = w;
mHeight = h;
}
}
}
最终效果:
使用sin函数+随机参数实现泡泡效果
冒泡自定义控件放到课堂app中的效果