When switch fragment with SwipeRefreshLayout during refreshing, fragment freezes but actually still work

AndroidAndroid FragmentsAndroid RecyclerviewSwiperefreshlayout

Android Problem Overview


UPDATE: I thought it worked correctly. But after some test trouble still exists *sniff*

Then I made a simpler version to see what exactly happen and I get to know that the refreshing fragment which should have been detached still left there. Or exactly, the view of the old fragment left there, on top of the newer fragment. Since RecyclerView's background of my original app is not transparent, so It turned out what I said before.

END OF UPDATE


I have a MainActivity with layout like this:

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

    <!-- As the main content view, the view below consumes the entire
         space available using match_parent in both dimensions. -->
    <FrameLayout android:id="@+id/container" android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <!-- android:layout_gravity="start" tells DrawerLayout to treat
         this as a sliding drawer on the left side for left-to-right
         languages and on the right side for right-to-left languages.
         If you're not building against API 17 or higher, use
         android:layout_gravity="left" instead. -->
    <!-- The drawer is given a fixed width in dp and extends the full height of
         the container. -->
    <fragment android:id="@+id/navigation_drawer"
        android:layout_width="@dimen/navigation_drawer_width" android:layout_height="match_parent"
        android:layout_gravity="start" tools:layout="@layout/fragment_navigation_drawer" />

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

The fragment ContentView I use to fill @id/container is configured as:

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

<android.support.v7.widget.RecyclerView
    android:id="@+id/tweet_list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/grey_300"/>

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

And here is onCreateView() of ContentView

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

    mRecyclerView = (RecyclerView) inflatedView.findViewById(R.id.tweet_list);
    mRecyclerView.setHasFixedSize(false);

    mLinearLayoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
    mRecyclerView.setLayoutManager(mLinearLayoutManager);

    mTweetList = new ArrayList<Tweet>;
    mAdapter = new TweetAdapter(mTweetList);
    mRecyclerView.setAdapter(mAdapter);
    mRecyclerView.setItemAnimator(new DefaultItemAnimator());

    mSwipeRefreshLayout = (SwipeRefreshLayout) inflatedView.findViewById(R.id.contentView);
    mSwipeRefreshLayout.setOnRefreshListener(
        ...
    );
    return inflatedView;
}

Then in MainActivity I switch the content to display by switching different ContentView. All looks good except one thing: when I switch ContentView fragments DURING refreshing by navigation drawer, then content freezes. But actually all things work as usual except you cannot see it.

Android Solutions


Solution 1 - Android

Well... After some struggling I eventually solved this problem by myself, in a tricky way...

I just need to add these in onPause() :

@Override
public void onPause() {
    super.onPause();
    ...

    if (mSwipeRefreshLayout!=null) {
        mSwipeRefreshLayout.setRefreshing(false);
        mSwipeRefreshLayout.destroyDrawingCache();
        mSwipeRefreshLayout.clearAnimation();
    }
}

Solution 2 - Android

This issue seems to still be occurring in appcompat 22.1.1. Wrapping the SwipeRefreshLayout inside a FrameLayout solved this for me

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v4.widget.SwipeRefreshLayout

    android:id="@+id/contentView"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/tweet_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/grey_300" />

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

Solution 3 - Android

In addition to @zlm2012 answer, I noticed that this issue was reproduced under Support Library 21.0.0, but seems to be fixed right now at 21.0.3

Solution 4 - Android

The solution works, but I think it is better just put those lines into its own function, like:

public void terminateRefreshing() {
    mSwipeRefreshLayout.setRefreshing(false);
    mSwipeRefreshLayout.destroyDrawingCache();
    mSwipeRefreshLayout.clearAnimation();
}

and call when switching the fragment.

Fragment prevFrag = fragmentManager.findFragmentById(drawerContainerId);

if(prevFrag instanceof SwipeRefreshFragment) {
	((SwipeRefreshFragment)prevFrag).terminateRefreshing();
}

fragmentManager.beginTransaction().replace(drawerContainerId, fragment).commit();

Solution 5 - Android

It works! Just remove the transition from your fragment replacement, in my case I removed the following from my code:

.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)

Solution 6 - Android

Set clickable="true" in top parent layout... It may solve the problem.

Solution 7 - Android

For the time being, you can avoid the issue by:

public class FixedRefreshLayout extends SwipeRefreshLayout {

    private static final String TAG = "RefreshTag";
    private boolean selfCancelled = false;

    public FixedRefreshLayout(Context context) {
        super(context);
    }

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

    @Override
    protected Parcelable onSaveInstanceState()
    {
        if(isRefreshing()) {
            clearAnimation();
            setRefreshing(false);
            selfCancelled = true;
            Log.d(TAG, "For hide refreshing");
        }
        return super.onSaveInstanceState();
    }

    @Override
    public void setRefreshing(boolean refreshing) {
        super.setRefreshing(refreshing);
        selfCancelled = false;
    }

    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        super.onWindowFocusChanged(hasWindowFocus);
        if(hasWindowFocus && selfCancelled) {
            setRefreshing(true);
            Log.d(TAG, "Force show refreshing");
        }
    }
}

This has the added advantage of resuming the animation incase you decide to not switch the fragment (coming from some menu). It also ties in with the internal animation to make sure that the refreshing animation does not return if the network refresh has already completed.

Solution 8 - Android

Terminate SwipeRefresh whenever navigation menu item is clicked. It worked.

private void selectItem(int position) {

       mSwipeRefreshLayout.setRefreshing(false);
       
       /*
        Other part of the function
       */
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionzlm2012View Question on Stackoverflow
Solution 1 - Androidzlm2012View Answer on Stackoverflow
Solution 2 - Androiduser1354603View Answer on Stackoverflow
Solution 3 - AndroidMetaphoreView Answer on Stackoverflow
Solution 4 - AndroidHuan XieView Answer on Stackoverflow
Solution 5 - AndroidraphaelbgrView Answer on Stackoverflow
Solution 6 - AndroidBishwajeet BiswasView Answer on Stackoverflow
Solution 7 - AndroidSaurabhView Answer on Stackoverflow
Solution 8 - AndroidShashidharaView Answer on Stackoverflow