优化抽屉和活动发射速度

yar*_*ian 52 android navigation-drawer

我正在使用谷歌DrawerLayout.

单击某个项目后,抽屉将平稳关闭并Activity启动.将这些活动转变为Fragments 不是一种选择.因此,启动活动然后关闭抽屉也不是一种选择.关闭抽屉并同时启动活动将使关闭动画口吃.

鉴于我希望首先顺利关闭它,然后启动活动,我遇到了用户点击抽屉项目和他们看到他们想要去的活动之间的延迟问题.

这就是每个项目的点击监听器的样子.

final View.OnClickListener mainItemClickListener = new View.OnClickListener() {
    @Override
    public void onClick(final View v) {
        mViewToLaunch = v;
        mDrawerLayout.closeDrawers();
    }
};
Run Code Online (Sandbox Code Playgroud)

我的活动也是DrawerListener,它的onDrawerClosed方法如下:

@Override
public synchronized void onDrawerClosed(final View view) {
    if (mViewToLaunch != null) {
        onDrawerItemSelection(mViewToLaunch);
        mViewToLaunch = null;
    }
}
Run Code Online (Sandbox Code Playgroud)

onDrawerItemSelection 刚刚启动五项活动之一.

我就没事onPauseDrawerActivity.

我正在检测它,从onClick调用的那一刻开始平均需要500-650ms,直到onDrawerClosed结束.

一旦抽屉关闭,在相应的活动开始之前,存在明显的滞后.

我意识到发生了一些事情:

  • 结束动画发生,就在那里几毫秒(比方说300).

  • 然后,抽屉在视觉上关闭和听众被解雇之间可能存在一些延迟.我试图通过查看DrawerLayout来源确切地弄清楚这有多少发生但尚未弄明白.

  • 然后是启动的活动执行其启动生命周期方法所需的时间,包括onResume.我还没有对此进行检测,但估计大约200-300毫秒.

这似乎是一个问题,走错路是非常昂贵的,所以我想确保我完全理解它.

一个解决方案是跳过关闭动画,但我希望保持它.

如何尽可能减少过渡时间?

小智 42

根据文件,

避免在动画期间执行昂贵的操作,例如布局,因为它可能导致口吃; 尝试在STATE_IDLE状态期间执行昂贵的操作.

Handler您可以覆盖(实现)的onDrawerStateChanged方法,而不是使用和硬编码时间延迟,这样您就可以在抽屉完全关闭时执行昂贵的操作.ActionBarDrawerToggleDrawerLayout.DrawerListener

在MainActivity中,

private class SmoothActionBarDrawerToggle extends ActionBarDrawerToggle {

    private Runnable runnable;

    public SmoothActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, Toolbar toolbar, int openDrawerContentDescRes, int closeDrawerContentDescRes) {
        super(activity, drawerLayout, toolbar, openDrawerContentDescRes, closeDrawerContentDescRes);
    }

    @Override
    public void onDrawerOpened(View drawerView) {
        super.onDrawerOpened(drawerView);
        invalidateOptionsMenu();
    }
    @Override
    public void onDrawerClosed(View view) {
        super.onDrawerClosed(view);
        invalidateOptionsMenu();
    }
    @Override
    public void onDrawerStateChanged(int newState) {
        super.onDrawerStateChanged(newState);
        if (runnable != null && newState == DrawerLayout.STATE_IDLE) {
            runnable.run();
            runnable = null;
        }
    }

    public void runWhenIdle(Runnable runnable) {
        this.runnable = runnable;
    }
}
Run Code Online (Sandbox Code Playgroud)

设置DrawerListeneronCreate:

mDrawerToggle = new SmoothActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.open, R.string.close);
mDrawerLayout.setDrawerListener(mDrawerToggle);
Run Code Online (Sandbox Code Playgroud)

最后,

