Navigation Drawer lag on Android

2020-01-30 07:00发布

问题:

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);

    }

回答1:

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;
          }
        }
    });


回答2:

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



回答3:

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 !



回答4:

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" />


回答5:

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



回答6:

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.



回答7:

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.



回答8:

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!