导航抽屉 (Navigation Drawer)

Navigation Drawer 是 Android 下一种广泛的界面布局,通过一个可弹出收回的抽屉,有效地提高了手机屏幕的利用率。
Android Studio 提供了 Navigation Drawer 模板,可以自动创建带有 Navigation Drawer 布局的应用。
虽然自动创建十分方便,但了解 Navigation Drawer 布局的构造细节,可以帮助我们理解这种布局方式,开发出高质量的应用。
本文将从零开始,一步一步创建一个Navigation Drawer 布局。

创建 Drawer Layout

Navigation Drawer 布局使用 DrawerLayout作为Activity的主布局。
其中,DrawerLayout 是Support Library里面提供的。
在 DrawerLayout 里,我们将添加两个View,一个作为主View(抽屉隐藏时显示的),一个作为抽屉。
其中,主View必须位于DrawerLayout的第一个成员。这么做跟XML的 z-排序有关,保证抽屉显示在内容的顶层。
主布局文件:res/layout/main.xml



    

    

抽屉中列表项的布局

在 main.xml 中,我们在抽屉布局里插入了一个 ListView 。下面,我们编写 ListView 中的列表项的布局。
列表项是 TextView,我们可以加入一些样式,例如: 内边距,背景色,文字色,文字尺寸。
列表项布局文件:res/layout/drawer_listview_item.xml


列表项文本

前面两章设定好了 ListView 的显示,ListView 显示哪些内容呢?
我们需要创建一个字符串数组,作为抽屉列表项目。
修改strings.xml:res/values/strings.xml



    mainActivity
    
        欢迎访问
        judymax.com
        望此教程
        有助于您
    
    打开抽屉
    关闭抽屉

Activity类

前面是对资源的设置,下面我们将进入 MainActivity.java 。
首先,我们还是要继续设置 ListView ,主要包括:
1. 列表项布局drawer_listview_item.xml如何应用到 ListView
2. 盛有列表项的字符串数组怎么装进 ListView
Activity文件:src/com/judymax/navdrawerex/mainActivity.java

package com.judymax.navdrawerex;
 
import android.os.Bundle;
import android.app.Activity;
import android.widget.ArrayAdapter;
import android.widget.ListView;
 
public class mainActivity extends Activity
{
    private String[] drawerListViewItems;
    private ListView drawerListView;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        drawerListViewItems = getResources().getStringArray(R.array.items);
 
        drawerListView = (ListView) findViewById(R.id.left_drawer);
 
        drawerListView.setAdapter(new ArrayAdapter(this,
                R.layout.drawer_listview_item, drawerListViewItems)); 
    }
}

有了这些代码,运行项目,就能够通过手势,从左向右滑,调出抽屉了。
下一节中,将再提供一种方式,即点击左上角的应用图标,调出抽屉。

应用图标调出抽屉

点击应用图标调出抽屉,这一功能是通过ActionBarDrawerToggle来实现的。
首先,创建ActionBarDrawerToggle:

actionBarDrawerToggle = new ActionBarDrawerToggle(
        this,                   // host activity
        drawerLayout,           // DrawerLayout
        R.drawable.ic_drawer,   // Up caret
        R.string.drawer_open,   // open description
        R.string.drawer_close   // close description
        );

其中,drawerLayout是对android.support.v4.widget.DrawerLayout 的引用:

drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);

ActionBarDrawerToggle创建好后,要添加到DrawerLayout里:

DrawerLayout.setDrawerListener(ActionBarDrawerToggle);

因为 ActionBarDrawerToggle 实现了 DrawerLayout.DrawerListener,所以我们可以将 ActionBarDrawerToggle传给 setDrawerListner。
之后,在 ActionBar 中显示出Up caret:

getActionBar().setDisplayHomeAsUpEnabled(true);

最后,重载两个函数:

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

@Override
public boolean onOptionsItemSelected(MenuItem item){
    if(actionBarDrawerToggle.onOptionsItemSelected(item)){
        return true;
    }
    return super.onOptionsItemSelected(item);
}

显示标准的抽屉图标

上面只是创建了图标,但是还没有显示出来,要显示图标:

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

抽屉项点击

为了响应点击事件,我们需要做两件事:
首先,在 onCreate() 里调用:

drawerListView.setOnItemClickListener(new DrawerItemClickListener());

之后,我们要以私有的内部类形式创建DrawerItemClickListener ,并在其中实现 ListView.OnItemClickListener方法,实现点击抽屉项,就弹出一条Toast,并关闭抽屉:

private class DrawerItemClickListener implements ListView.OnItemClickListener {
        @Override
        public void onItemClick(AdapterView parent, View view, int position, long id) {
            Toast.makeText(MainActivity.this, ((TextView)view).getText(), Toast.LENGTH_LONG).show();
            drawerLayout.closeDrawer(drawerListView);
        }
    }

最终程序

最终的 mainActivity.java 程序如下:

package com.judymax.navdrawerex;

import com.judymax.navdrawerex.R;
import android.app.Activity;
import android.os.Bundle;
import android.content.res.Configuration;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import android.widget.TextView;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;

public class mainActivity extends Activity
{
    private String[] drawerListViewItems;
    private ListView drawerListView;
    private DrawerLayout drawerLayout;
    private ActionBarDrawerToggle actionBarDrawerToggle;

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

        drawerListViewItems = getResources().getStringArray(R.array.items);
        drawerListView = (ListView)findViewById(R.id.left_drawer);

        drawerListView.setAdapter(new ArrayAdapter(this, R.layout.drawer_listview_item, drawerListViewItems));
        drawerListView.setOnItemClickListener(new DrawerItemClickListener());

        drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);

        actionBarDrawerToggle = new ActionBarDrawerToggle(
                this,                   // host activity
                drawerLayout,           // DrawerLayout
                R.drawable.ic_drawer,   // Up caret
                R.string.drawer_open,   // open description
                R.string.drawer_close   // close description
                );
        
        drawerLayout.setDrawerShadow(R.drawable.drawer_shadow,GravityCompat.START);
    }

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

    @Override
    public boolean onOptionsItemSelected(MenuItem item){
        if(actionBarDrawerToggle.onOptionsItemSelected(item)){
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState){
        super.onPostCreate(savedInstanceState);
        actionBarDrawerToggle.syncState();
    }

    private class DrawerItemClickListener implements ListView.OnItemClickListener{
        @Override
        public void onItemClick(AdapterView parent, View view, int position, long id){
            Toast.makeText(mainActivity.this, ((TextView)view).getText(), Toast.LENGTH_LONG).show();
            drawerLayout.closeDrawer(drawerListView);
        }
    }
}

总结

最终的效果图:

result

Navigation Drawer布局容易让人迷惑的地方就在于actionBarDrawerToggle。mainActivity.java中相当一部分的内容,都是在将它融入到应用框架中。可见,为了实现这种方便的功能,开发者也是蛮拼的。
一旦接受了这种设定,Navigation Drawer 就很好理解了。

参考

http://hmkcode.com/android-creating-a-navigation-drawer/