penduDev
penduDev

Reputation: 4795

How to make Recycler View not scroll when appbar snaps

I have made the snapping app bar like this:

enter image description here

Please note that when the scroll is left in the middle(i.e the title is half visible, then the app bar snaps automatically)

In case of google play this is what the snap looks like:

enter image description here

Now, I want the snap to work like the one in google play. Which is that when the snap occurs, then only the app bar should snap and the recycler view should not move. It would be better if the solution supported pre lollipop devices too.

Thanks!

Upvotes: 10

Views: 4299

Answers (4)

godness
godness

Reputation: 154

I found a solution that works quite well in my project. It consists of 2 behaviors, one for the AppBarLayout and another one for the scrolling container. You can find it on Github here: appbar-snap-behavior

It's quite easy to install it:

compile "com.github.godness84:appbar-snap-behavior:0.1.1"

Remember to add maven { url "https://jitpack.io" } in your root build.gradle at the end of repositories:

allprojects {
        repositories {
            ...
            maven { url "https://jitpack.io" }
        }
}

Then:

  1. add app:layout_behavior="com.github.godness84.appbarsnapbehavior.AppBarSnapBehavior" to your AppBarLayout
  2. use app:layout_behavior="com.github.godness84.appbarsnapbehavior.ScrollingViewBehavior" into your scrolling container.

Unfortunately, since the default behavior of the AppBarLayout is replaced, some features are not available anymore (for example AppBarLayout.setExpanded()), but in normal situations it works! Give it a try and let me know.

Upvotes: 2

Narger
Narger

Reputation: 11

i've managed to 'bypass' the problem.

I've created an abstract class, use it in your projects !

THIS IS LAYOUT:

<FrameLayout
    android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
        >

        <android.support.v4.view.ViewPager
            android:id="@+id/viewPager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="@dimen/status_bar_height" />

        <FrameLayout
            android:id="@+id/appBarLayout"
            android:layout_width="match_parent"
            android:layout_height="@dimen/status_bar_app_bar"
            android:background="@color/appbar"
            >

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:layout_marginTop="@dimen/status_bar_height"
                android:background="@color/appbar" />

            <android.support.design.widget.AppBarLayout 
                android:id="@+id/tabLayout"
                android:layout_width="match_parent"
                android:layout_height="48dp"
                android:layout_gravity="bottom"
                android:background="@color/appbar"
                />

        </FrameLayout>

    </FrameLayout>

The framelayout'll be your "new" appbar.

Then, your fragments (the ones in the viewpager) must extend this class:

public abstract class SnappableAppBarFragment extends Fragment {

    public int scrollAttuale;
    private boolean attivaSnap = true;
    private boolean isTouching = false;

    public void setSnapActivated(boolean state){attivaSnap = state;}

    public void setUpSnappableAppbar(final View fragMainView, final NestedScrollView nestedScrollView, final FrameLayout appBar, final int actionBarHeight) {
        nestedScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
            @Override
            public void onScrollChanged() {
                if (!attivaSnap)
                    return;
                int scrollY = nestedScrollView.getScrollY();
                int differenza = scrollAttuale - scrollY;
                if (differenza > 0 && appBar.getY() < 0) {
                    //Esci
                    appBar.animate().translationYBy(differenza).setDuration(0).start();
                    if (appBar.getY() > 0) {
                        appBar.animate().translationY(0).setDuration(0).start();
                    }
                }
                if (differenza < 0 && appBar.getY() > -actionBarHeight) {
                    //Entra
                    appBar.animate().translationYBy(differenza).setDuration(0).start();
                    if (appBar.getY() < -actionBarHeight)
                        appBar.animate().translationY(-actionBarHeight).setDuration(0).start();
                }

                if (differenza >= -2 && differenza <= 2 && !isTouching ){
                    int spazioTot = actionBarHeight;
                    if ((Math.abs(appBar.getY()) < spazioTot / 2 || nestedScrollView.getScrollY() <= 200) && appBar.getY() != 0) {
                        //Espandi
                        appBar.animate().translationY(0).setDuration(270).start();
                    } else if (appBar.getY() != 0) {
                        //Chiudi
                        appBar.animate().translationY(-actionBarHeight).setDuration(270).start();
                    }
                }
                scrollAttuale = scrollY;
                //Scrolling verso l'alto differenza è positiva
                //Scrolling verso il basso differenza è negativa
            }
        });
        fragMainView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (!attivaSnap)
                    return false;

                if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
                    isTouching = false;
                    Handler handler = new Handler();
                    handler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            int spazioTot = actionBarHeight;
                            //   && nestedScrollView.getScrollY() <= 200  && nestedScrollView.getScrollY() <= 200   && nestedScrollView.getScrollY() <= 200
                            if ((Math.abs(appBar.getY()) < spazioTot / 2 || nestedScrollView.getScrollY() <= 200) && appBar.getY() != 0) {
                                //Espandi
                                appBar.animate().translationY(0).setDuration(270).start();
                            } else if (appBar.getY() != 0) {
                                //Chiudi
                                appBar.animate().translationY(-actionBarHeight).setDuration(270).start();
                            }
                        }
                    }, 0);
                }
                if (motionEvent.getAction() == MotionEvent.ACTION_DOWN || motionEvent.getAction() == MotionEvent.ACTION_SCROLL) {
                    isTouching = true;
                }
                return false;
            }
        });
    }
}

