75

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.

zlm2012
  • 1,811
  • 2
  • 12
  • 10
  • Normal, you want refresh, & while you change the entire list, then when refresh is terminate, it try to reload datas which had changed. You should lock the swipe when refreshing. – Sidd Nov 21 '14 at 10:08
  • Well, seems that it's Google's fault... After upgrade to AppCompat v21.0.2, all pain has gone XD. Thanks anyway <3 – zlm2012 Nov 21 '14 at 14:25

8 Answers8

106

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();
    }
}
zlm2012
  • 1,811
  • 2
  • 12
  • 10
  • 1
    Though i add this code but it seems not work,my support library is 22.0.0,indeed i find the freeze doesn't disappear in my app,I try to use setTransition() and this can also sovle the problem ,but both this code and setTransition() are not completely true,if you replace the fragment within about one second when swipe layout starts refreshing you will also find freeze content,but once exceed a short time the replace of fragment works properly,my last solution is to warp the swipe layout in a framelayout,and you don't need to add other code any more. – cajsaiko Apr 15 '15 at 11:36
  • The problem still persists with `support-v4:22.1.1` , I have had this problem while using swipe to refresh and sliding menu. I tried to setRefreshing false while calling fragment transaction, but that didnt' work. Thanks for the solution. – Ultimo_m Apr 28 '15 at 14:04
  • Although this might seem to fix the issue, it slows everything down. If I destroy the drawing cache and clear the animation layer of the swipeRefreshLayout, there is a delay(clearly visible for the human eye) when switching from 1 fragment to the other. Google, will you fix this? – Mike May 18 '15 at 12:48
  • 5
    Wrapping the SwipeRefreshLayout inside a FrameLayout solved in appcompat v7:22+ – Rahul Rastogi Jul 23 '15 at 00:02
  • You are a good man. It works perfect for me with `appcompat-v7:22.2.0` – German Latorre Aug 14 '15 at 13:51
  • Thanks this works for me, I'm using app appcompat-v7:22.2.1 – Kim Montano Mar 30 '16 at 02:24
  • 1
    Works here, too: `com.android.support:appcompat-v7:24.2.1` – Jeremy Sep 14 '16 at 12:59
  • 2
    Thanks. I only had `setRefreshing(false)` during FragmentTransaction. After adding `clearAnimation()`, my app works without freezing. – RobinBattle Oct 28 '16 at 18:15
  • For me, this approach requires the use of SwipeRefreshLayout.removeAllViews(); – Zach Oct 29 '16 at 07:30
77

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>
user1354603
  • 4,034
  • 4
  • 31
  • 43
  • Works great, thanks! Less hacky, though maybe slightly more inefficient than the most voted solution? – Pin Jul 16 '15 at 16:09
  • 1
    Hi @Pin, I don't think it's an heavier solution than the other.. FrameLayout is the lightest ViewGroup element, it shouldn't add so much complexity. – andrea.rinaldi Sep 30 '15 at 14:48
  • After switching back the tab, the SwipeRefreshLayout is gone. I went with zlm2012 's solution. – de. Jul 04 '16 at 12:28
  • 1
    This actually worked for me while in a Fragment. Wow. – EpicPandaForce Feb 03 '17 at 14:07
  • 2
    Thanks! It's solve my problem! I wasted such a large amount of time searching the reason and for solution. It's must be accepted as a right answer! – Trancer Feb 05 '17 at 12:14
  • 3
    In 25.2.0 bug still occurred and this solved problem. I wanted to flip the table before found this solution. Thanks. – Lukas Novak Mar 05 '17 at 10:23
  • This is working solution for `appcompat-v7:24+`.. I think this must be accepted answer... Thanks @user1354603 – Kushal May 15 '17 at 07:50
3

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

Metaphore
  • 749
  • 1
  • 7
  • 15
1

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();
Huan Xie
  • 11
  • 1
1

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)
raphaelbgr
  • 179
  • 3
  • 10
1

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

pacholik
  • 8,607
  • 9
  • 43
  • 55
  • 1
    this solutions works for click event , but for navigation drawer swipe event this solution not work. – samit Jun 29 '17 at 09:51
0

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.

Saurabh
  • 1,055
  • 14
  • 38
0

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

private void selectItem(int position) {

       mSwipeRefreshLayout.setRefreshing(false);

       /*
        Other part of the function
       */
}
Shashidhara
  • 665
  • 6
  • 22
  • I did this but I also had to do what the accepted answer said, otherwise I got a bunch of views on the screen left over from starting to refresh – EpicPandaForce Jan 26 '17 at 15:54