Android上的导航抽屉滞后

Posted

技术标签:

【中文标题】Android上的导航抽屉滞后【英文标题】:Navigation Drawer lag on Android 【发布时间】:2014-10-21 11:45:18 【问题描述】:

我遇到了 Navigation Drawer 的问题,它太慢了,我正在寻找的解决方案是先关闭抽屉然后显示活动,但是它不起作用,当然我错过了一些东西。

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】:

你可以这样做以避免抽屉滞后,改变你的onItemClick

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

编辑: 首选方式应该是在 DrawerLayout 上设置 DrawerListener 并在 onDrawerClosed 中设置您的片段 像这样:

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

【讨论】:

感谢您的帮助,现在速度更快了,但仍然有一些延迟,这不应该发生。如何关闭抽屉然后显示活动?你能帮我解决这个问题吗? 是的,您可以尝试将延迟从 200 更改为 250 或 300。 不应该滞后这个延迟,其他地方有问题。 你也可以这样做,不耽误***.com/a/18979249/2158970 不要使用 postDelayed,它可能会创建 app.FragmentManagerImpl.checkStateLoss 异常。查看此博客 - Blog【参考方案2】:

与其在onClick中做事务,为什么不在DrawerLayout.DrawerListener的onDrawerClosed中做呢?

【讨论】:

对不起,我对 android 开发很陌生 你也可以这样做,但我更喜欢简单的解决方案 - postDelay。 我认为这种方式是正确的解决方案。使用延迟取决于设备性能。 如果您已经对自己的工作有所了解,这个答案是最好的答案。 别忘了打电话给drawerLayout.setDrawerListener(this),否则onDrawerClosed()甚至都不会被调用!!【参考方案3】:

我想我已经找到了最好的解决方案!

首先像这样进行片段交易:

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

我知道发布 Runnable 看起来没用,但是这个技巧可以避免在抽屉上的点击动画滞后,尤其是当你使用 android:background="?attr/selectableItemBackground" 用于可点击元素。

然后在片段的 onResume() 函数的末尾关闭抽屉(在本例中为“finalMenuFragment”),如下所示:

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

            mDrawerLayout.closeDrawer(mFragmentContainerView);
        
    );

我再次知道发布 Runnable 似乎很愚蠢,但它使关闭动画变得流畅。

这样,如果您想要流畅的动画,并且如果您的片段中没有很多视图,抽屉将尽可能快地关闭,它会更快地关闭并且仍然没有延迟。

如果有人对此解决方案进行了测试,我希望得到一些反馈,希望对您有所帮助!

【讨论】:

仅供参考:Handler#postDelayed(Runnable, 0) 相当于 Handler#post(Runnable)【参考方案4】:

我刚刚在 One-Line

中解决了这个问题

转到您的 Menifest 文件在您的抽屉活动中只需将 HardwareAccelerated true 以防止打开抽屉的延迟

<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】:

如果滞后,您可以请求硬件加速。 如果清单将此添加到您的活动中

 android:hardwareAccelerated="true"

如果您的 oncreate 方法添加此代码

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

现在您的活动中有硬件加速。 来源:https://developer.android.com/guide/topics/graphics/hardware-accel.html

【讨论】:

【参考方案6】:

好吧,你可以在关闭抽屉之前延迟几秒钟!

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

               mDrawerLayout.closeDrawer(containerView)
          
   , 300)`

为我工作!

【讨论】:

【参考方案7】:

试试这个。抽屉将被告知使用广播关闭。这是为了确保正在加载的活动或片段在关闭抽屉之前首先完成。

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

内部活动或片段

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

您可以将其从 onCreate 或 onAttach 更改为较早的过程,但这取决于您的应用。

【讨论】:

【参考方案8】:

我认为对片段事务使用延迟是最不稳定的解决方案。

在 onDrawerClosed 上执行这些操作会让您在关闭和片段出现之间稍有延迟。

我更喜欢在导航点击后直接进行交易,在 postDelayed 200 毫秒后关闭抽屉(在可运行检查中,以防它不为空)。 片段在抽屉开始关闭之前打开。

不仅从点击到抽屉关闭的延迟比抽屉关闭和下一个片段出现后的延迟更短,当用户从屏幕上移开手指时,用户体验更好 - 延迟不太明显。

【讨论】:

【参考方案9】:

请注意,当您的实现未能处理正确的场景时,您的 OnBackpressed() 方法将无法正常工作,正如你们中的一些人所经历的那样。

算法

    声明一个类变量以检查何时选择了导航项。如果选择了一个项目,则将 true 分配给该变量,并在导航后将该变量设置为 false

    在您的类中实现 NavigationView.OnNavigationItemSelectedListener 并在 onNavigationItemSelected() 方法中处理您的导航。请注意,为防止滞后,请在 onNavigationItemSelected() 方法中添加 DrawerListener(),最后在 onDrawerClosed() 方法中处理您的导航您的 DrawerListener()

这样,您的片段或活动将仅在抽屉成功关闭时打开,这将防止不希望的滞后。

Java 代码实现。

 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;

  


【讨论】:

以上是关于Android上的导航抽屉滞后的主要内容,如果未能解决你的问题,请参考以下文章

Android 移除导航抽屉上的阴影

Android上的自定义导航抽屉

工具栏上的 Android 导航抽屉

多个活动上的 Android 导航抽屉

导航抽屉上的按钮单击

如何通过android上的复选框控制导航抽屉项目