简体   繁体   English

如何在CoordinatorLayout中滚动滚动顶部和底部工具栏(或任何其他视图)进入/退出屏幕?

[英]How to animate both top and bottom Toolbars(or any other view) enter/exit screen on scroll in CoordinatorLayout?

My activity contains 2 AppBarLayouts in a CoordinatorLayout, one on the top of screen, and the other bottom. 我的活动在CoordinatorLayout中包含2个AppBarLayouts,一个在屏幕顶部,另一个在底部。 I want to make both of the 2 AppBarLayouts hide on scroll of a RecyclerView. 我想让两个AppBarLayouts隐藏在RecyclerView的滚动上。

When there is only the top one, it's easy to make it hide on scroll by adding app:layout_scrollFlags="scroll|enterAlways" to the Toolbar, and app:layout_behavior="@string/appbar_scrolling_view_behavior" to the container of RecyclerView. 当只有最顶层的时候,通过将app:layout_scrollFlags="scroll|enterAlways"到工具栏,并将app:layout_behavior="@string/appbar_scrolling_view_behavior"到RecyclerView的容器中,可以很容易地将其隐藏在滚动上。

When there is only the bottom AppBarLayout which hide on scroll, it was realized by making a custom behavior BottomAppBarLayoutBehavior extends AppBarLayout.Behavior . 当只有底部的AppBarLayout隐藏在滚动上时,它通过自定义行为实现BottomAppBarLayoutBehavior extends AppBarLayout.Behavior

However, when both of them are made hide on scroll, they succeed to hide but the RecyclerView shakes on scroll. 但是,当它们都在滚动时隐藏时,它们会成功隐藏但RecyclerView会在滚动时抖动。 And the area of top Toolbar leaves empty white space after hiding. 顶部工具栏的区域在隐藏后留下空白空间。 Below is the xml: 下面是xml:

<android.support.design.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.Toolbar
            android:id="@+id/topToolbar"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            app:layout_scrollFlags="scroll|enterAlways" />

    </android.support.design.widget.AppBarLayout>

    <FrameLayout
        android:layout_width="fill_parent"
        android:orientation="vertical"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        android:id="@+id/RecyclerViewContainer"
        android:layout_height="fill_parent"/>

    <android.support.design.widget.AppBarLayout 
        android:layout_width="match_parent"
        android:layout_gravity="bottom"
        app:layout_behavior="myPackage.BottomAppBarLayoutBehavior">

        <LinearLayout
            android:layout_width="match_parent"
            android:orientation="horizontal"
            android:layout_height="match_parent">
            <!-- some Views -->
        </LinearLayout>

    </android.support.design.widget.AppBarLayout>

</android.support.design.widget.CoordinatorLayout>

Below is the code of BottomAppBarLayoutBehavior: 以下是BottomAppBarLayoutBehavior的代码:

public class BottomAppBarLayoutBehavior extends AppBarLayout.Behavior {
    private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();
    private boolean mIsAnimatingOut = false;

    public BottomAppBarLayoutBehavior(Context context, AttributeSet attrs) {
        super();
    }

    @Override
    public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final AppBarLayout child,
                                       final View directTargetChild, final View target, final int nestedScrollAxes) {
        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
                || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
    }

    @Override
    public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final AppBarLayout child,
                               final View target, final int dxConsumed, final int dyConsumed,
                               final int dxUnconsumed, final int dyUnconsumed) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
        if (dyConsumed > 0 && !this.mIsAnimatingOut && child.getVisibility() == View.VISIBLE) {
            animateOut(child);
        } else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
            animateIn(child);
        }
    }

    private void animateOut(final AppBarLayout appBarLayout) {
        if (Build.VERSION.SDK_INT >= 14) {
            ViewCompat.animate(appBarLayout).translationY(168F).alpha(0.0F).setInterpolator(INTERPOLATOR).withLayer()
                    .setListener(new ViewPropertyAnimatorListener() {
                        public void onAnimationStart(View view) {
                            BottomAppBarLayoutBehavior.this.mIsAnimatingOut = true;
                        }

                        public void onAnimationCancel(View view) {
                            BottomAppBarLayoutBehavior.this.mIsAnimatingOut = false;
                        }

                        public void onAnimationEnd(View view) {
                            BottomAppBarLayoutBehavior.this.mIsAnimatingOut = false;
                            view.setVisibility(View.GONE);
                        }
                    }).start();
        } else {
            Animation anim = AnimationUtils.loadAnimation(appBarLayout.getContext(), R.anim.fab_out);
            anim.setInterpolator(INTERPOLATOR);
            anim.setDuration(200L);
            anim.setAnimationListener(new Animation.AnimationListener() {
                public void onAnimationStart(Animation animation) {
                    BottomAppBarLayoutBehavior.this.mIsAnimatingOut = true;
                }

                public void onAnimationEnd(Animation animation) {
                    BottomAppBarLayoutBehavior.this.mIsAnimatingOut = false;
                    appBarLayout.setVisibility(View.GONE);
                }

                @Override
                public void onAnimationRepeat(final Animation animation) {
                }
            });
            appBarLayout.startAnimation(anim);
        }
    }

    private void animateIn(AppBarLayout appBarLayout) {
        appBarLayout.setVisibility(View.VISIBLE);
        if (Build.VERSION.SDK_INT >= 14) {
            ViewCompat.animate(appBarLayout).scaleX(1.0F).scaleY(1.0F).alpha(1.0F)
                    .setInterpolator(INTERPOLATOR).withLayer().setListener(null)
                    .start();
        } else {
            Animation anim = AnimationUtils.loadAnimation(appBarLayout.getContext(), R.anim.fab_in);
            anim.setDuration(200L);
            anim.setInterpolator(INTERPOLATOR);
            appBarLayout.startAnimation(anim);
        }
    }
}