And finally, in fragments:

 public class Fragment1 extends SnappableAppBarFragment {

          ...

        @Override
            public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                     Bundle savedInstanceState) {
                // Inflate the layout for this fragment
                viewPrincipale = inflater.inflate(R.layout.fragment_home, container, false);

                mainActivity = (MainActivity) getActivity();

                setUpSnappableAppbar(viewPrincipale, (NestedScrollView) viewPrincipale.findViewById(R.id.nestedScrollView), parentAppBar, actionBarHeight);

        ...

        }

        public void setParentAppBar(FrameLayout frameLayout) {
            parentAppBar = frameLayout;
        }

        public void setActionBarHeight(int height) {
            actionBarHeight = height;
        }   
        ...
    }

Eventually, when declaring fragments, set parentappbar and actionbarheight from activity:

fragment1.setParentAppBar((FrameLayout) findViewById(R.id.appBarLayout));
fragment1.setActionBarHeight(toolbar.getLayoutParams().height);

That's it, sorry if it's long but is the only way i found in order to make it work !

Also, sorry for bad english, i'm an italian developer :P Bye

EDIT: IMPORTANT!! CHANGE IN SNAPPABLEAPPBARFRAGMENT THIS: final int actionBarHeight TO final float actionBarHeight !!! IT'LL BE SMOOTHER :D

Upvotes: 1

Sandy
Sandy

Reputation: 1005

Hi Use the below layout it will work exactly like the google play store app i have tested it

<?xml version="1.0" encoding="utf-8"?>

<android.support.design.widget.AppBarLayout
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingTop="@dimen/appbar_padding_top"
    android:theme="@style/AppTheme.AppBarOverlay">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:layout_scrollFlags="scroll|enterAlways|snap"
        app:popupTheme="@style/AppTheme.PopupOverlay">

    </android.support.v7.widget.Toolbar>

    <android.support.design.widget.TabLayout
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

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

<android.support.v4.view.ViewPager
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />

Also note i have used the following design support library

compile 'com.android.support:design:23.1.1'

let me know in case of any issue i will surely help you on that.

Upvotes: 1

Michele Lacorte
Michele Lacorte

Reputation: 5363

See my library Retractable Toolbar

You have to add this on build.gradle

compile 'it.michelelacorte.retractabletoolbar:library:1.0.0'

Than in your MainActivity.java use RecyclerView and this:

RetractableToolbarUtil.ShowHideToolbarOnScrollingListener showHideToolbarListener;
recyclerView.addOnScrollListener(showHideToolbarListener = new RetractableToolbarUtil.ShowHideToolbarOnScrollingListener(toolbar));

if (savedInstanceState != null) {
            showHideToolbarListener.onRestoreInstanceState((RetractableToolbarUtil.ShowHideToolbarOnScrollingListener.State) savedInstanceState
                    .getParcelable(RetractableToolbarUtil.ShowHideToolbarOnScrollingListener.SHOW_HIDE_TOOLBAR_LISTENER_STATE));
}

This is effect:

enter image description here

EDIT:

Since 23.1.0 design library you can add |snap attribute to your ToolBar layout:

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

It should be more or less what you are looking.

Upvotes: 6

Related Questions