24

I'm having a problem with Navigation Drawer , it is too slow, the solution I'm looking for is to close the drawer first and then show the activity, but It is not working, certainly I'm missing something.

private class DrawerItemClickListener implements ListView.OnItemClickListener {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int posicao, long id) {
            setLastPosition(posicao);
            setFragmentList(lastPosition);
            layoutDrawer.closeDrawer(linearDrawer);
        }
    }

    private OnClickListener userOnClick = new OnClickListener() {
        @Override
        public void onClick(View v) {
            layoutDrawer.closeDrawer(linearDrawer);
        }
    };

    private void setFragmentList(int posicao) {

        FragmentManager fragmentManager = getSupportFragmentManager();
        Fragment fragment = new FragmentViagens();

        switch (posicao) {

            case 0:
                fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
                break;
            case 1:
                fragmentManager.beginTransaction().replace(R.id.content_frame, new FragmentPedidos()).commit();
                break;
            case 2:
                fragmentManager.beginTransaction().replace(R.id.content_frame, new FragmentClientes()).commit();
                break;

        }

        navigationAdapter.setChecked(posicao, true);
        setTitleFragments(lastPosition);
        navigationAdapter.resetarCheck();
        layoutDrawer.closeDrawer(linearDrawer);

    }
Deividas Strioga
  • 1,467
  • 16
  • 24
AND4011002849
  • 1,971
  • 8
  • 38
  • 81

9 Answers9

43

You can do It this way to avoid drawer lag, change your onItemClick:

layoutDrawer.closeDrawer(linearDrawer);
setLastPosition(posicao);
new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            setFragmentList(lastPosition);
        }
    }, 200);

Edit: prefered way should be setting DrawerListener on DrawerLayout and setting your fragment in onDrawerClosed like this:

Fragment mFragmentToSet = null;

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    // Handle navigation view item clicks here.
    switch (item.getItemId()) {
        case R.id.nav_home:
            mFragmentToSet = HomeFragment.newInstance(); 
            break;
    }

    mDrawerLayout.closeDrawer(GravityCompat.START);
    return true;
}

mDrawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
        @Override public void onDrawerSlide(View drawerView, float slideOffset) {}
        @Override public void onDrawerOpened(View drawerView) {}
        @Override public void onDrawerStateChanged(int newState) {}

        @Override
        public void onDrawerClosed(View drawerView) {
          //Set your new fragment here
          if (mFragmentToSet != null) {
            getSupportFragmentManager()
                  .beginTransaction()
                  .replace(FRAGMENT_CONTAINER_ID, mFragmentToSet)
                  .commit();
            mFragmentToSet = null;
          }
        }
    });
Mauker
  • 11,237
  • 7
  • 58
  • 76
Yuraj
  • 3,185
  • 1
  • 23
  • 42
  • Thank you for your help, It is faster now, but still getting some lag, it shouldn't be happening. How can I close the drawer and then show the activity? Can you help me on that? – AND4011002849 Aug 27 '14 at 20:06
  • Yeah, you could try changing delay from 200 to 250 or 300. – Yuraj Aug 27 '14 at 20:08
  • It should not lag with this delay, there is problem somewhere else. – Yuraj Aug 27 '14 at 20:16
  • You can do It this way too without delay http://stackoverflow.com/a/18979249/2158970 – Yuraj Aug 27 '14 at 20:20
  • 1
    dont use postDelayed, It might create app.FragmentManagerImpl.checkStateLoss exception. Checkout this blog - [Blog](http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html) – Pradip Vaghasiya May 16 '17 at 06:17
20

Rather than do transaction in onClick, why not do it in onDrawerClosed from DrawerLayout.DrawerListener?

uDevel
  • 1,891
  • 1
  • 13
  • 14
9

I think I have found the best solution for this !

First do your fragment transaction like so :

new Handler().post(new Runnable() {
                @Override
                public void run() {
                    getSupportFragmentManager()
                            .beginTransaction()
                            .setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
                            .replace(R.id.container, finalMenuFragment)
                            .commit();
                }
            });

I know it looks useless to post a Runnable but this hack avoid lag on the click animation on the drawer especially when you use android:background="?attr/selectableItemBackground" for a clickable element.

