1

In my App, I have two Fragments: FragmentA and FragmentB which are loaded from my MainActivity by the help of an SlidingMenu.

When FragmentB is shown, it immediatelly starts an AsyncTask in background to load data from a server. The loading process is reflected by an active SwipeRefreshLayout to the user.

When loading is finished, the UI of FragmentB is refreshed with the loaded data, by the help of a delegate which is passed to MyAsyncClass.

I am now facing the following problem: As soon as processFinish in my delegate is called, I am stopping the Refresh of the SwipeRefreshLayout by the help of swipeRefreshLayout.setRefreshing(false). If the user is going back to FragmentA while background-Task is still in progress, it appears in about 20% of the cases, that FragmentB is still in background of FragmentA in the User Interface (see Screenshot below).

As you can see in the image, the FragmentA is shown (the text "No tracks played yet" is from FragmentA), but in the background you can see still the FragmentB.

As already mentioned, this is not in 100% of the cases, so it should not be an background/transparency issue. If I comment out the swipeRefreshLayout.setRefreshing(false), then the problem is not reproductible, but somehow I need to stop the SwipeRefreshLayout in case the user stays on FragmentB.

Any idea what causes this behaviour?

Screenshot of the problem

AsyncResponse-Interface:

public interface AsyncResponse {

    void processFinish(String result);
}

FragmentB-Class:

    public class FragmentB extends MyFragment implements SwipeRefreshLayout.OnRefreshListener, AsyncResponse {

        private SwipeRefreshLayout swipeRefreshLayout;
        private View view;
        private RecyclerView recyclerView;

        public FragmentB () {
        }


        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

            view = inflater.inflate(R.layout.songs_list, container, false);

            swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh_layout);
            swipeRefreshLayout.setOnRefreshListener(this);
            swipeRefreshLayout.setColorSchemeColors(getResources().getColor(R.color.colorPrimary));

            // Set the adapter
            Context context = view.getContext();
            recyclerView = (RecyclerView) view.findViewById(R.id.listinclude);
            recyclerView.setLayoutManager(new LinearLayoutManager(context));

            recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), R.drawable.divider));

            List<Song> songsList = databaseHandler.getAllSongs();
            setVisibilities(songsList);

            this.recyclerViewAdapter = new RecyclerViewAdapter(songsList, mListener, false, false);

            recyclerView.setAdapter(this.recyclerViewAdapter);

  swipeRefreshLayout.setRefreshing(true);
            receive();

            return view;
        }

        private void setVisibilities(List<Song> songsList) {
            ViewFlipper viewFlipper = (ViewFlipper) view.findViewById(R.id.viewFlipper);
            if (songsList.isEmpty() && viewFlipper.getDisplayedChild() == 0) {
                viewFlipper.setDisplayedChild(1);
            } else if (!songsList.isEmpty() && viewFlipper.getDisplayedChild() == 1) {
                viewFlipper.setDisplayedChild(0);
            }
        }


        @Override
        public void onRefresh() {
            if (MyOnlineHelper.isOnline(getContext())) {
                receive();
            } else {
                swipeRefreshLayout.setRefreshing(false);
            }
        }

        private void receive() {
            new MyAsyncClass(this, getContext()).execute("receiving");
        }

        @Override
        public void processFinish(String output) {
                // refreshUI
                swipeRefreshLayout.setRefreshing(false);
        }
    }

FragmentA-Class:

public class FragmentA extends MyFragment implements AsyncResponse {

    private View view;
    private RecyclerView recyclerView;

    public FragmentA() {
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        List<Song> songsList = myDatabaseHandler.getAllSongs();

        view = inflater.inflate(R.layout.home_list, container, false);

        // Set the adapter
        Context context = view.getContext();
        recyclerView = (RecyclerView) view.findViewById(R.id.listinclude);
        recyclerView.setLayoutManager(new LinearLayoutManager(context));

        recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), R.drawable.divider));

        setVisibilities(songsList);

        this.recyclerViewAdapter = new RecyclerViewAdapter(songsList, mListener, false, true);

        recyclerView.setAdapter(this.recyclerViewAdapter);
        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    }


    private void setVisibilities(List<Song> songsList) {
        ViewFlipper viewFlipper = (ViewFlipper) view.findViewById(R.id.viewFlipper);
        if (songsList.isEmpty() && viewFlipper.getDisplayedChild() == 0) {
            viewFlipper.setDisplayedChild(1);
        } else if (!songsList.isEmpty() && viewFlipper.getDisplayedChild() == 1) {
            viewFlipper.setDisplayedChild(0);
        }
    }


    @Override
    public void processFinish(String output) {
        // does something
    }
}

