美文网首页程序猿学习自定义控件实用控件
头像随波浪漂浮效果—仿Android百度外卖

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

作者: Sobin | 来源:发表于2016-08-29 23:38 被阅读1900次

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


    demo.gif

    源码

    代码在github:HeaderWaveView

    改造轮子

    轮子就不重复造了,github上已经有实现波浪的效果WaveView,WaveView作者的水波浪实现方法是比较优雅的,简单粗暴的,绘制一个波形图,填充到View里,移动波形图。
    实现思路,画两条sin函数曲线相叠的波形图,通过Matrix平移,将sin函数的height传给头像view,进行Translation。

    • 1、将轮子的水波浪方向上下颠倒
    baiduwaimai.png

    因为百度外卖的水波浪下面还有一个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();
                }
            });
    
    

    相关文章

      网友评论

      • 长空北:求iOS 的源码,多谢
      • 1184104588:请问下,怎么改变中间波浪的宽度 和 添加波浪滚动的条数?
        1184104588:@Sobin 感谢。
        Sobin:@1184104588 y = Asin(wx+b)+h ,w影响周期,A影响振幅,h影响y位置,b为初相,所以要改变波浪宽度需要增大周期w,添加波浪滚动条数需要自己在shader里添加多一条曲线哦
      • 楚南玄:怎么计算得到波纹中间部分的height的算法是
        Sobin:@楚南玄 y = Asin(wx+b)+h ,然后根据onDraw时的Matrix 进行还原

      本文标题:头像随波浪漂浮效果—仿Android百度外卖

      本文链接:https://www.haomeiwen.com/subject/zlkfettx.html