Material Design 實戰(zhàn) 之第二彈——滑動菜單詳解&實戰(zhàn)(DrawerLayout & NavigationView)


本模塊共有六篇文章龟糕,參考郭神的《第一行代碼》,對Material Design的學習做一個詳細的筆記换途,大家可以一起交流一下:




文章提要與總結(jié)


1. DrawerLayout 
        控件用處:實現(xiàn)滑動菜單

        1.1 首先它是一個布局,在布局中允許放入兩個直接子控件,
        第一個子控件是主屏幕中顯示的內(nèi)容踏兜;
        第二個子控件是滑動菜單中顯示的內(nèi)容;
                關(guān)于第二個子控件有一點需要注意八秃,layout_gravity這個屬性是必須指定的:left   right   start
        
        1.2 添加導(dǎo)航按鈕:

        1.2.1 首先調(diào)用findViewById()方法得到了DrawerLayout的實例碱妆;
        1.2.2 getSupportActionBar()方法得到了ActionBar的實例;
        1.2.3 調(diào)用ActionBar的setDisplayHomeAsUpEnabled()讓導(dǎo)航按鈕顯示出來昔驱;
        1.2.4 調(diào)用了setHomeAsUpIndicator()方法來設(shè)置一個導(dǎo)航按鈕圖標疹尾;
        1.2.5 在onOptionsItemSelected()中對HomeAsUp按鈕的點擊事件進行處理——調(diào)用DrawerLayout的openDrawer()方法將滑動菜單展示出來;
                注意openDrawer()方法要求傳入一個Gravity參數(shù),
            為了保證這里的行為和XML中(DrawerLayout標簽下的第二個直接子控件的android:layout_gravity值)定義的一致纳本,
            我們傳入了GravityCompat.START窍蓝;
        1.2.6 實際上Toolbar最左側(cè)的這個按鈕就叫作HomeAsUp按鈕,它默認的圖標是一個返回的箭頭繁成,含義是返回上一個活動吓笙;
              這里將其換了圖標,并將邏輯響應(yīng)修改了巾腕;
              HomeAsUp按鈕的id永遠都是android.R.id.homeC婢Α!尊搬!

2. NavigationView
        控件用處:輕松布局華麗炫酷的滑動菜單頁面侮穿;
        2.1 添加了兩行依賴關(guān)系
                compile 'com.android.support:design:24.2.1'
                compile 'de.hdodenhof:circleimageview:2.1.0'
    2.2
          在開始使用NavigationView之前,我們還需要提前準備好兩個東西:menu和headerLayout毁嗦。
        2.2.1 menu是用來在NavigationView中顯示具體的菜單項的亲茅;
                  為Menu resource file;
                  在<menu>中嵌套了一個<group>標簽
                      <group>標簽下的<item>:
                        android:id屬性指定菜單項的id狗准,
                        android:icon屬性指定菜單項的圖標克锣,
                        android:title屬性指定菜單項顯示的文字。
        2.2.2 headerLayout則是用來在NavigationView中顯示頭部布局的腔长。
                  為Layout resourcefile袭祟;
    2.3 使用NavigationView
            添加android.support.design.widget.NavigationView標簽,
                使用app:menu="@menu/nav_menu"
                app:headerLayout="@layout/nav_header"
                將menu和headerLayout設(shè)置完畢

效果圖:




正文


最終成果圖



DrawerLayout   /美 [?dr??] /

關(guān)于滑動菜單和DrawerLayout捞附,郭神如是說:


<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
    </FrameLayout>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:text="This is menu"
        android:textSize="30sp"
        android:background="#FFF"/>

</android.support.v4.widget.DrawerLayout>

可見這里最外層的控件使用了DrawerLayout巾乳,這個控件是由support-v4庫提供的。

DrawerLayout中放置了兩個直接子控件:
第一個子控件是FrameLayout鸟召,用于作為主屏幕中顯示的內(nèi)容胆绊,當然里面還有我們剛剛定義的Toolbar。
第二個子控件這里使用了一個TextView欧募,用于作為滑動菜單中顯示的內(nèi)容压状,其實使用什么都可以,DrawerLayout并沒有限制只能使用固定的控件跟继。

但是關(guān)于第二個子控件有一點需要注意种冬,layout_gravity這個屬性是必須指定的,
因為我們需要告訴DrawerLayout滑動菜單是在屏幕的左邊還是右邊舔糖,
指定left表示滑動菜單在左邊娱两;
指定right表示滑動菜單在右邊;
這里指定了start金吗,表示會根據(jù)系統(tǒng)語言進行判斷十兢,如果系統(tǒng)語言是從左往右的趣竣,比如英語、漢語纪挎,滑動菜單就在左邊,如果系統(tǒng)語言是從右往左的跟匆,比如阿拉伯語异袄,滑動菜單就在右邊。

