Android自定义View实现仿美团底部导航动画

目录

效果展示

实现原理

这个效果主要是运用了贝塞尔曲线、自定义ViewGroup以及动画的知识。
1.贝塞尔曲线(用于实现顺畅的圆弧)
一个自定义的ViewGroup中总共使用了三段贝塞尔曲线:


而要做到三段贝塞尔曲线完美对接,则需要使第一段贝塞尔曲线的控制点与第二段贝塞尔曲线的起始点和控制点,三点在一条直线上,因此B点需要随着C点的升高。(如下图所示)

2.自定义ViewGroup
这里为了简单我是直接继承LinearLayout通过重写onMeasure方法来实现的,主要是对控件 的高度做了调整,代码如下:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if(imgChild == null){
            //获取ImageView 和ImageView 的宽度
            for(int i = 0 ; i < getChildCount() ; i++){
                View child = getChildAt(i);
                if(child instanceof ImageView){
                    imgChild = (ImageView) child;
                    imgChildWidth = imgChild.getMeasuredWidth();
                    LinearLayout.LayoutParams layoutParams = (LayoutParams) imgChild.getLayoutParams();
                    layoutParams.topMargin += (imgChildWidth / 2 + offsetFloatHeight);//整体的高度等于自适应高度加浮动的高度加偏移的高度
                    imgChild.setLayoutParams(layoutParams);
                    Log.e("宽度",imgChildWidth+"");
                    break;
                }
            }
        }
    }

3.动画
这里我总共定义了两个动画,一个用来浮起一个用来缩回,其中浮起的动画加了个顶部回弹的效果,代码如下:

 /**
     * 加载缩回的动画
     */
    private void initRestractAnim() {
        if(animatorRestract == null){
            animatorRestract = ValueAnimator.ofFloat(imgChildWidth / 2f, 0);
            animatorRestract.setDuration(500);
            animatorRestract.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float animatedValue = (float) animation.getAnimatedValue();
                    if(imgChild != null){
                        imgChild.setTranslationY(-animatedValue/3);
                    }
                    setFloatHeight(animatedValue);
                }
            });
        }
    }

    /**
     * 加载浮起的动画
     */
    private void initFloatAnim() {
        if(animatorFloat == null){
            animatorFloat = ValueAnimator.ofFloat(0, imgChildWidth / 2f + offsetFloatHeight,imgChildWidth / 2f);
            animatorFloat.setDuration(500);
            animatorFloat.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float animatedValue = (float) animation.getAnimatedValue();
                    if(imgChild != null){
                        imgChild.setTranslationY(-animatedValue/3);
                    }
                    setFloatHeight(animatedValue);
                }
            });
        }
    }

案例源码

想要详细了解的同学可以在这下载源码:https://gitee.com/itfitness/FitnessLib

推荐阅读更多精彩内容