Android仿百度外卖头像随波浪漂浮效果

龙旋

共 4094字,需浏览 9分钟

 ·

2022-03-18 16:05

前几天看到有iOS仿百度外卖的个人页,所以就随手撸了个Android。


效果图如下:



改造轮子


轮子就不重复造了,github上已经有实现波浪的效果WaveView,WaveView作者的水波浪实现方法是比较优雅的,简单粗暴的,绘制一个波形图,填充到View里,移动波形图。


实现思路,画两条sin函数曲线相叠的波形图,通过Matrix平移,将sin函数的height传给头像view,进行Translation。


1、将轮子的水波浪方向上下颠倒



因为百度外卖的水波浪下面还有一个scrollview,而当scrollview往上滑动时,内容会被水波浪整个view覆盖,看着不舒服,所以将水波浪方向调整。

        wavePaint.setColor(mBehindWaveColor);        for (int beginX = 0; beginX < endX; beginX++) {            double wx = beginX * mDefaultAngularFrequency;            float beginY = (float) (mDefaultWaterLevel + mDefaultAmplitude * Math.sin(wx));            canvas.drawLine(beginX, beginY, beginX, -1, wavePaint);            waveY[beginX] = beginY;        }
wavePaint.setColor(mFrontWaveColor); final int wave2Shift = (int) (mDefaultWaveLength / 4); for (int beginX = 0; beginX < endX; beginX++) { canvas.drawLine(beginX, waveY[(beginX + wave2Shift) % endX], beginX, -1, wavePaint); }


2、在HeaderWaveView中提供get方法返回sin函数的height

public float getSinHeight() {        //sin函数的height        return (float) (getHeight() * mWaterLevelRatio                + mAmplitudeRatio / DEFAULT_AMPLITUDE_RATIO * mDefaultAmplitude * Math.sin(mWaveShiftRatio * getWidth() * mDefaultAngularFrequency));    }


动画实现


创建HeaderWaveHelper类进行动画管理,动画采用AnimatorSet进行组合。通过设置waterLevelRatio波浪水位、amplitudeRatio波浪振幅、mDefaultFloatViewRotation头像旋转调整角,调整整个效果。


当动画cancel时,不能很鬼畜的直接将振幅调零,start时也同理,应该缓慢过渡。

public void cancel() {        if (mAnimatorSet != null && !mHasCancel) {            mHasCancel = true;            mHasStart = false;            mAmplitudeAnim.cancel();            amplitudeChangeAnim = ObjectAnimator.ofFloat(                    mHeaderWaveView, "amplitudeRatio", mDefaultAmplitudeRatioT, 0.00001f);            amplitudeChangeAnim.setDuration(1000);            amplitudeChangeAnim.start();            amplitudeChangeAnim.addListener(new Animator.AnimatorListener() {                @Override                public void onAnimationStart(Animator animator) {
} @Override public void onAnimationEnd(Animator animator) { if (mFloatView.getAnimation() != null) { mFloatView.getAnimation().cancel(); } }
@Override public void onAnimationCancel(Animator animator) {
}
@Override public void onAnimationRepeat(Animator animator) {
} }); } }
public void start() {        mHeaderWaveView.setShowWave(true);        if (mAnimatorSet != null && !mHasStart) {            mHasStart = true;            mHasCancel = false;            if (amplitudeChangeAnim != null) {                amplitudeChangeAnim = ObjectAnimator.ofFloat(                        mHeaderWaveView, "amplitudeRatio", 0.00001f, mDefaultAmplitudeRatioF);                amplitudeChangeAnim.setDuration(1000);                amplitudeChangeAnim.start();            }
if (!mIsFirst) { mAnimatorSet.start(); mIsFirst = true;            } } }


三个动画A、B、C组合成一个AnimatorSet,playTogether,其中A、B是循环的,而C需要实现三个阶段,从0到0.5的过渡(单次),0.5-0.8的来回(循环),停止时再由当前循环数值到0(单次)。我的实现思路是,由A、B、C的循环构成AnimatorSet,在停止事件触发后cancel C的动画,然后new一个动画实现C的当前数值到0,开始同理。而整个结束开始过程中AnimatorSet一直运行。老是感觉这种动画实现方法不优雅,不知道有没有其他更好的实现方式。


还有一个小细节,view如果是在onCreat中获取,是无法获得view.getHeight(),采用view.getMeasuredHeight()代替,不理解可以去看看View的绘制机制。

waveShiftAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                //获取sin函数的height,更新mFloatView                float value = mHeaderWaveView.getSinHeight() - mFloatView.getMeasuredHeight();                mFloatView.setRotation(value + mDefaultFloatViewRotation);                mFloatView.setTranslationY(value);                mFloatView.invalidate();            }        });


源码地址:

https://github.com/sobinyuan/HeaderWaveView


到这里就结束啦。

浏览 68
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报