相信Sliding Menu不少人都用過,在Android和iOS的app中,愈來愈多的開發者都會把本身的菜單界面放在一個列表裏,而後讓用戶經過向右(或者向左)滑動的操做看到應用全部的功能。Google官方的應用也基本都選擇了這種交互方式,不一樣的是,Google使用的是Navigation Drawer,而咱們大部分用的仍是Sliding Menu。html
你們對Sliding Menu這個開源項目可能已經很熟悉了,可是Navigation Drawer咱們有些童鞋可能瞭解的還比較少,它是Google I/O 2013剛推出不久的一個在support v4包裏面的一個控件,下面我就經過一個demo跟你們介紹一下Navigation Drawer的使用方法。http://safe.ijiami.cn/android
這個demo是google官方的,你們能夠到這裏下載一下:http://developer.android.com/training/implementing-navigation/nav-drawer.html,我下面寫的代碼說明也基本就是翻譯了一下這個教程,英語比較好的童鞋建議仍是直接看官方的吧。web
建立一個抽屜app
導航抽屜是一個位於屏幕左側邊緣用來顯示應用程序導航項的一個面板。導航抽屜在大部分時間是不顯示的,但兩種狀況下會進行顯示:一是發生從屏幕左側邊緣向右滑的手勢,二是點擊了工具欄中應用圖標。導航抽屜在SupportLibrary 中提供支持,在使用導航抽屜時,須要符合導航抽屜設計原則(NavigationDrawer),看看你是否有必要建立導航抽屜 。ide
建立抽屜佈局工具
若是你要添加一個導航抽屜,須要用DrawerLayout來做爲用戶界面的根視圖,DrawerLayout視圖下需放置兩個子視圖,一個是用來顯示顯示屏幕的主體內容(導航抽屜隱藏的時候),一個是用來顯示導航抽屜。佈局
用來顯示屏幕主體內容的視圖通常是FrameLayout(運行的時候,會被一個Fragment填充),用來顯示導航抽屜的視圖通常是一個ListView,以下所示this
<android.support.v4.widget.DrawerLayout
google
xmlns:android="http://schemas.android.com/apk/res/android"
spa
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="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>
複製代碼
上面的佈局說明了導航抽屜的佈局一些很是重要的特色:
1、顯示主體內容的視圖必須是DrawerLayout下的第一個子視圖,由於抽屜視圖必須在主體內容視圖的上方(意味着DrawerLayout是一個以z軸來佈局的控件)
2、顯示主體內容的視圖必須設置爲匹配父視圖的高和寬,由於當抽屜視圖隱藏的時候顯示主體內容的視圖表明瞭整個用戶界面
3、抽屜視圖的layout_gravity屬性值需爲「start」,To support right-to-left (RTL) languages,specify the value with "start" insteadof "left" (so the drawer appears on the right when thelayout is RTL)
4、抽屜視圖的寬度不宜匹配父視圖,應當適當的窄一點,這樣就能在抽屜顯示的時候還能看到主體內容視圖的一部分
初始化抽屜列表
抽屜視圖通常包含一個ListView(具體包含的View取決於你的應用),該ListView和日常沒什麼兩樣,都須要一個Adapter來填充,也可設置單項選中監聽器
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<String>(this,
R.layout.drawer_list_item, mPlanetTitles));
// Set the list's click listener
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
...
}
}
複製代碼
處理導航項選點擊事件
導航項的點擊事件其實就是包含的ListView項的點擊事件,咱們須要根據點擊的項來相應的改變主體內容,記得上面說過顯示主體內容的View運行時通常會是一個Fragment,因此只要把當前的Fragment替換成相應的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);
}
複製代碼
監聽導航抽屜打開和關閉事件
能夠爲DrawerLayout設置 DrawerLayout.DrawerListener監聽器來監聽打開和關閉事件,當導航抽屜打開和關閉時分別會回調onDrawerOpened() 和onDrawerClosed() 方法
可是若是你的Activity包含Action Bar話,你能夠選擇 ActionBarDrawerToggle 來替代 DrawerListener ,ActionBarDrawerToggle 實現了DrawerListener接口,因此抽屜的打開和關閉事件照樣能監聽的到,而且使用ActionBarDrawerToggle能促進Action Bar Icon和導航抽屜之間的交互(經過點擊icon來打開和關閉導航抽屜),當導航抽屜打開或關閉時Action Bar的狀態也應該作相應的改變( NavigationDrawer 中有介紹)
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) {
getActionBar().setTitle(mTitle);
invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
}
/** Called when a drawer has settled in a completely open state. */
public void onDrawerOpened(View 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);
}
}
複製代碼
點擊應用圖標來打開和關閉導航抽屜
前面咱們是介紹過經過手勢來打開和關閉導航抽屜,可是若是Activity包含Action Bar的話,當咱們點擊應用圖標時也能打開和關閉導航抽屜,並且咱們也須要經過圖標來指示導航抽屜當前的狀態,若是咱們是使用ActionBarDrawerToggle類的話,能夠經過設置構造方法的參數來作到這一點,它的構造方法參數有五個,分別表明:宿主Activity、DrawerLayout、導航抽屜打開時應用圖標、導航抽屜打開時描述文本、導航抽屜關閉時描述文本
還有一點要注意的是,當手機屏幕的配置環境發生變化時(橫豎屏切換),導航抽屜的配置也需改變,當宿主Activity的onRestoreInstanceState方法調用的時候,導航抽屜的狀態也需進行同步,可在onPostCreate方法中進行同步,具體的能夠看以下代碼
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) {
getActionBar().setTitle(mTitle);
}
/** Called when a drawer has settled in a completely open state. */
public void onDrawerOpened(View 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);
}
...
}
複製代碼
幾張運行的截圖: