Fragments overlapping with DrawerLayout/NavigationView
Asked Answered
P

2

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...

Paver answered 10/6, 2015 at 9:42 Comment(8)
can i see your some more code ..so that i can help you ..Concerning
@Concerning What part of it? I believe I posted the relative part. It's a basic DrawerLayout/NavigationView implementation.Paver
the code you have provided was perfect. that's why i thought the problem can be in code, which is not provided over here ....Concerning
@Concerning added some codePaver
Seems the click it's running BEFORE the onCreate, have you debugged?Homophonic
How could it be? I can, with my own fingers, provoke this bug. So the UI is built and it went through onCreate().Paver
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...Produce
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.Paver
P
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;
}
Paver answered 11/6, 2015 at 12:43 Comment(2)
Alternative way would be to check if the Activity passed onResume() at top of my navigationDrawer() method. Works even with faulty code now.Paver
And that did not work: I set a boolean for as to when a transaction was in place, and made my Fragments set that to false in onResume(). Still not working...Paver
C
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);
Concerning answered 10/6, 2015 at 9:52 Comment(1)
Thought of that and did it already: did not work (plus that does not explain my issue). Thanks for the input anywayPaver
P
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;
}
Paver answered 11/6, 2015 at 12:43 Comment(2)
Alternative way would be to check if the Activity passed onResume() at top of my navigationDrawer() method. Works even with faulty code now.Paver
And that did not work: I set a boolean for as to when a transaction was in place, and made my Fragments set that to false in onResume(). Still not working...Paver

© 2022 - 2024 — McMap. All rights reserved.