How to collapse/expand a view based on scroll dire

2019-02-22 02:33发布

I have a View and a RecyclerView housed in a LinearLayout. What I want to achieve is something like this:

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

Basically when I scroll the RecyclerView up, the View collapses. It expands if I scroll the RecyclerView down.

I've tried various methods but the animation stutters if the finger jerks around a scroll position. It only animates well if the finger does a deliberate scroll movement in one direction. How do I do this correctly?

4条回答
一夜七次
2楼-- · 2019-02-22 02:44

You have to use Coordinator Layout with the 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: the comments will give errors if you put on a xml file. Is on propose so you will remember to read hahahah

查看更多
Emotional °昔
3楼-- · 2019-02-22 02:50

To achieve toolbar to expand and collapse smoothly you can apply translate animation or use CoordinatorLayout with AppBarLayout and Toolbar.

Animation : First you have to detect scroll up and scroll down on your RecyclerView. To do so you can set “setOnScrollListener” on your RecyclerView. Once you have both scroll up and scroll down, simply apply animation.

Code:

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);
                    }
                }
            }
        });

Animation Methods:

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: By using coordinatorLayout with appBarLayout and toolbar, and set the scroll flag used within the attribute app:layout_scrollFlags to achieve the scroll effect. It must be enabled for any scroll effects to take into effect. This flag must be enabled along with enterAlways, enterAlwaysCollapsed, exitUntilCollapsed, or snap.

  • enterAlways: The view will become visible when scrolling up. This flag is useful in cases when scrolling from the bottom of a list and wanting to expose the Toolbar as soon as scrolling up takes place.
  • enterAlwaysCollapsed: Normally, when only enterAlways is used, the Toolbar will continue to expand as you scroll down.Assuming enterAlways is declared and you have specified a minHeight, you can also specify enterAlwaysCollapsed. When this setting is used, your view will only appear at this minimum height. Only when scrolling reaches to the top will the view expand to its full height
  • exitUntilCollapsed: When the scroll flag is set, scrolling down will normally cause the entire content to move.By specifying a minHeight and exitUntilCollapsed, the minimum height of the Toolbar will be reached before the rest of the content begins to scroll and exit from the screen
  • snap: Using this option will determine what to do when a view only has been partially reduced. If scrolling ends and the view size has been reduced to less than 50% of its original, then this view to return to its original size. If the size is greater than 50% of its sized, it will disappear completely.

Code:

<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>
查看更多
beautiful°
4楼-- · 2019-02-22 03:04

Try this:- I also want this kind of animation on custom view and i have achieved it this way.

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);

                if (scrolledDistance > HIDE_THRESHOLD && controlsVisible) {
                    hideViews();
                    controlsVisible = false;
                    scrolledDistance = 0;
                } else if (scrolledDistance < -HIDE_THRESHOLD && !controlsVisible) {
                    showViews();
                    controlsVisible = true;
                    scrolledDistance = 0;
                }
            }
        });
    }

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

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

Edit - 1 for ScrollView try this listener

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;
                }
            }
        });

Hope it also helps you...

查看更多
Lonely孤独者°
5楼-- · 2019-02-22 03:07

You need to use CoordinatorLayout to achieve what you want. You could find all needed information in this tutorial.

查看更多
登录 后发表回答