7

Using a DrawerLayout with a NavigationView and a FrameLayout I want to switch Fragments. That works great. However, if I switch too quickly, then the Fragments overlaps...

It is like executePendingTransactions() does not work.

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <include layout="@layout/toolbar" />

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

    </LinearLayout>

    <android.support.design.widget.NavigationView
        android:id="@+id/navigationView"
        android:layout_width="@240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:choiceMode="singleChoice"
        android:divider="@color/transparent"
        android:dividerHeight="0dp"
        app:menu="@menu/navigationdrawer" />

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

If I switch Fragments (too) rapidly (manually or by code with a 750ms delay on my Nexus 5), I get both Fragments to overlap, with the second Fragment having the touch enabled BUT the first Fragment being on top...

The first Fragment contains an ImageView and TextViews. The second Fragment contains a TabLayout and a ViewPager (if that could have anything to day with my issue). Yes I'm using AppCompat 22.2.0 and Design 22.2.0 libraries.

If I set a background color to both, then I can only see the first Fragment, and it never changes.

I tried popBackStackImmediate(), executePendingTransactions(), remove(fragment), android:fitsSystemWindows="true", Android: fragments overlapping issue, delaying, and other things, without success.

@Override
protected void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // ...

    mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(final MenuItem menuItem) {
            navigationDrawer(menuItem.getItemId());
            return true;
        }
    });

    if (savedInstanceState == null) {
        final MenuItem menuItem = mNavigationView.getMenu().getItem(0);
        if (menuItem != null) {
            navigationDrawer(menuItem.getItemId());
        }
    }
}

private void navigationDrawer(final int itemId) {
    final Fragment fragment = getFragment(itemId);
    getSupportFragmentManager().beginTrasaction()
        .replace(R.id.frameLayout, fragment)
        .addToBackStack(null)
        .commit();
    mNavigationView.getMenu().findItem(itemId).setChecked(true);
    mDrawerLayout.closeDrawer(mNavigationView);
    supportInvalidateOptionsMenu();
}

@Override
public boolean onOptionsItemSelected(final MenuItem item) {
    switch (item.getItemId()) {
        case R.id.menu_first:
        case R.id.menu_second:
        case R.id.menu_third:
            navigationDrawer(item.getItemId());
            return true;
    }
    return super.onOptionsItemSelected(item);
}

EDIT

In my onCreate() I was doing this:

if (savedInstanceState == null) {
    final MenuItem menuItem = mNavigationView.getMenu().getItem(0);
    if (menuItem != null) {
        navigationDrawer(menuItem.getItemId());
    }
}

Which turns out to make a call too rapidly. Removing this code solved my issue (temporally, see below).

I still don't know why executePendingTransactions() did not prevent such weird issue...

EDIT 2

I thought about keeping a boolean (init to false) to keep track of when a Fragment transaction takes place. I set it to true in my navigationDrawer() and to false in Fragment.onResume(). Still no go...

So: still having problem on my MainFragment that loads an image using Picasso and switching too rapidly (800ms) to another Fragment: they still overlap...

shkschneider
  • 17,833
  • 13
  • 59
  • 112
  • can i see your some more code ..so that i can help you .. – Moinkhan Jun 10 '15 at 10:22
  • @Moinkhan What part of it? I believe I posted the relative part. It's a basic DrawerLayout/NavigationView implementation. – shkschneider Jun 10 '15 at 12:16
  • the code you have provided was perfect. that's why i thought the problem can be in code, which is not provided over here .... – Moinkhan Jun 10 '15 at 12:23
  • @Moinkhan added some code – shkschneider Jun 10 '15 at 12:35
  • Seems the click it's running BEFORE the onCreate, have you debugged? – Sulfkain Jun 22 '15 at 14:02
  • How could it be? I can, with my own fingers, provoke this bug. So the UI is built and it went through `onCreate`(). – shkschneider Jun 22 '15 at 14:11
  • I think your problem is due to your navigationDrawer() method getting called from the onCreate() method(when savedInstance is null) and also the listener for the NavigationView. At some point you may add more fragments than you want(which is why removing the code from onCreate seems to work ). Also the navigationDrawer() method should do some checks and not blindly add fragments no matter what, for example checking the fragment container for already present fragments(for the initial onCreate call), not adding fragments when the user selects the same menu option... – user Jun 23 '15 at 10:57
  • I did what I think was that, and answered my own question. Yet I managed to reproduce the bug, hence the bounty. To be sure, could you propose an answer with what you wrote and I'll try it the way you see it. Thanks. – shkschneider Jun 23 '15 at 12:07

2 Answers2

1

You may try this ... It will delay your frequent click ...

private final Handler mDrawerActionHandler = new Handler();
private static final long DRAWER_CLOSE_DELAY_MS = 250;

mDrawerActionHandler.postDelayed(new Runnable() {
      @Override
      public void run() {
        // your navigation code goes here
      }
}, DRAWER_CLOSE_DELAY_MS);
Moinkhan
  • 12,732
  • 5
  • 48
  • 65
  • Thought of that and did it already: did not work (plus that does not explain my issue). Thanks for the input anyway – shkschneider Jun 10 '15 at 10:07
0

In my onCreate() I was doing this:

    if (savedInstanceState == null) {
        final MenuItem menuItem = mNavigationView.getMenu().getItem(0);
        if (menuItem != null) {
            navigationDrawer(menuItem.getItemId());
        }
    }

Which turns out to make a call too rapidly. Removing this code solved my issue.

I still don't know:

why executePendingTransactions() did not prevent such weird issue...

EDIT

Still having problem on my MainFragment that loads an image using Picasso and switching too rapidly (800ms) to another one: they still overlap...

EDIT 2

So I now use a boolean to mark if my Activity is transitioning a Fragment or not, with a minimal timer of 1s second between switches.

In my Fragments onViewCreated():

if (view != null) {
    view.post(new Runnable() {
        @Override
        public void run() {
            // Activity might be null
            getActivity().setTransitioning(false);
        }
    });
}

And in my navigationDrawer():

if (isTransitioning()) {
    // Switching Fragments too fast
    return false;
}
shkschneider
  • 17,833
  • 13
  • 59
  • 112
  • Alternative way would be to check if the `Activity` passed `onResume()` at top of my `navigationDrawer()` method. Works even with faulty code now. – shkschneider Jun 11 '15 at 12:48
  • And that did not work: I set a `boolean` for as to when a transaction was in place, and made my `Fragment`s set that to false in `onResume()`. Still not working... – shkschneider Jun 17 '15 at 09:57