MyAsyncClass:

public class MyAsyncClass extends AsyncTask<String, Integer, String> {

        private AsyncResponse delegate;
        private Context mContext;

        private MyDatabaseHandler myDatabaseHandler;

        public MyAsyncClass(AsyncResponse delegate, Context context) {
            this.delegate = delegate;
            this.mContext = context;
        }


        @Override
        protected String doInBackground(String... params) {

            // calling webservice and writing it to the database
        }

        @Override
        protected void onPostExecute(String result) {
            delegate.processFinish(result);
            super.onPostExecute(result);
        }
    }

MainActivity-Class:

public class MainActivity extends AppCompatActivity implements MyFragment.OnListFragmentInteractionListener, AsyncResponse {


    private FragmentA fragmentA = new FragmentA();
    private FragmentB fragmentB;

    private NavigationView navigationView;
    private DrawerLayout drawer;
    private Toolbar toolbar;

    // index to identify current nav menu item
    private static int navItemIndex = 0;

    public static String CURRENT_TAG = MyConstants.TAG_FRAGMENT_A;

    // toolbar titles respected to selected nav menu item
    private String[] activityTitles;

    // flag to load home fragment when user presses back key
    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        fragmentA.setDatabaseHandler(this.myDatabaseHandler);

        // Init UI
        toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        mHandler = new Handler();

        drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        navigationView = (NavigationView) findViewById(R.id.nav_view);

        fabSendButton = (FloatingActionButton) findViewById(R.id.fab);

        // Navigation view header
        navHeader = navigationView.getHeaderView(0);


        // load toolbar titles from string resources
        activityTitles = getResources().getStringArray(R.array.sliding_menu_item_activity_titles);


        // initializing navigation menu
        setUpNavigationView();

        if (savedInstanceState == null) {
            navItemIndex = 0;
            CURRENT_TAG = MyConstants.TAG_FRAGMENT_A;
            loadHomeFragment();
        }
    }

    /***
     * Returns respected fragment that user
     * selected from navigation menu
     */
    private void loadHomeFragment() {

        // set toolbar title
        setToolbarTitle();

        // if user select the current navigation menu again, don't do anything
        // just close the navigation drawer
        if (getSupportFragmentManager().findFragmentByTag(CURRENT_TAG) != null) {
            drawer.closeDrawers();
            return;
        }

        // Sometimes, when fragment has huge data, screen seems hanging
        // when switching between navigation menus
        // So using runnable, the fragment is loaded with cross fade effect
        // This effect can be seen in GMail app
        Runnable mPendingRunnable = new Runnable() {
            @Override
            public void run() {
                // update the activity_main_header_with_item content by replacing fragments
                Fragment fragment = getFragment();
                FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
                fragmentTransaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
                fragmentTransaction.replace(R.id.frame, fragment, CURRENT_TAG);
                fragmentTransaction.commit();
            }
        };

        // If mPendingRunnable is not null, then add to the message queue
        if (mPendingRunnable != null) {
            mHandler.post(mPendingRunnable);
        }

        //Closing drawer on item click
        drawer.closeDrawers();

        // refresh toolbar menu
        invalidateOptionsMenu();
    }

    private Fragment getFragment() {
        switch (navItemIndex) {
            case 0:
                return this.fragmentA;
            case 1:
                if (fragmentB == null) {
                    fragmentB = new FragmentB();
                    fragmentB.setDatabaseHandler(this.myDatabaseHandler);
                }
                return fragmentB;
            default:
                return this.fragmentA;
        }
    }

    private void setToolbarTitle() {
        getSupportActionBar().setTitle(activityTitles[navItemIndex]);
    }

    private void setUpNavigationView() {
        //Setting Navigation View Item Selected Listener to handle the item click of the navigation menu
        navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {

            // This method will trigger on item Click of navigation menu
            @Override
            public boolean onNavigationItemSelected(MenuItem menuItem) {

                //Check to see which item was being clicked and perform appropriate action
                switch (menuItem.getItemId()) {
                    //Replacing the activity_main_header_with_item content with ContentFragment Which is our Inbox View;
                    case R.id.nav_A:
                        navItemIndex = 0;
                        CURRENT_TAG = MyConstants.TAG_FRAGMENT_A;
                        break;
                    case R.id.nav_B:
                        navItemIndex = 1;
                        CURRENT_TAG = MyConstants.TAG_FRAGMENT_B;
                        break;
                    default:
                        navItemIndex = 0;
                }

                loadHomeFragment();

                return true;
            }
        });

        ActionBarDrawerToggle actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.openDrawer, R.string.closeDrawer) {

            @Override
            public void onDrawerClosed(View drawerView) {
                // Code here will be triggered once the drawer closes as we dont want anything to happen so we leave this blank
                super.onDrawerClosed(drawerView);
            }

            @Override
            public void onDrawerOpened(View drawerView) {
                // Code here will be triggered once the drawer open as we dont want anything to happen so we leave this blank
                super.onDrawerOpened(drawerView);
            }
        };

        //Setting the actionbarToggle to drawer layout
        drawer.setDrawerListener(actionBarDrawerToggle);

        //calling sync state is necessary or else your hamburger icon wont show up
        actionBarDrawerToggle.syncState();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main_header_with_item, menu);
        return true;
    }
}
joshi737
  • 869
  • 3
  • 12
  • 26

