46

我正在开发一个使用抽屉式导航模式(With DrawerLayout)的应用程序。

每次单击抽屉的项目,都会替换主容器中的片段。

但是,我不确定何时是进行片段交易的合适时间?抽屉什么时候开始关闭?还是关闭后?

在 google 的文档示例中,您可以看到他们在单击项目后立即进行交易,然后关闭抽屉。
结果,抽屉看起来迟钝且不平滑,而且看起来很糟糕(我的应用程序中也发生了这种情况)。

另一方面,在GmailGoogle Drive应用程序中,他们似乎在抽屉关闭后进行交易(我对吗?)。
结果,抽屉并不滞后并且非常平滑,但至少需要大约 1 秒(抽屉关闭所需的时间)才能看到下一个片段。

似乎在立即进行片段事务时抽屉不可能平滑。

你怎么看?

提前致谢!

4

7 回答 7

25

是的,完全同意,执行片段(带视图)事务会导致布局传递,这会导致视图上的动画动画,引用DrawerLayout 文档

DrawerLayout.DrawerListener 可用于监视抽屉视图的状态和运动。避免在动画过程中执行昂贵的操作,例如布局,因为它会导致卡顿;尝试在 STATE_IDLE 状态期间执行昂贵的操作。

因此,请在抽屉关闭后执行您的片段事务,或者有人修补支持库以某种方式解决该问题:)

于 2013-07-05T15:10:51.670 回答
25

另一种解决方案是在关闭抽屉后创建Handler并发布延迟Runnable,如下所示:https ://stackoverflow.com/a/18483633/769501 。这种方法的好处是,您的片段将比您等待时更快地被替换DrawerListener#onDrawerClosed(),但当然,任意延迟并不能 100% 保证抽屉动画会及时完成。

也就是说,我使用了 200 毫秒的延迟,效果非常好。

private class DrawerItemClickListener implements OnItemClickListener {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
        drawerLayout.closeDrawer(drawerList);
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                switchFragments(position); // your fragment transactions go here
            }
        }, 200);
    }
}
于 2014-01-21T21:52:10.977 回答
15

这是我为实现类似于 Gmail 应用程序的流畅交易动画所做的:

活动抽屉.xml

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <!-- The main content view -->
    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <!-- The navigation drawer -->
    <ListView 
    android:id="@+id/left_drawer"
        android:layout_width="280dp"
        android:layout_height="match_parent"
        android:layout_gravity="left"
        android:choiceMode="singleChoice" />

</android.support.v4.widget.DrawerLayout>

DrawerActivity.java

private Fragment mContentFragment;
private Fragment mNextContentFragment;
private boolean mChangeContentFragment = false;

private Handler mHandler = new Handler();

...

@Override
public void onCreate(Bundle savedInstanceState) {
    ...

    mDrawerLayout.setDrawerListener(new DrawerListener());

    mDrawerList.setOnItemClickListener(new DrawerItemClickListener());

    ...
}

....

private class DrawerItemClickListener implements ListView.OnItemClickListener {

    @Override
    public void onItemClick(AdapterView parent, View view, int position, long id) {
        getSupportFragmentManager().beginTransaction().remove(mContentFragment).commit();

        switch (position) {
            case 0:
                mNextContentFragment = new Fragment1();
                break;

            case 1:
                mNextContentFragment = new Fragment2();
                break;

            case 2:
                mNextContentFragment = new Fragment3();
                break;
        }

        mChangeContentFragment = true;

        mDrawerList.setItemChecked(position, true);

        mHandler.postDelayed(new Runnable() {

            @Override
            public void run() {
                mDrawerLayout.closeDrawer(mDrawerList);
            }           
        }, 150);
    }
}

private class DrawerListener implements android.support.v4.widget.DrawerLayout.DrawerListener {

    @Override
    public void onDrawerClosed(View view) {
        if (mChangeContentFragment) {
             getSupportFragmentManager().beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).replace(R.id.content_frame, mNextContentFragment).commit();

             mContentFragment = mNextContentFragment;           
             mNextContentFragment = null;

             mChangeContentFragment = false;
         }
     }
 }

希望对你有帮助!:-)

于 2013-09-24T10:35:34.993 回答
5

我知道这个问题很老,但我遇到了同样的问题,我想我会发布我的解决方案,因为我认为它是一个比添加硬编码延迟时间更好的实现。我所做的是onDrawerClosed在执行任务之前使用该功能来验证抽屉是否已关闭。

//on button click...
private void displayView(int position) {
    switch (position) {
    //if item 1 is selected, update a global variable `"int itemPosition"` to be 1
    case 1:
        itemPosition = 1;
        //();
        break;
    default:
        break;
    }

    // update selected item and title, then close the drawer
    mDrawerList.setItemChecked(position, true);
    mDrawerList.setSelection(position);
    mDrawerLayout.closeDrawer(mDrawerList); //close drawer
}

然后在 中onDrawerClosed,打开相应的活动。

public void onDrawerClosed(View view) {
    getSupportActionBar().setTitle(mTitle);
    // calling onPrepareOptionsMenu() to show action bar icons
    supportInvalidateOptionsMenu();
    if (itemPosition == 1) {
        Intent intent = new Intent(BaseActivity.this, SecondActivity.class);
        startActivity(intent);
    }
}
于 2014-08-20T18:05:01.250 回答
2

只需在处理程序中编写代码并延迟 200 毫秒。

 new Handler().postDelayed(new Runnable() {
  @Override
   public void run() {
       openSelectionDrawerItem(position);          
   }
 }, 200);
于 2015-11-06T18:16:33.273 回答
1

而不是延迟您的项目点击,这可能会使您的应用程序感觉很慢。我只会延迟关闭 mDrawerLayout。我不会使用DrawerLayout.OnDrawerListener onClose(...)任何一个,因为这些回调调用起来太慢了。

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        mDrawerLayout.closeDrawer(GravityCompat.START);
    }
}, 200);
于 2016-02-09T07:51:41.863 回答
0

如果您希望它顺利且没有任何延迟,请让抽屉保持打开状态,然后在返回时将其关闭(在 onRestart() 方法中)。

@Override
protected void onRestart() {
    // TODO Auto-generated method stub
    super.onRestart();
    mDrawerLayout.closeDrawer(mDrawerList);     
}

返回时的副作用是(快速)动画,但这可能是可以接受的。

于 2015-01-15T10:34:48.237 回答