一.導(dǎo)航抽屜Navigation Drawer
想必用過Android的人都熟悉導(dǎo)航抽屜,這算是一個比較經(jīng)典的導(dǎo)航方式了憔鬼,個人也非常喜歡這種導(dǎo)航方式实束。下面介紹這種導(dǎo)航方式的開發(fā)方法。
創(chuàng)建一個抽屜布局DrawerLayout
<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="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>
這里有一些需要注意的地方:
- 要添加一個導(dǎo)航抽屜逊彭,首先應(yīng)該用support庫提供的DrawerLayout布局來作為你的主布局的根布局,在根布局中构订,應(yīng)該包含顯示主要內(nèi)容的布局和drawer的布局侮叮。
- 第一個布局必須是你的主要內(nèi)容的布局(因為XML文件是根據(jù)z軸的順序來顯示,而抽屜要在主要內(nèi)容的上面)
- 主要內(nèi)容布局需要將長寬都設(shè)置為鋪滿父容器悼瘾,因為當抽屜隱藏時囊榜,它是主要顯示的容器。
- 抽屜view(the ListView)必須指定horizontal_gravity亥宿,通過設(shè)置
android:layout_gravity
卸勺。為了支持從右向左的語言,最好指定值為"start"
而不是left
烫扼。 - 為了讓用戶能看見主頁面的一部分曙求,抽屜view的寬度應(yīng)該用
dp
來設(shè)置,并且盡量不要超過320dp。
初始化Drawer List
在你的activity中悟狱,第一件事就應(yīng)該去初始化導(dǎo)航抽屜列表的每一項静浴。不管你怎么去組織你的app內(nèi)容,一個導(dǎo)航抽屜通常都是由一個列表組成的挤渐,所以我們應(yīng)該從一個適配器Adapter
開始入手(比如ArrayAdapter
或者SimpleCursorAdapter
)苹享。
例如,這段代碼演示了如何通過一個string array來初始化你的列表浴麻。
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); // 為list設(shè)置一個適配器
mDrawerList.setAdapter(new ArrayAdapter<String>(this, R.layout.drawer_list_item, mPlanetTitles)); // 設(shè)置一個監(jiān)聽器
mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); ...
}
}
通過setOnItemClickListener()
接收導(dǎo)航抽屜的列表點擊事件得问。接下來展示如何實現(xiàn)這個接口和當選擇一項時改變主頁面。
處理導(dǎo)航點擊事件
當用戶點擊抽屜的列表時软免,系統(tǒng)會在你通過setOnItemClickListener()
設(shè)置的監(jiān)聽器OnItemClickListener()
上調(diào)用onItemClick()
你的onItemClick()
的功能取決于你的app的功能宫纬。在下面的例子中,選擇每一項都會動態(tài)的插入一個不同的Fragment
到主頁面(FrameLayout
)中或杠。
private class DrawerItemClickListener implements ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
selectItem(position);
}
}
/** 替換主頁面中的fragment */
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);
// 通過替換已存在的fragment來插入
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()// 開始事務(wù)
.replace(R.id.content_frame, fragment)// 替換
.commit();// 提交
// 高亮選中項哪怔,設(shè)置標題,關(guān)閉抽屜
mDrawerList.setItemChecked(position, true);
setTitle(mPlanetTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
}
@Overridepublic void setTitle(CharSequence title) {
mTitle = title;
getActionBar().setTitle(mTitle);
}
監(jiān)聽打開和關(guān)閉抽屜事件
要監(jiān)聽打開和關(guān)閉事件向抢,首先應(yīng)該在你的DrawerLayout
上設(shè)置監(jiān)聽器setDrawerListener()
认境,還要實現(xiàn)DrawerLayout.DrawerListener
,這個接口提供回調(diào)函數(shù)挟鸠,比如 onDrawerOpened()
和onDrawerClosed()
叉信。
然而,與其實現(xiàn)這個接口艘希,如果你的activity包含了actionbar
硼身,你可以替換為使用ActionBarDrawerToggle
類,這個類實現(xiàn)了DrawerLayout.DrawerListener
這個接口覆享,你仍可以去重載那些回調(diào)函數(shù)佳遂,但是使用ActionBarDrawerToggle可以促進action bar的圖標和導(dǎo)航抽屜的正確的交互。
當抽屜可見時撒顿,你應(yīng)該改變action bar的內(nèi)容丑罪,像是改變標題,移除action items來適應(yīng)主頁面的內(nèi)容凤壁,下面的代碼通過一個ActionBarDrawerToggle
類展示了如何去重載DrawerLayout.DrawerListener
回調(diào)函數(shù)吩屹。
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) {
/** 當抽屜完全關(guān)閉時調(diào)用*/
public void onDrawerClosed(View view) { super.onDrawerClosed(view);
getActionBar().setTitle(mTitle);
invalidateOptionsMenu(); // 創(chuàng)建調(diào)用onPrepareOptionsMenu()
}
/** 當抽屜完全打開時調(diào)用 */
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
getActionBar().setTitle(mDrawerTitle);
invalidateOptionsMenu(); // 創(chuàng)建調(diào)用onPrepareOptionsMenu()
}
};
// 設(shè)置toggle為抽屜監(jiān)聽器
mDrawerLayout.setDrawerListener(mDrawerToggle);
}
/* 當調(diào)用invalidateOptionsMenu()時調(diào)用 */
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// 如果導(dǎo)航抽屜式打開的,根據(jù)內(nèi)容隱藏action items
boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
return super.onPrepareOptionsMenu(menu);
}
}
下個部分介紹ActionBarDrawerToggle
的構(gòu)造函數(shù)參數(shù)和與action icon交互的其他要求拧抖。
通過App Icon來打開和關(guān)閉抽屜
用戶能夠通過右滑來打開抽屜煤搜,但是如果你使用action bar,那么你也應(yīng)該允許用戶通過點擊App Icon來打開和關(guān)閉抽屜唧席。同時也應(yīng)該通過一個特殊的icon來顯示隱藏抽屜的存在擦盾。你能夠通過之前介紹的ActionBarDrawerToggle
來實現(xiàn)嘲驾。
為了使ActionBarDrawerToggle
工作,在這里創(chuàng)建一個例子和他的構(gòu)造函數(shù)厌衙,他需要以下的元素:
- 托管抽屜的
Activity
DrawerLayout
- 一個圖片資源用作抽屜開關(guān)的指示器
圖片資源 - 一個描述抽屜打開的string(為了用戶友好)
- 一個描述抽屜關(guān)閉的string(為了用戶友好)
不管你是否創(chuàng)建了ActionBarDrawerToggle
的子類作為你抽屜監(jiān)聽器距淫,你需要在你的activity的整個生命周期中能夠調(diào)用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);
}
...
}
最后給一個例子的完整版
導(dǎo)航抽屜完整版
未完待續(xù)……