3 Answers3

2

Thanks to Prsnth Gettin High and this post ( When switch fragment with SwipeRefreshLayout during refreshing, fragment freezes but actually still work), wrapping the SwipeRefreshLayout in a FrameLayout helps, like that:

<?xml version="1.0" encoding="utf-8"?>
<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 xmlns:android="http://schemas.android.com/apk/res/android"
                                                  android:id="@+id/swipe_refresh_layout"
                                                  android:layout_width="match_parent"
                                                  android:layout_height="match_parent">

        <ViewFlipper
            android:id="@+id/viewFlipper"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <include
                android:id="@+id/listinclude"
                layout="@layout/list"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginLeft="8dp"
                android:layout_marginRight="8dp"
                android:visibility="visible"/>

            <include
                android:id="@+id/emptyinclude"
                layout="@layout/fragment_empty"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:visibility="visible"/>
        </ViewFlipper>

    </android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>
Community
  • 1
  • 1
joshi737
  • 869
  • 3
  • 12
  • 26
0

try downcasting the fragment object to the specific fragment.

FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
Fragment fragment = getFragment();
FragmentA fragA;
FragmentB fragB;
if(fragment intanceof  FragmentA)
{
    fragA=(FragmentA) fragment;
    fragmentTransaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
    fragmentTransaction.replace(R.id.frame, fragA, FragmentA.TAG);
}
if(fragment instanceof FragmentB)
{
    fragB=(FragmentB) fragment;
     fragmentTransaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
     fragmentTransaction.replace(R.id.frame, fragB, FragmentB.TAG);
}
fragmentTransaction.commit();

And may I ask why are you using a runnable to swap fragments?

Abhiroj Panwar
  • 485
  • 9
  • 19
-1

try reusing the view. It may fix the issue.

`@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

if(view==null){view = inflater.inflate(R.layout.songs_list, container, false);}`

  • http://stackoverflow.com/questions/27057449/when-switch-fragment-with-swiperefreshlayout-during-refreshing-fragment-freezes/27073879#27073879 try this out – Prsnth Gettin High May 06 '17 at 17:49
  • negative, added ` @Override public void onPause() { if (swipeRefreshLayout != null) { swipeRefreshLayout.setRefreshing(false); swipeRefreshLayout.destroyDrawingCache(); swipeRefreshLayout.clearAnimation(); } super.onPause(); }` still the same behaviour. – joshi737 May 06 '17 at 18:08
  • 1
    try covering it in FrameLayout . – Prsnth Gettin High May 06 '17 at 18:40