And then you close the drawer AT THE END OF THE onResume() function of your fragment (in this example it's "finalMenuFragment") like so :

 new Handler().post(new Runnable() {
        public void run() {

            mDrawerLayout.closeDrawer(mFragmentContainerView);
        }
    });

Again I know it seems stupid to post a Runnable but it makes the close animation smooth.

This way, the drawer will close as fast as it can if you want smooth animations and if your fragment has not a lot of views in it, it will close faster and still without lag.

I would like some feedback on this if somebody tested this solution, hope it helps !

Quentin G.
  • 988
  • 9
  • 15
5

I just Solved this issue in just One-Line

go to your Menifest file In your drawer Activity just put HardwareAccelerated true to prevent lag of opening drawer

<activity
        android:name=".activity.YourDrawerActivity"
        android:configChanges="keyboardHidden|orientation"
        android:screenOrientation="portrait"
        android:hardwareAccelerated="true"  //important 
        android:theme="@style/AppTheme.NoActionBar"
        android:windowSoftInputMode="stateHidden" />
Darshan
  • 63
  • 1
  • 7
4

If it lags you can request for hardware acceleration. if manifest add this to your activity

 android:hardwareAccelerated="true"

if your oncreate method add this code

getWindow().setFlags(
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);

now you have hardware acceleration in your activity. source: https://developer.android.com/guide/topics/graphics/hardware-accel.html

sajad abbasi
  • 1,988
  • 2
  • 22
  • 43
3

well, you can delay for few seconds before closing the drawer!

`new Handler().postDelayed(new Runnable() {
       @Override
          public void run() {

               mDrawerLayout.closeDrawer(containerView)
          }
   }, 300)`

Worked for me!

GouravGupta
  • 143
  • 2
  • 5
0

Try this. The drawer will be told to close using broadcast. This is to ensure, the activity or fragment being load is done 1st, before closing the drawer.

Inside MainActivity

private BroadcastReceiver xBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Drawer.isDrawerOpen(GravityCompat.START)){
            Drawer.closeDrawers();
        }
    }
};

@Override
protected void onStart() {
    super.onStart();
    IntentFilter filter = new IntentFilter();
    filter.addAction("my.drawer.listener");
    this.registerReceiver(xBroadcastReceiver, filter);
}

@Override
public void onDestroy() {
    super.onDestroy();
    this.unregisterReceiver(xBroadcastReceiver);
}

Inside activity or fragment

@Override
public void onStart() {
    super.onStart();
    Intent intent = new Intent();
    intent.setAction("my.drawer.listener");
    getActivity().sendBroadcast(intent);
}

you could change it to earlier process from onCreate or onAttach, but it depend on your app.

Azizi Musa
  • 1,009
  • 2
  • 10
  • 31
0

I think using delays for fragment transactions is the most unstable solution.

The one with doing them onDrawerClosed gives you a slight lag between closing and fragment appearing.

I prefer doing transaction straight after navigation click, close the drawer after with postDelayed 200ms (in runnable check just in case it is not null). The fragment opens just before drawer starts closing.

Not only delay from tap until drawer closing is shorter than the one after drawer closed and next fragment appearing, it is better UX as user is removing finger from screen - delay is less apparent.

Deividas Strioga
  • 1,467
  • 16
  • 24
0

Kindly note that when your implementation fails to handle the right scenarios your OnBackpressed() method will not work properly as some of you have been experiencing.

Algorithm

  1. Declare a class variable to check when a navigation item is selected. If an item is selected assign true to that variable and after your navigation, set the variable to false.

  2. Implement NavigationView.OnNavigationItemSelectedListener in your class and handle your navigations in the onNavigationItemSelected() method. Note that to prevent the lagging, add a DrawerListener() in your onNavigationItemSelected() method and finally handle your navigations in the onDrawerClosed() method of your DrawerListener().

With this, your fragment or activity will only open when the drawer has been successfully closed and this will prevent the undesired lagging.

Java Code implementation.

 public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {

   private static boolean itemSelected;

   NavigationView navigationView;

   DrawerLayout drawer;


@Override
protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);


    itemSelected =false;

    NavigationView navigationView = findViewById(R.id.your_navigation_view_id);

    DrawerLayout drawer = findViewById(R.id.your_drawer_id);


    }


@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {

    itemSelected = true;

    drawer.closeDrawer(GravityCompat.START);

    drawer.addDrawerListener(new DrawerLayout.DrawerListener() {
        @Override
        public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {

        }

        @Override
        public void onDrawerOpened(@NonNull View drawerView) {

        }

        @Override
        public void onDrawerClosed(@NonNull View drawerView) {

            if(itemSelected) {

                if (item.getItemId() == R.id.profile_menu) {

                  //Open your Activity or Fragment here
                  startActivity(new Intent(MainActivity.this, ProfileActivity.class));

                } else if (item.getItemId() == R.id.nav_services_menu) {
           
                  //Open your Activity or Fragment here
                  startActivity(new Intent(MainActivity.this, Services.class));

              }

                itemSelected = false;

            }

        }

        @Override
        public void onDrawerStateChanged(int newState) {

        }
    });

    return true;

  }

}