Android 導(dǎo)航

一.導(dǎo)航抽屜Navigation Drawer

想必用過Android的人都熟悉導(dǎo)航抽屜,這算是一個比較經(jīng)典的導(dǎo)航方式了憔鬼,個人也非常喜歡這種導(dǎo)航方式实束。下面介紹這種導(dǎo)航方式的開發(fā)方法。


導(dǎo)航抽屜
導(dǎo)航抽屜

創(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()叉信。

回調(diào)函數(shù)解析

然而,與其實現(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ù)……

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市婶希,隨后出現(xiàn)的幾起案子榕暇,更是在濱河造成了極大的恐慌,老刑警劉巖喻杈,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件彤枢,死亡現(xiàn)場離奇詭異,居然都是意外死亡筒饰,警方通過查閱死者的電腦和手機缴啡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瓷们,“玉大人业栅,你說我怎么就攤上這事∶危” “怎么了碘裕?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長攒钳。 經(jīng)常有香客問我帮孔,道長,這世上最難降的妖魔是什么不撑? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任文兢,我火速辦了婚禮,結(jié)果婚禮上焕檬,老公的妹妹穿的比我還像新娘姆坚。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著懂扼,像睡著了一般恩闻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上幢妄,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天兔仰,我揣著相機與錄音,去河邊找鬼蕉鸳。 笑死乎赴,一個胖子當著我的面吹牛忍法,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播榕吼,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼饿序,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了羹蚣?” 一聲冷哼從身側(cè)響起原探,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎顽素,沒想到半個月后咽弦,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡胁出,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年型型,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片全蝶。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡闹蒜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抑淫,到底是詐尸還是另有隱情绷落,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布丈冬,位于F島的核電站嘱函,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏埂蕊。R本人自食惡果不足惜往弓,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蓄氧。 院中可真熱鬧函似,春花似錦、人聲如沸喉童。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽堂氯。三九已至蔑担,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咽白,已是汗流浹背啤握。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留晶框,地道東北人排抬。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓懂从,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蹲蒲。 傳聞我的和親對象是個殘疾皇子番甩,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容