I have found the solution by myself. 我自己找到了解决方案。 The AppBarLayout wrapper for the bottom LinearLayout is redundant. 底部LinearLayoutAppBarLayout包装器是多余的。 Remove the AppBarLayout wrapper and make the Behavior class extends CoordinatorLayout.Behavior<LinearLayout> . 删除AppBarLayout包装并使Behavior类扩展CoordinatorLayout.Behavior<LinearLayout> Finally add the behavior to LinearLayout , then the top Toolbar and bottom LinearLayout enter and exit screen on scroll correctly. 最后将行为添加到LinearLayout ,然后顶部Toolbar和底部LinearLayout进入和退出屏幕正确滚动。

The correct activity xml: 正确的活动xml:

<android.support.design.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.Toolbar
            android:id="@+id/top_toolbar"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            app:layout_scrollFlags="scroll|enterAlways" />

    </android.support.design.widget.AppBarLayout>

    <FrameLayout
        android:layout_width="fill_parent"
        android:orientation="vertical"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        android:id="@+id/recycler_view_container"
        android:layout_height="fill_parent"/>

    <LinearLayout
        android:id="@+id/bottom_bar"
        android:layout_width="match_parent"
        android:orientation="horizontal"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        app:layout_behavior="*THE_FULL_PACKAGE_NAME*.LinearLayoutBehavior ">
        <!-- child views -->
    </LinearLayout>

</android.support.design.widget.CoordinatorLayout>

The correct Behavior class: 正确的Behavior类:

public class LinearLayoutBehavior extends CoordinatorLayout.Behavior<LinearLayout> {
    private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();
    private boolean mIsAnimatingOut = false;

    public LinearLayoutBehavior(Context context, AttributeSet attrs) {
        super();
    }

    @Override
    public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final LinearLayout child,
                                       final View directTargetChild, final View target, final int nestedScrollAxes) {
        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
                || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
    }

    @Override
    public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final LinearLayout child,
                               final View target, final int dxConsumed, final int dyConsumed,
                               final int dxUnconsumed, final int dyUnconsumed) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
        if (dyConsumed > 0 && !this.mIsAnimatingOut && child.getVisibility() == View.VISIBLE) {
            // User scrolled down and the FAB is currently visible -> hide the FAB
            animateOut(child);
        } else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
            // User scrolled up and the FAB is currently not visible -> show the FAB
            animateIn(child);
        }
    }

    private void animateOut(final LinearLayout linearLayout) {
        if (Build.VERSION.SDK_INT >= 14) {
            ViewCompat.animate(linearLayout).translationY(168F).alpha(0.0F).setInterpolator(INTERPOLATOR).withLayer()
                    .setListener(new ViewPropertyAnimatorListener() {
                        public void onAnimationStart(View view) {
                            LinearLayoutBehavior.this.mIsAnimatingOut = true;
                        }

                        public void onAnimationCancel(View view) {
                            LinearLayoutBehavior.this.mIsAnimatingOut = false;
                        }

                        public void onAnimationEnd(View view) {
                            LinearLayoutBehavior.this.mIsAnimatingOut = false;
                            view.setVisibility(View.GONE);
                        }
                    }).start();
        } else {
            Animation anim = AnimationUtils.loadAnimation(linearLayout.getContext(), R.anim.fab_out);
            anim.setInterpolator(INTERPOLATOR);
            anim.setDuration(200L);
            anim.setAnimationListener(new Animation.AnimationListener() {
                public void onAnimationStart(Animation animation) {
                    LinearLayoutBehavior.this.mIsAnimatingOut = true;
                }

                public void onAnimationEnd(Animation animation) {
                    LinearLayoutBehavior.this.mIsAnimatingOut = false;
                    linearLayout.setVisibility(View.GONE);
                }

                @Override
                public void onAnimationRepeat(final Animation animation) {
                }
            });
            linearLayout.startAnimation(anim);
        }
    }

    private void animateIn(LinearLayout linearLayout) {
        linearLayout.setVisibility(View.VISIBLE);
        if (Build.VERSION.SDK_INT >= 14) {
            ViewCompat.animate(linearLayout).translationY(0).scaleX(1.0F).scaleY(1.0F).alpha(1.0F)
                    .setInterpolator(INTERPOLATOR).withLayer().setListener(null)
                    .start();
        } else {
            Animation anim = AnimationUtils.loadAnimation(linearLayout.getContext(), R.anim.fab_in);
            anim.setDuration(200L);
            anim.setInterpolator(INTERPOLATOR);
            linearLayout.startAnimation(anim);
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM