Creating a Navigation Drawer 创建导航抽屉

Creating a Navigation Drawer 创建导航抽屉

原文链接:https://developer.android.com/training/implementing-navigation/nav-drawer.html

导航抽屉(Navigation Drawer)是位于屏幕左边缘用于展示程序主导航选项的一个面板。大部分时间,它是被隐藏的。当手指从屏幕的左边缘滑过时,或在应用程序的顶层,当用户点击操作栏(Action Bar)中的程序图标时,它会被显示出来。

在你决定在你的程序中使用 Navigation Drawer 之前,你应当了解其用例和设计原则,详细内容请查阅 Navigation Drawer 设计指南

Create a Drawer Layout

为了添加一个 Navigation Drawer,在声明用户界面时,使用 DrawerLayout 作为布局的根视图。在 DrawerLayout 中添加一个视图用于存放屏幕的主要内容(当抽屉被隐藏时,你的主要布局),添加另一个视图用于存放 Navigation Drawer 的内容。

举个例子,下面的布局使用了一个 DrawerLayout,它包含了两个子视图:一个 FrameLayout 用来存放主要内容和一个用于存放 Navigation Drawer 的 ListView。

<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">
    
    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    
    <ListView android:id="@+id/left_drawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp"
        android:background="#111"/>
android.support.v4.widget.DrawerLayout>

这个布局展示了一些重要的布局特点:

  • 主要内容的视图(例子中的 FrameLayout)必须是 DrawerLayout 的第一个子视图,因为 XML 依据 Z 轴方向进行排序,抽屉视图必须在内容之上。
  • 主要内容的视图应该被设置为匹配父视图的宽度和高度,因为当 Navigation Drawer 被隐藏后,它代表了整个 UI。
  • 抽屉视图(例子中的 ListView)必须通过设置 android:layout_gravity 属性来指定它的水平重心。为了支持 right-to-left(RTL) 语言,其值被设定为 start,而不是 left(因此当布局是 RTL 的,抽屉会从右边出现)。
  • 抽屉视图以 dp 为单位指定了宽度,高度设置为匹配父视图。抽屉视图的宽度不应超过 320dp,这样用户就能够始终看到主要内容的一部分。

Initialize the Drawer List

在你的 activity 中,一个你首先要做的事是初始化导航抽屉列表中的每一个选项。你如何来做取决于你的程序的内容,通常一个导航抽屉由一个 ListView 构成,因此这个列表应当有一个适配器(Adapter)来填充( 例如 ArrayAdapter 或 SimpleCursorAdapter)。下面是一个例子:

public class MainActivity extends Activity {
    private String[] mPlanetTitles;
    private DrawerLayout mDrawerLayout;
    private ListView mDrawerList;
    ...

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mPlanetTitles = getResources().getStringArray(R.array.planets_array);
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerList = (ListView) findViewById(R.id.left_drawer);

        // Set the adapter for the list view
        mDrawerList.setAdapter(new ArrayAdapter(this,
                R.layout.drawer_list_item, mPlanetTitles));
        // Set the list's click listener
        mDrawerList.setOnItemClickListener(new DrawerItemClickListener());

        ...
    }
}

Handle Navigation Click Events

当用户选中了抽屉列表中的一个选项时,系统会调用 OnItemClickListeneronItemClick()
onItemClick() 中你应该做什么取决于你的程序逻辑。在下面的例子中,选中列表中的每一个选项都会向主要内容视图(FrameLayout,元素 ID 是 R.id.content_frame)插入一个不同的 Fragment。

private class DrawerItemClickListener implements ListView.OnItemClickListener {
    @Override
    public void onItemClick(AdapterView parent, View view, int position, long id) {
        selectItem(position);
    }
}

/** Swaps fragments in the main content view */
private void selectItem(int position) {
    // Create a new fragment and specify the planet to show based on position
    Fragment fragment = new PlanetFragment();
    Bundle args = new Bundle();
    args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
    fragment.setArguments(args);

    // Insert the fragment by replacing any existing fragment
    FragmentManager fragmentManager = getFragmentManager();
    fragmentManager.beginTransaction()
                   .replace(R.id.content_frame, fragment)
                   .commit();

    // Highlight the selected item, update the title, and close the drawer
    mDrawerList.setItemChecked(position, true);
    setTitle(mPlanetTitles[position]);
    mDrawerLayout.closeDrawer(mDrawerList);
}

@Override
public void setTitle(CharSequence title) {
    mTitle = title;
    getActionBar().setTitle(mTitle);
}

Listen for Open and Close Events

想要鉴定抽屉的打开和关闭事件,我们需要调用 setDrawerListener() 并传入一个实现了 DrawerLayout.DrawerListener 接口的对象。这个接口提供了抽屉事件的回调,例如 onDrawerOpened()onDrawerClosed
当你的 activity 包含 action bar 时,你可以继承 ActionBarDrawerToggle 类代替实现 DrawerLayout.DrawerListener 接口。ActionBarDrawerToggle 实现了 DrawerLayout.DrawerListener 接口,因此你仍然可以重写那些回调函数,这样可以促成 action bar 图标与导航抽屉之间正确的交互行为。
正如导航抽屉设计指南中讨论的,当抽屉可见时,你应当修改 action bar 的内容,例如修改标题和移除功能选项,标题和功能选项都是和主要内容相关联的。下面的代码展示了在一个 ActionBarDrawerToggle 类对象中重写 DrawerLayout.DrawerListener 的回调函数。

public class MainActivity extends Activity {
    private DrawerLayout mDrawerLayout;
    private ActionBarDrawerToggle mDrawerToggle;
    private CharSequence mDrawerTitle;
    private CharSequence mTitle;
    ...

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ...

        mTitle = mDrawerTitle = getTitle();
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
                R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) {

            /** Called when a drawer has settled in a completely closed state. */
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                getActionBar().setTitle(mTitle);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
            }

            /** Called when a drawer has settled in a completely open state. */
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                getActionBar().setTitle(mDrawerTitle);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
            }
        };

        // Set the drawer toggle as the DrawerListener
        mDrawerLayout.setDrawerListener(mDrawerToggle);
    }

    /* Called whenever we call invalidateOptionsMenu() */
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        // If the nav drawer is open, hide action items related to the content view
        boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
        menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
        return super.onPrepareOptionsMenu(menu);
    }
}

Open and Close with the App Icon

用户可以通过从左边缘或向左边缘的滑动手势打开或者关闭导航抽屉。但是如果你正在使用 action bar,你应该允许用户通过触摸程序图标的方式打开或关闭导航抽屉。当抽屉存在时,程序图标应该变为一个特殊的图标。你可以使用 ActionBarDrawerToggle 来实现这些行为。
创建一个 ActionBarDrawerToggle 对象,你需要如下的参数( android.support.v4.app.ActionBarDrawerToggle):
- 托管抽屉的 Activity 对象
- DrawerLayout 对象
- 用于指示抽屉显示的绘制资源(drawable resource)
- 字符串资源,用来形容“打开抽屉”的动作
- 字符串资源,用来形容“关闭抽屉”的动作
对于android.support.v7.app.ActionBarDrawerToggle,我们将不再需要第三个参数。

public class MainActivity extends Activity {
    private DrawerLayout mDrawerLayout;
    private ActionBarDrawerToggle mDrawerToggle;
    ...

    public void onCreate(Bundle savedInstanceState) {
        ...

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerToggle = new ActionBarDrawerToggle(
                this,                  /* host Activity */
                mDrawerLayout,         /* DrawerLayout object */
                R.drawable.ic_drawer,  /* nav drawer icon to replace 'Up' caret */
                R.string.drawer_open,  /* "open drawer" description */
                R.string.drawer_close  /* "close drawer" description */
                ) {

            /** Called when a drawer has settled in a completely closed state. */
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                getActionBar().setTitle(mTitle);
            }

            /** Called when a drawer has settled in a completely open state. */
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                getActionBar().setTitle(mDrawerTitle);
            }
        };

        // Set the drawer toggle as the DrawerListener
        mDrawerLayout.setDrawerListener(mDrawerToggle);

        getActionBar().setDisplayHomeAsUpEnabled(true);
        getActionBar().setHomeButtonEnabled(true);
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        mDrawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        mDrawerToggle.onConfigurationChanged(newConfig);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Pass the event to ActionBarDrawerToggle, if it returns
        // true, then it has handled the app icon touch event
        if (mDrawerToggle.onOptionsItemSelected(item)) {
          return true;
        }
        // Handle your other action bar items...

        return super.onOptionsItemSelected(item);
    }

    ...
}

完整的例子:下载地址

你可能感兴趣的:(Android)