3

When you refresh a facebook page, the SwipeRefreshLayout appears to be placed within the activity class and comes down from the top of the screen:

swipetorefresh

What appears to be a RecyclerView underneath it is refreshed. You will also need to scroll all the way up to the top of the screen before the SwipeRefreshLayout will activate itself - so if the ImageView in the toolbar does not appear on the top of the screen, then when you swipe down, you are actually just scrolling the RecyclerView. In the screenshot below, we can see that the top ImageView is not visible, so we are just scrolling our recyclerview.

below is a scroll

I'm trying to implement something similar in my app by doing the following in xml which looks similar to the first Facebook screenshot I posted above - my activity has the 'swipeRefresh circle icon' coming out from the top and follows the standard layout xml for setting up a toolbar with an ImageView:

MAIN ACTIVITY XML:

<android.support.v4.widget.SwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipeContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/coordinatorlayout"
        >

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

            <android.support.design.widget.CollapsingToolbarLayout
                android:id="@+id/collapsing_toolbar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:contentScrim="?attr/colorPrimary"
                android:background="@color/white"
                app:expandedTitleTextAppearance="@color/transparent"
                app:layout_scrollFlags="scroll|exitUntilCollapsed"
                >

                    <ImageView
                        android:id="@+id/backdrop"
                        android:contentDescription="@string/photo"
                        android:layout_width="match_parent"
                        android:background="@color/white"
                        android:layout_height="192dp"
                        android:scaleType="centerCrop"
                        android:fitsSystemWindows="true"
                        />

                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    app:layout_collapseMode="pin"/>

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

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

            <FrameLayout
                android:id="@+id/container"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                app:layout_behavior="@string/appbar_scrolling_view_behavior">

                <android.support.v4.widget.NestedScrollView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:fillViewport="true"
                    >
                </android.support.v4.widget.NestedScrollView>

            </FrameLayout>

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

</android.support.v4.widget.SwipeRefreshLayout>

However, the SwipeRefreshLayout takes the full focus of the activity including any scroll actions in my Fragment that I inflate within my FrameLayout nested into my main activity xml. The Fragment that I inflate is a RecyclerView which scrolls up and down similar to Facebook's RecyclerView.

To get away from this problem, I do know that I can just remove the SwipeRefreshLayoutfrom my Activity xml and placed it with my Fragment xml but then my SwipeRefresh circle icon will only start towards the end of the screen instead of the top.

Not sure how Facebook managed to stop SwipeRefreshLayoutfrom gaining the full focus for all scroll actions - does anyone know how they implemented their SwipeRefreshLayout?

This is my Fragment xml code:

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

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/post_container"
        android:background="#E0E0E0">

        <ProgressBar
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/progresswheel"
            android:layout_margin="32dp"
            android:layout_centerHorizontal="true"
            android:visibility="gone"
            tools:visibility="visible"/>

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

    </RelativeLayout>

</android.support.design.widget.CoordinatorLayout>
Simon
  • 19,658
  • 27
  • 149
  • 217
  • put `SwipeRefreshLayout` in your `Fragment` not in `Activity` as parent. – Vipul Asri Dec 28 '15 at 10:33
  • yes - as I mentioned that in my question but that is not the preferred option as then the swipe to refresh circle will start in the middle of the page and not at the top. – Simon Dec 28 '15 at 10:57
  • may be you are doing it wrong. Please post your complete code including fragment part. – Vipul Asri Dec 28 '15 at 13:06
  • xml code added for the fragment. I don't think any of the java code is relevant. This is a layout issue. – Simon Dec 28 '15 at 13:23
  • I was talking about XML code only. And where are you adding this fragment layout in activity? in `NestedScrollView`? – Vipul Asri Dec 28 '15 at 13:30
  • Nope - I inflate it within the framelayout. The nestedScrollView just helps it register any scrolling behavior within my fragment so that the toolbar will maximise and minimize when the contents in my framelayout are scrolled. – Simon Dec 28 '15 at 13:33
  • why are you using `FrameLayout` as parent of `NestedScrollView`? My suggestion would be to remove it and also remove `CoordinatorLayout` as parent in your fragment. Try it. – Vipul Asri Dec 28 '15 at 14:20

1 Answers1

6

OK - I finally figured this out.

It is not easy but the solution is actually to take out the fragment class and replace it with a normal recyclerview.

In XML:

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


<android.support.v4.widget.SwipeRefreshLayout
    android:id="@+id/swipeContainer"
    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.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/coordinatorlayout"
        android:fitsSystemWindows="true"
        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:id="@+id/appbar"
            android:fitsSystemWindows="true"
            >
            <android.support.design.widget.CollapsingToolbarLayout
                android:id="@+id/collapsing_toolbar"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:contentScrim="?attr/colorPrimary"
                android:background="@color/white"
                app:expandedTitleMarginStart="48dp"
                app:expandedTitleMarginEnd="64dp"
                android:fitsSystemWindows="true"
                app:expandedTitleTextAppearance="@color/transparent"
                app:layout_scrollFlags="scroll|exitUntilCollapsed"
                >

                    <ImageView
                        android:layout_width="match_parent"
                        android:layout_height="300dp"
                        android:scaleType="centerCrop"
                        android:fitsSystemWindows="true"
                        android:id="@+id/photo"
                        app:layout_collapseMode="parallax"
                        android:src="@drawable/hand_gestures"/>

                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    app:layout_collapseMode="pin"/>


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

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

        <android.support.v7.widget.RecyclerView
            android:layout_width="match_parent"
            android:fitsSystemWindows="true"
            android:fillViewport="true"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            android:layout_height="match_parent"
            android:id="@+id/recyclerview">

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

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

</android.support.v4.widget.SwipeRefreshLayout>

Then you will need to follow the solution on this thread: Android: CollapsingToolbarLayout and SwipeRefreshLayout get stuck to get the swipeToRefreshLayout working properly.

You do this by implementing the following - copied from the thread:

in your fragment/activity make it implement AppBarLayout.OnOffsetChangedListener (Now the refreshing is enabled when toolbar is fully expanded):

@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
    if (collapsingToolbarLayout.getHeight() + verticalOffset < 2 * ViewCompat.getMinimumHeight(collapsingToolbarLayout)) {
        swipeRefreshLayout.setEnabled(false);
    } else {
        swipeRefreshLayout.setEnabled(true);
    }
}

Override onPause() & onResume() as in @blackcj answer:

@Override
public void onResume() {
    super.onResume();
    appBarLayout.addOnOffsetChangedListener(this);
}

@Override
public void onPause() {
    super.onPause();
    appBarLayout.removeOnOffsetChangedListener(this);
}

Then set LinearLayoutManager to your recyclerView:

LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);

For me this worked like a charm, first appBarlayout gets expanded and only then swipeRefreshLayout triggers refreshing.

RustamG
  • 1,815
  • 1
  • 14
  • 16
Simon
  • 19,658
  • 27
  • 149
  • 217