現(xiàn)在重新運行一下程序玛臂,然后在屏幕的左側(cè)邊緣向右拖動烤蜕,就可以讓滑動菜單顯示出來了,如圖:


這里我們并沒有改動多少代碼迹冤,

  • 首先調(diào)用findViewById()方法得到了DrawerLayout的實例讽营,
  • 然后調(diào)用getSupportActionBar()方法得到了ActionBar的實例,雖然這個ActionBar的具體實現(xiàn)是由Toolbar來完成的泡徙。
  • 接著調(diào)用ActionBar的setDisplayHomeAsUpEnabled()方法讓導(dǎo)航按鈕顯示出來橱鹏,
  • 又調(diào)用了setHomeAsUpIndicator()方法來設(shè)置一個導(dǎo)航按鈕圖標。

實際上堪藐,Toolbar最左側(cè)的這個按鈕就叫作HomeAsUp按鈕莉兰,它默認的圖標是一個返回的箭頭,含義是返回上一個活動礁竞。很明顯糖荒,這里我們將它默認的樣式(該按鈕圖標)和作用(改/設(shè)置了按鈕點擊事件)都進行了修改。

接下來在onOptionsItemSelected()方法中對HomeAsUp按鈕的點擊事件進行處理模捂,
HomeAsUp按鈕的id永遠都是android.R.id.home捶朵;

切記是android.R.id.home,如果寫成R.id.home是實現(xiàn)不了功能的狂男!

然后調(diào)用DrawerLayout的openDrawer()方法將滑動菜單展示出來综看;
注意openDrawer()方法要求傳入一個Gravity參數(shù),為了保證這里的行為和XML中定義的一致岖食,我們傳入了GravityCompat.START寓搬;

當前MainActivity全文:

public class MainActivity extends AppCompatActivity {

    private DrawerLayout mDrawerLayout;

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

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);


        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBar actionBar = getSupportActionBar();
        if(actionBar != null){
            actionBar.setDisplayHomeAsUpEnabled(true);//讓導(dǎo)航按鈕顯示出來
            actionBar.setHomeAsUpIndicator(R.drawable.ic_menu);//設(shè)置一個導(dǎo)航按鈕圖標
        }

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.toolbar,menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()){
            case android.R.id.home:
                mDrawerLayout.openDrawer(GravityCompat.START);
                break;
            case R.id.backup:
                Toast.makeText(this,"You clicked Backup" , Toast.LENGTH_SHORT).show();
                break;
            case R.id.delete:
                Toast.makeText(this,"You clicked Delete" , Toast.LENGTH_SHORT).show();
                break;
            case R.id.settings:
                Toast.makeText(this,"You clicked Settings" , Toast.LENGTH_SHORT).show();
                break;
            default:
        }
        return true;
    }

}

運行程序,效果如下:

可見在Toolbar的最左邊出現(xiàn)了一個導(dǎo)航按鈕县耽,用戶看到這個按鈕就知道這肯定是可以點擊的句喷。
現(xiàn)在點擊一下這個按鈕,滑動菜單界面就會再次展示出來了兔毙。



NavigationView


首先這個控件是DesignSupport庫中提供的唾琼,需要將這個庫引入到項目中。
打開app/build.gradle文件澎剥,在dependencies閉包中添加依賴:

    compile 'com.android.support:design:24.2.1'
    compile 'de.hdodenhof:circleimageview:2.1.0'

這里添加了兩行依賴關(guān)系锡溯,
第一行就是DesignSupport庫,
第二行是一個開源項目CircleImageView,它可以用來輕松實現(xiàn)圖片圓形化的功能祭饭,我們待會就會用到它芜茵。
CircleImageView的項目主頁地址是:https://github.com/hdodenhof/CircleImageView

3>糯!
在開始使用NavigationView之前寺鸥,我們還需要提前準備好兩個東西:menu和headerLayout猪钮。
menu是用來在NavigationView中顯示具體的菜單項的;
headerLayout則是用來在NavigationView中顯示頭部布局的胆建。


1/4.準備menu

