【问题标题】:How to collapse/expand a view based on scroll direction?如何根据滚动方向折叠/展开视图?
【发布时间】:2016-10-11 18:16:33
【问题描述】:

我在 LinearLayout 中有一个 View 和一个 RecyclerView。我想要实现的是这样的:

https://material.google.com/patterns/scrolling-techniques.html#scrolling-techniques-behavior

基本上,当我向上滚动 RecyclerView 时,视图会折叠。如果我向下滚动 RecyclerView,它会展开。

我尝试了各种方法,但如果手指在滚动位置附近晃动,动画就会卡顿。只有当手指在一个方向上进行有意识的滚动运动时,它才会产生良好的动画效果。我该如何正确地做到这一点?

【问题讨论】:

  • 你见过 CollapsingToolbarLayout 吗?
  • 嘿@geft 检查我的答案,让我知道它是否有效。
  • 您能否提供一个新链接,以便我们查看您所指的内容,以便了解它是否也是我们正在查找的内容?否则,不清楚这个问题是什么,也不清楚它的答案

标签: android android-layout view scroll


【解决方案1】:

您必须将 Coordinator Layout 与 CollapsingToolbarLayout 一起使用

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

<android.support.design.widget.AppBarLayout
    android:id="@+id/app_bar_layout"
    android:layout_width="match_parent"
    android:layout_height="210dp"
    android:stateListAnimator="@animator/appbar_always_elevated" //I put this here because I want to have shadow when is open, but you have to create the xml file.
    android:background="@color/WHITE">

    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/collapsing_toolbar_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:collapsedTitleTextAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
        app:expandedTitleMarginStart="72dp"
        app:expandedTitleTextAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
        app:layout_scrollFlags="scroll|exitUntilCollapsed"> //HERE you should take a look what you want your collapse bar do.

        <Here you put the content for you collapse bar, like a ImageView>

        <android.support.v7.widget.Toolbar //This is the size of your fixed bar when you collapse, even here you can put a back button, for example
            android:id="@+id/app_bar"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            app:layout_collapseMode="pin" />
    </android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>

<android.support.v4.widget.SwipeRefreshLayout
    android:id="@+id/main_home_list_swipe"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" >

    <android.support.v7.widget.RecyclerView
        android:id="@+id/main_home_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

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

Obs:如果您放入 xml 文件,cmets 会出错。正在提议中,所以你会记得阅读哈哈哈

【讨论】:

  • 我可以删除工具栏并添加我的自定义视图吗?
  • 您的自定义视图是什么?可能你应该把工具栏放在里面,但我必须知道什么是 XD。 @XoXo
  • stackoverflow.com/questions/48082432/… 检查自定义布局。我什至展示了我期待的 onScroll 转换。
  • @XoXo 在链接上回答
  • @Canato android:stateListAnimator="@animator/appbar_always_elevated" 是什么?
【解决方案2】:

试试这个:- 我也想要这种自定义视图上的动画,我已经通过这种方式实现了。

public class TestActivity extends AppCompatActivity {
    private static final int HIDE_THRESHOLD = 20;
    //this is you custom layout it is any thing.
    LinearLayout customLayout;
    private int scrolledDistance = 0;
    private boolean controlsVisible = true;
    private RecyclerView recyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.your_layout);

        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                scrolledDistance = dy;
                if (scrolledDistance > HIDE_THRESHOLD && controlsVisible) {
                    hideViews();
                    controlsVisible = false;
                } else if (scrolledDistance < -HIDE_THRESHOLD && !controlsVisible) {
                    showViews();
                    controlsVisible = true;
                }
            }
        });
    }

    private void hideViews() {
        customLayout.animate().translationY(-customLayout.getHeight()).setInterpolator(new AccelerateInterpolator(2));
    }

    private void showViews() {
        customLayout.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2));
    }
}

编辑 - 1 对于ScrollView 试试这个监听器

scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
            @Override
            public void onScrollChanged() {
                if (scrolledDistance > HIDE_THRESHOLD && controlsVisible) {
                    hideViews();
                    controlsVisible = false;
                    scrolledDistance = 0;
                } else if (scrolledDistance < -HIDE_THRESHOLD && !controlsVisible) {
                    showViews();
                    controlsVisible = true;
                    scrolledDistance = 0;
                }
            }
        });

希望对你也有帮助...

【讨论】:

  • 我可以用 ScrollView 做到这一点吗?你能给我一些关于这个相关问题的想法吗? stackoverflow.com/questions/48082432/…
  • 在您的代码中。 customLayout 未附加到 java 代码。你也可以把你的布局?动画不适合我。
  • 这是您使用任何自定义布局 @XoXo 创建的自定义标题。
  • pastebin.com/VQkLg6s3 这就是我现在的布局。你能告诉我为什么动画不工作吗?
  • @XoXo 在那种情况下你为什么不试试CoordinatorLayout 它更适合你这种情况。
【解决方案3】:

要实现工具栏的平滑展开和折叠,您可以应用翻译动画或将CoordinatorLayout与AppBarLayout和Toolbar一起使用。

动画:首先,您必须检测 RecyclerView 上的向上滚动和向下滚动。为此,您可以在 RecyclerView 上设置“setOnScrollListener”。向上滚动和向下滚动后,只需应用动画即可。

代码:

rvHomeList.setOnScrollListener(new RecyclerView.OnScrollListener() {

            int verticalOffset;

            boolean scrollingUp;

            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    if (scrollingUp) {
                        Log.e("onScrollStateChanged", "UP");
                        if (verticalOffset > llTop.getHeight()) {
                            toolbarAnimateHide();
                        }
                    } else {
                        Log.e("onScrollStateChanged", "down");
                        if (llTop.getTranslationY() < llTop.getHeight() * -0.6 && verticalOffset > llTop.getHeight()) {
                            toolbarAnimateHide();
                        } else {
                            toolbarAnimateShow(verticalOffset);
                        }
                    }
                }
            }

            @Override
            public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                verticalOffset += dy;
                scrollingUp = dy > 0;
                int toolbarYOffset = (int) (dy - llTop.getTranslationY());
                llTop.animate().cancel();
                if (scrollingUp) {
                    Log.e("onScrolled", "UP");
                    if (toolbarYOffset < llTop.getHeight()) {
                        llTop.setTranslationY(-toolbarYOffset);
                    } else {
                        llTop.setTranslationY(-llTop.getHeight());
                    }
                } else {
                    Log.e("onScrolled", "down");
                    if (toolbarYOffset < 0) {
                        llTop.setTranslationY(0);
                    } else {
                        llTop.setTranslationY(-toolbarYOffset);
                    }
                }
            }
        });

动画方法:

private void toolbarAnimateShow(final int verticalOffset) {
        if (!isShowing) {
            isShowing = true;
            llTop.animate()
                    .translationY(0)
                    .setInterpolator(new LinearInterpolator())
                    .setDuration(180)
                    .setListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationStart(Animator animation) {
                            llTop.setVisibility(View.VISIBLE);
                            isShowing = false;
                        }
                    });
        }
    }

    private void toolbarAnimateHide() {
        if (!isHidding) {
            isHidding = true;
            llTop.animate()
                    .translationY(-llTop.getHeight())
                    .setInterpolator(new LinearInterpolator())
                    .setDuration(180)
                    .setListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            llTop.setVisibility(View.GONE);
                            isHidding = false;
                        }
                    });
        }
    }

CoordinatorLayout with AppBarLayout and Toolbar: 通过appBarLayout 和toolbar 使用coordinatorLayout,并在app:layout_scrollFlags 属性中设置滚动标志来实现滚动效果。必须启用它才能使任何滚动效果生效。此标志必须与 enterAlways、enterAlwaysCollapsed、exitUntilCollapsed 或 snap 一起启用。

  • enterAlways: 向上滚动时视图将变为可见。当从列表底部滚动并希望在向上滚动时立即显示工具栏时,此标志很有用。
  • enterAlwaysCollapsed: 通常,当只使用 enterAlways 时,工具栏会随着您向下滚动而继续展开。假设声明了 enterAlways 并且您已指定了 minHeight,您也可以指定 enterAlwaysCollapsed。使用此设置时,您的视图将仅显示在此最小高度。只有当滚动到顶部时,视图才会展开到其全高
  • exitUntilCollapsed: 设置滚动标志时,向下滚动通常会导致整个内容移动。通过指定 minHeight 和 exitUntilCollapsed,将在其余内容开始之前达到 Toolbar 的最小高度滚动并退出屏幕
  • snap: 使用此选项将确定当视图仅部分缩小时要执行的操作。如果滚动结束并且视图大小已减小到其原始大小的 50% 以下,则此视图返回其原始大小。如果大小大于其大小的 50%,它将完全消失。

代码:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/llBase"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:orientation="vertical">

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <android.support.design.widget.AppBarLayout
            android:id="@+id/appbarContainer"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="@dimen/_40sdp"
                android:gravity="center"
                android:theme="@style/ThemeOverlay.AppCompat.Light"
                app:layout_scrollFlags="scroll|enterAlways">

                <include
                    layout="@layout/top_bar"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content" />
            </android.support.v7.widget.Toolbar>
        </android.support.design.widget.AppBarLayout>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <RelativeLayout
                android:id="@+id/rlMain"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"/>

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

</LinearLayout>

【讨论】:

  • 谢谢兄弟。拯救我的一天
【解决方案4】:

您需要使用 CoordinatorLayout 来实现您想要的。 您可以在此tutorial 中找到所有需要的信息。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多