private void selectItem(int position) {
    switch (position) {
        case DRAWER_ITEM_SETTINGS: {
            mDrawerToggle.runWhenIdle(new Runnable() {
                @Override
                public void run() {
                    Intent intent = new Intent(MainActivity.this, SettingsActivity.class);
                    startActivity(intent);
                }
            });
            mDrawerLayout.closeDrawers();
            break;
        }
        case DRAWER_ITEM_HELP: {
            mDrawerToggle.runWhenIdle(new Runnable() {
                @Override
                public void run() {
                    Intent intent = new Intent(MainActivity.this, HelpActivity.class);
                    startActivity(intent);
                }
            });
            mDrawerLayout.closeDrawers();
            break;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我相信这是更好的答案.它指的是文档,并根据文档中的建议实现了一种方法.+1 (5认同)
  • 但是在使用它时,您应该立即运行第一个片段以处理First Activity启动 (2认同)

Shr*_*jan 27

我在DrawerLayout面临同样的问题.

我对此进行了研究,然后找到了一个很好的解决方案.

我在做什么.....

如果您为DrawerLayout引用Android Sample应用程序,请检查selectItem(position)的代码;

在此函数中基于位置选择片段被调用.我已经根据我的需要使用下面的代码修改它,并且没有动画关闭口吃.

private void selectItem(final int position) {
    //Toast.makeText(getApplicationContext(), "Clicked", Toast.LENGTH_SHORT).show();
    mDrawerLayout.closeDrawer(drawerMain);
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            Fragment fragment = new TimelineFragment(UserTimeLineActivity.this);
            Bundle args = new Bundle();
            args.putInt(TimelineFragment.ARG_PLANET_NUMBER, position);
            fragment.setArguments(args);

            FragmentManager fragmentManager = getSupportFragmentManager();
            fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();

            // update selected item and title, then close the drawer
            mCategoryDrawerList.setItemChecked(position, true);

            setTitle("TimeLine: " + mCategolyTitles[position]);
        }
    }, 200);


    // update the main content by replacing fragments


}
Run Code Online (Sandbox Code Playgroud)

我在这里首先关闭DrawerLayout.这需要大约250毫秒.然后我的处理程序将调用该片段.哪个工作顺利,并根据要求.

希望它对你也有帮助.

享受编码...... :)


yar*_*ian 19

所以我似乎用合理的解决方案解决了这个问题.

可感知潜伏期的最大来源是抽屉在视觉上关闭和onDrawerClosed被叫时之间的延迟.我通过在一个Runnable私人网站Handler上发布一个指定的延迟来启动预期的活动来解决这个问题.选择该延迟以与抽屉关闭相对应.

我试图onDrawerSlide在80%的进展后进行发射,但这有两个问题.首先是它结结巴巴.第二个问题是,如果你将百分比增加到90%或95%,由于动画的性质增加而不会被调用的可能性增加 - 然后你不得不回头onDrawerClosed,这就失去了目的.

这种解决方案有可能出现断断续续的问题,特别是在较旧的手机上,但仅仅通过将延迟增加到足够高就可以将可能性降低到0.我认为250ms是口吃和延迟之间的合理平衡.

代码的相关部分如下所示:

public class DrawerActivity extends SherlockFragmentActivity {
    private final Handler mDrawerHandler = new Handler();

    private void scheduleLaunchAndCloseDrawer(final View v) {
        // Clears any previously posted runnables, for double clicks
        mDrawerHandler.removeCallbacksAndMessages(null); 

        mDrawerHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                onDrawerItemSelection(v);
            }
        }, 250);
        // The millisecond delay is arbitrary and was arrived at through trial and error

        mDrawerLayout.closeDrawer();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 你什么意思?我在你之前发布了这个答案.它与您的非常相似,但每次都不会创建一个处理程序,这样您就可以取消之前的计划启动. (2认同)
  • 这对我有很大帮助. (2认同)
  • 小心在onDrawerItemSelection(v)中提交片段事务,因为当用户选择抽屉项目之后,但在抽屉关闭之前,当应用程序停止时,它将导致IllegalStateException. (2认同)

War*_*zit 8

谷歌IOsched 2015运行非常顺利(除了设置),原因是他们如何实现抽屉以及他们如何启动东西.

首先他们使用处理程序启动延迟:

        // launch the target Activity after a short delay, to allow the close animation to play
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                goToNavDrawerItem(itemId);
            }
        }, NAVDRAWER_LAUNCH_DELAY);
Run Code Online (Sandbox Code Playgroud)

延迟是:

private static final int NAVDRAWER_LAUNCH_DELAY = 250;
Run Code Online (Sandbox Code Playgroud)

他们做的另一件事是从活动onCreate()中使用以下代码启动的活动中删除动画:

overridePendingTransition(0, 0);
Run Code Online (Sandbox Code Playgroud)

要查看源代码,请转到git.