我們先來準備menu烤低,這里我事先找了幾張圖片來作為按鈕的圖標,并將它們放在了drawable-xxhdpi目錄下笆载。然后右擊menu文件夾→New→Menu resource file扑馁,創(chuàng)建一個nav_menu.xml文件,并編寫如下代碼:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/nav_call"
            android:icon="@drawable/nav_call"
            android:title="Call"/>
        <item
            android:id="@+id/nav_friends"
            android:icon="@drawable/nav_friends"
            android:title="Friends"/>
        <item
            android:id="@+id/nav_location"
            android:icon="@drawable/nav_location"
            android:title="Location"/>
        <item
            android:id="@+id/nav_mail"
            android:icon="@drawable/nav_mail"
            android:title="Mail"/>
        <item
            android:id="@+id/nav_task"
            android:icon="@drawable/nav_task"
            android:title="Tasks"/>
    </group>

</menu>

我們首先在<menu>中嵌套了一個<group>標簽凉驻,
然后將group的checkableBehavior屬性指定為singlegroup表示一個組檐蚜,
checkableBehavior指定為single表示組中的所有菜單項只能單選;

那么下面我們來看一下這些菜單項吧沿侈。這里一共定義了5個item闯第,
分別使用
android:id屬性指定菜單項的id,
android:icon屬性指定菜單項的圖標缀拭,
android:title屬性指定菜單項顯示的文字咳短。
就是這么簡單,現(xiàn)在我們已經(jīng)把menu準備好了蛛淋。


2/4.準備headerLayout

接下來應(yīng)該準備headerLayout了咙好,這是一個可以隨意定制的布局,不過這里不將它做得太復(fù)雜褐荷。我們就在headerLayout中放置頭像勾效、用戶名、郵箱地址這3項內(nèi)容吧叛甫;
說到頭像层宫,那我們還需要再準備一張圖片,這里找了一張寵物圖片其监,并把它放在了drawable-xxhdpi目錄下萌腿。
另外這張圖片最好是一張正方形圖片,因為待會我們會把它圓形化抖苦。
然后右擊layout文件夾→New→Layout resourcefile毁菱,創(chuàng)建一個nav_header.xml文件米死。
修改其中的代碼,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="180dp"
    android:padding="10dp"
    android:background="?attr/colorPrimary">

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/icon_image"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:src="@drawable/nav_icon"
        android:layout_centerInParent="true"/>

    <TextView
        android:id="@+id/mail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="8267*****@qq.com"
        android:textColor="#FFF"
        android:textSize="14sp"/>

    <TextView
        android:id="@+id/username"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/mail"
        android:text="wanchuangxiaoyun"
        android:textColor="#FFF"
        android:textSize="14sp"/>

</RelativeLayout>

可以看到贮庞,布局文件的最外層是一個RelativeLayout峦筒,我們將它的
寬度設(shè)為match_parent,
高度設(shè)為180dp窗慎,
這是一個NavigationView比較適合的高度物喷,然后
指定它的背景色為colorPrimary;

在RelativeLayout中我們放置了3個控件捉邢,
CircleImageView是一個用于將圖片圓形化的控件脯丝,它的用法非常簡單商膊,基本和ImageView是完全一樣的伏伐,這里給它指定了一張圖片作為頭像,然后設(shè)置為居中顯示晕拆。
另外兩個TextView分別用于顯示用戶名和郵箱地址藐翎,它們都用到了一些RelativeLayout的定位屬性;


3/4.使用NavigationView

現(xiàn)在menu和headerLayout都準備好了实幕,我們終于可以使用NavigationView了吝镣。
修改activitymam.xml中的代碼,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
    </FrameLayout>

    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:menu="@menu/nav_menu"
        app:headerLayout="@layout/nav_header">

    </android.support.design.widget.NavigationView>

</android.support.v4.widget.DrawerLayout>

可見這里將之前的TextView換成了NavigationView昆庇,這樣滑動菜單中顯示的內(nèi)容也就變成NavigationView了末贾。
這里又通過app:menu和app:headerLayout將我們剛才準備好的menu和headerLayout設(shè)置了進去,
android:layout_gravity="start"則是跟上面的textview一個道理了整吆,用于指明滑動菜單的滑動位置拱撵,
這樣NavigationView就定義完成了。

接下來還要去處理菜單項的點擊事件表蝙。修改MainActivity中的代碼:


代碼還是比較簡單的拴测,
這里首先獲取到了NavigauonView的實例,
然后調(diào)用它的setCheckedItem()方法將Call菜單項設(shè)置為默認選中府蛇。

接著調(diào)用了setNavigationItemSelectedListener()方法來設(shè)置一個菜單項選中事件的監(jiān)聽器集索,當用戶點擊了任意菜單項時,就會回調(diào)到onNavigationItemSelected()方法中汇跨。
我們可以在這個方法中寫相應(yīng)的邏輯處理务荆,不過這里并沒有附加任何邏輯,只是調(diào)用了DrawerLayout的closeDrawers()方法將滑動菜單關(guān)閉穷遂,這也是合情合理的做法蛹含。

下面是當前MainActivity.java的全文:

public class MainActivity extends AppCompatActivity {

    private DrawerLayout mDrawerLayout;

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

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);


        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        NavigationView navView = (NavigationView) findViewById(R.id.nav_view);
        ActionBar actionBar = getSupportActionBar();
        if(actionBar != null){
            actionBar.setDisplayHomeAsUpEnabled(true);//讓導(dǎo)航按鈕顯示出來
            actionBar.setHomeAsUpIndicator(R.drawable.ic_menu);//設(shè)置一個導(dǎo)航按鈕圖標
        }

        navView.setCheckedItem(R.id.nav_call);//將Call菜單項設(shè)置為默認選中
        navView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener(){
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                mDrawerLayout.closeDrawers();//關(guān)閉滑動菜單
                return true;
            }
        });

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.toolbar,menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()){
            case R.id.home:
                mDrawerLayout.openDrawer(GravityCompat.START);
                break;
            case R.id.backup:
                Toast.makeText(this,"You clicked Backup" , Toast.LENGTH_SHORT).show();
                break;
            case R.id.delete:
                Toast.makeText(this,"You clicked Delete" , Toast.LENGTH_SHORT).show();
                break;
            case R.id.settings:
                Toast.makeText(this,"You clicked Settings" , Toast.LENGTH_SHORT).show();
                break;
            default:
        }
        return true;
    }

}

現(xiàn)在可以重新運行一下程序了塞颁,點擊一下Toolbar左側(cè)的導(dǎo)航按鈕浦箱,效果如圖所示:

怎么樣吸耿?這樣的滑動菜單頁面,你無論如何也不能說它丑了吧酷窥?MaterialDesign的魅力就在
這里咽安,它真的是一種非常美觀的設(shè)計理念,只要你按照它的各種規(guī)范和建議來設(shè)計界面蓬推,最終做
出來的程序就是特別好看的妆棒。
——郭霖大神

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市沸伏,隨后出現(xiàn)的幾起案子糕珊,更是在濱河造成了極大的恐慌,老刑警劉巖毅糟,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件红选,死亡現(xiàn)場離奇詭異,居然都是意外死亡姆另,警方通過查閱死者的電腦和手機喇肋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來迹辐,“玉大人蝶防,你說我怎么就攤上這事∶鞣裕” “怎么了间学?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長印荔。 經(jīng)常有香客問我低葫,道長,這世上最難降的妖魔是什么躏鱼? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任氮采,我火速辦了婚禮,結(jié)果婚禮上染苛,老公的妹妹穿的比我還像新娘鹊漠。我一直安慰自己,他們只是感情好茶行,可當我...
    茶點故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布躯概。 她就那樣靜靜地躺著,像睡著了一般畔师。 火紅的嫁衣襯著肌膚如雪娶靡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天看锉,我揣著相機與錄音姿锭,去河邊找鬼塔鳍。 笑死,一個胖子當著我的面吹牛呻此,可吹牛的內(nèi)容都是我干的轮纫。 我是一名探鬼主播,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼焚鲜,長吁一口氣:“原來是場噩夢啊……” “哼掌唾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起忿磅,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤糯彬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后葱她,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體撩扒,經(jīng)...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年览效,在試婚紗的時候發(fā)現(xiàn)自己被綠了却舀。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片虫几。...
    茶點故事閱讀 38,768評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡锤灿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出辆脸,到底是詐尸還是另有隱情但校,我是刑警寧澤,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布啡氢,位于F島的核電站状囱,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏倘是。R本人自食惡果不足惜亭枷,卻給世界環(huán)境...
    茶點故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搀崭。 院中可真熱鬧叨粘,春花似錦、人聲如沸瘤睹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽轰传。三九已至驴党,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間获茬,已是汗流浹背港庄。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工倔既, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鹏氧。 一個月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓叉存,卻偏偏與公主長得像,于是被迫代替她去往敵國和親度帮。 傳聞我的和親對象是個殘疾皇子歼捏,可洞房花燭夜當晚...
    茶點故事閱讀 43,666評論 2 350