側(cè)滑菜單這種設(shè)計(jì)份氧,在很多 APP 上面都有看到同辣,例如大版本3.0之前的知乎、網(wǎng)易新聞匀们、滴滴打車(chē)等缴淋。有些熱衷于 Android Material Design 的開(kāi)發(fā)者,甚至將一些 IOS 化的 Android 應(yīng)用改頭換面泄朴,將其 MD 化重抖。這過(guò)程中,基本上都會(huì)給這些“改版”應(yīng)用裝上“抽屜”祖灰,例如 Xposed 上面的經(jīng)典插件——“WechatUI ”钟沛、Github 上面的“Material Design 豆瓣客戶(hù)端——豆芽”,以及一些第三方微博客戶(hù)端局扶。
既然側(cè)滑菜單如此受大家歡迎恨统,那我們就趕緊去了解一下它吧
側(cè)滑菜單的實(shí)現(xiàn)方式
側(cè)滑菜單主要有兩種實(shí)現(xiàn)方式,一是使用開(kāi)源庫(kù)三妈,如 SlidingMenu畜埋、MaterialDrawer 等,二是使用 Android 官方推薦的 DrawerLayout 實(shí)現(xiàn)畴蒲。
我們就先以官方推薦的為基礎(chǔ)來(lái)進(jìn)行學(xué)習(xí)悠鞍,如果能把官方推薦的知識(shí)掌握了,再使用開(kāi)源庫(kù)就更不是問(wèn)題了模燥。
DrawerLayout
DrawerLayout 是谷歌推出的一款用于實(shí)現(xiàn)側(cè)滑菜單效果的控件咖祭,集成在 support library 里面,使用時(shí)需要加載 android-support-v4.jar 包蔫骂。
DrawerLayout 分為側(cè)邊菜單和主內(nèi)容區(qū)兩部分么翰,側(cè)邊菜單可以根據(jù)手勢(shì)展開(kāi)或隱藏,主內(nèi)容區(qū)可以根據(jù)側(cè)邊菜單的變換而改變辽旋。
使用方法
先看代碼
布局:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.ginkwang.drawertest.MainActivity">
?
<FrameLayout
android:id="@+id/fl_content"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
?
<!--左邊抽屜菜單-->
<RelativeLayout
android:id="@+id/rl_left"
android:layout_width="200dp"
android:layout_height="match_parent"
android:layout_gravity="left"
>
<ListView
android:id="@+id/lv_left"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
?
<!--右邊抽屜菜單-->
<RelativeLayout
android:id="@+id/rl_right"
android:layout_width="200dp"
android:layout_height="match_parent"
android:layout_gravity="right"
>
<ListView
android:id="@+id/lv_right"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
</android.support.v4.widget.DrawerLayout>
從布局文件可以看出硬鞍,DrawerLayout 是根布局,然后緊跟著的是第一個(gè)子元素是默認(rèn)內(nèi)容(抽屜未打開(kāi)的默認(rèn)內(nèi)容,即本例中的 FrameLayout)固该,之后的是抽屜內(nèi)容锅减。
抽屜菜單的擺放通過(guò)抽屜布局的 android:layout_gravity="" 的屬性來(lái)控制,可填入 "left"伐坏、"right"怔匣、"start"、"end"桦沉。此屬性必須設(shè)置每瞒,要不然側(cè)滑抽屜時(shí)會(huì)報(bào)錯(cuò)。
抽屜菜單寬度單位為 dp纯露,大小一般不超過(guò) 320dp剿骨,這樣打開(kāi)抽屜后,依然可以看到部分內(nèi)容布局埠褪。
代碼
布局文件寫(xiě)好之后浓利,側(cè)滑效果按理說(shuō)就已經(jīng)實(shí)現(xiàn)了。但是此時(shí)你滑動(dòng)菜單時(shí)钞速,會(huì)發(fā)現(xiàn)左邊拉出來(lái)什么內(nèi)容都沒(méi)有贷掖,這是因?yàn)槲覀儧](méi)有給菜單布局注入數(shù)據(jù)。
public class MainActivity extends AppCompatActivity {
?
private static final String TAG = "MainActivity";
?
private Context mContext;
private DrawerLayout mDlMain;
private FrameLayout mFlContent;
private RelativeLayout mRlLeft, mRlRight;
private ListView mLvLeft;
private TextView mTvRight;
?
private String[] leftMenuNames = {"left_item1", "left_item2",
"left_item3", "left_item4"};
?
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
initView();
}
?
private void initView() {
mDlMain = (DrawerLayout) findViewById(R.id.dl_main);
mFlContent = (FrameLayout) findViewById(R.id.fl_content);
mRlLeft = (RelativeLayout) findViewById(R.id.rl_left);
mRlRight = (RelativeLayout) findViewById(R.id.rl_right);
mLvLeft = (ListView) findViewById(R.id.lv_left);
mLvLeft.setAdapter(new ArrayAdapter<String>(mContext,
android.R.layout.simple_list_item_1, leftMenuNames));//給左邊菜單寫(xiě)入數(shù)據(jù)
mTvRight = (TextView) findViewById(R.id.tv_right);
mTvRight.setText("right_content");//給右邊菜單內(nèi)容賦值
}
}
效果
菜單添加點(diǎn)擊事件
菜單做好之后,自然就會(huì)給菜單賦予點(diǎn)擊事件,來(lái)控制主內(nèi)容區(qū)顯示的內(nèi)容间驮。給菜單添加點(diǎn)擊事件,其實(shí)就是給菜單布局里面的 listview 添加點(diǎn)擊事件牙甫。這個(gè)簡(jiǎn)單得很,就是 listview.setOnItemClickListener(this) 即可调违。點(diǎn)擊之后窟哺,我們之前在主界面布局里定義的 FrameLayout 就會(huì)顯示相應(yīng)的 Fragment。代碼如下:
mLvLeft.setOnItemClickListener(this);
?
...
?
//左側(cè)菜單點(diǎn)擊事件
@Override
public void onItemClick(AdapterView<?> pAdapterView, View pView, int pI, long pL) {
Fragment lFragment = new MenuFragment();
Bundle lBundle = new Bundle();
lBundle.putString("menu_str", "item_" + (pI + 1));
lFragment.setArguments(lBundle);
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.fl_content, lFragment).commit();
mLvLeft.setItemChecked(pI, true);
mDlMain.closeDrawers();//關(guān)閉抽屜
}
MenuFragment 代碼如下:
public class MenuFragment extends Fragment {
?
private View mView;
private Context mContext;
private TextView mTvMenuFragment;
?
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.layout_fragment_menu, container, false);
mContext = getActivity();
Bundle lBundle = getArguments();
mTvMenuFragment = (TextView) mView.findViewById(R.id.tv_menu_fragment);
if (lBundle != null) {
mTvMenuFragment.setText(lBundle.getString("menu_str"));
}
return mView;
}
}
上面代碼中翰萨,涉及到一個(gè)關(guān)閉抽屜的操作 DrawerLayout .closeDrawers(),那打開(kāi)抽屜的操作就是 DrawerLayout .openDrawer(int gravity)糕殉,gravity 為抽屜開(kāi)啟方向亩鬼,應(yīng)與布局文件中定義的方向一致,不然會(huì)報(bào)錯(cuò)阿蝶。下面在代碼中實(shí)現(xiàn)按鈕控制抽屜的開(kāi)啟操作:
布局:
在默認(rèn)顯示布局的 FrameLayout 中添加操作按鈕:
<FrameLayout
android:id="@+id/fl_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btn_open_left_drawer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="開(kāi)啟左邊抽屜"/>
?
<Button
android:id="@+id/btn_open_all_drawer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center|top"
android:text="開(kāi)啟全部抽屜"/>
?
<Button
android:id="@+id/btn_open_right_drawer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="開(kāi)啟右邊抽屜"/>
</FrameLayout>
代碼:
在代碼中初始化按鈕雳锋,并實(shí)現(xiàn)按鈕點(diǎn)擊事件:
private Button mBtnOpenLeftDrawer, mBtnOpenAllDrawer, mBtnOpenRightDrawer;
?
...
mBtnOpenLeftDrawer = (Button) findViewById(R.id.btn_open_left_drawer);
mBtnOpenLeftDrawer.setOnClickListener(this);
mBtnOpenAllDrawer = (Button) findViewById(R.id.btn_open_all_drawer);
mBtnOpenAllDrawer.setOnClickListener(this);
mBtnOpenRightDrawer = (Button) findViewById(R.id.btn_open_right_drawer);
mBtnOpenRightDrawer.setOnClickListener(this);
?
...
?
@Override
public void onClick(View pView) {
switch (pView.getId()) {
case R.id.btn_open_left_drawer:
mDlMain.openDrawer(Gravity.LEFT);
break;
?
case R.id.btn_open_all_drawer:
mDlMain.openDrawer(Gravity.LEFT);
mDlMain.openDrawer(Gravity.RIGHT);
break;
?
case R.id.btn_open_right_drawer:
mDlMain.openDrawer(Gravity.RIGHT);
break;
}
}
效果:
去除抽屜劃出后主內(nèi)容頁(yè)背景的灰色
DrawerLayout.setScrimColor(Color.TRANSPARENT);
填充抽屜劃出后與屏幕邊緣之間的內(nèi)容
DrawerLayout.setDrawerShadow();
常見(jiàn)問(wèn)題
- 在點(diǎn)擊DrawerLayout中的空白處的時(shí)候,底部的content會(huì)獲得事件
由于Google的demo是一個(gè)ListView羡洁,所以ListView會(huì)獲得焦點(diǎn)玷过,事件就不會(huì)傳遞了,看不出來(lái)問(wèn)題。但是如果用的include加載的布局辛蚊,會(huì)出現(xiàn)這個(gè)情況粤蝎,那么如何解決?
解決辦法:在include進(jìn)的那個(gè)布局里面袋马,添加clickable=true - 除了抽屜的布局視圖之外的視圖究竟放哪里
左初澎、右抽屜和中間內(nèi)容視圖默認(rèn)是不顯示的,其他布局視圖都會(huì)直接顯示出來(lái)虑凛,但是需要將其放在 DrawerLayout 內(nèi)部才能正常使用(不要放在外面)碑宴,否則要么是相互覆蓋,或者就是觸屏事件失效桑谍,滾動(dòng)等效果全部失效延柠。
使用 NavigationView 實(shí)現(xiàn)側(cè)滑菜單的布局
前文中涉及到的側(cè)滑菜單的布局都是我們自己寫(xiě)的,雖然邏輯并不復(fù)雜锣披,各種樣式也可定義無(wú)限制贞间,但總是要耗費(fèi)些許時(shí)間。而且各家 APP 最終實(shí)現(xiàn)的樣式也五花八門(mén)盈罐,風(fēng)格不統(tǒng)一榜跌。
終于,谷歌在 Android 大版本5.0時(shí)推出了 MD 風(fēng)格的導(dǎo)航菜單 —— NavigationView 盅粪。NavigationView 整體分為兩部分钓葫,上部分叫 HeaderLayout,下部分的點(diǎn)擊項(xiàng)為 Menu票顾。
我們就一起來(lái)看看 NavigationView 的使用方式吧础浮。
NavigationView 使用方法
NavigationView 是 Design Support 中的控件,所以如果想要使用它奠骄,首先要先引入 Design 庫(kù)豆同。
引入 Design 庫(kù)
在項(xiàng)目的 build.gradle 中添加如下代碼:
dependencies {
...
compile 'com.android.support:design:26.0.0-alpha1'
}
然后我們還要準(zhǔn)備好構(gòu)成 NavigationView 的兩個(gè)部分,Menu 和 HeaderLayout含鳞。
新建 Menu
在 xml 下新建 menu 文件夾影锈,然后在 menu 文件夾下再新建 nav_manu.xml 文件,menu 文件代碼為:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item
android:id="@+id/item_nav_menu_news"
android:icon="@mipmap/ic_launcher"
android:title="@string/nav_menu_news"/>
?
<item
android:id="@+id/item_nav_menu_star"
android:icon="@mipmap/ic_launcher"
android:title="@string/nav_menu_star"/>
?
<item
android:id="@+id/item_nav_menu_share"
android:icon="@mipmap/ic_launcher"
android:title="@string/nav_menu_share"/>
?
<item
android:id="@+id/item_nav_menu_set"
android:icon="@mipmap/ic_launcher"
android:title="@string/nav_menu_set"/>
</group>
</menu>
可以看到蝉绷,menu 文件夾下是一個(gè) group 組鸭廷,其中 android:checkableBehavior 屬性設(shè)置為 single,代表 menu 項(xiàng)為單選熔吗。
然后每個(gè)菜單項(xiàng)是由一個(gè)個(gè)的 item 構(gòu)成的辆床,item 的幾個(gè)屬性的簡(jiǎn)單易懂,這里就不詳述桅狠。
新建 HeaderLayout
在 xml 下的 layout 文件夾中新建 layout_nav_header.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="?attr/colorPrimary"
android:gravity="center"
android:orientation="vertical"
android:padding="15dp">
?
<ImageView
android:id="@+id/iv_nav_img"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@mipmap/ic_launcher"/>
?
<TextView
android:id="@+id/tv_nav_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="我知道你 的憂郁是為了什么"
android:textColor="@android:color/white"/>
?
</LinearLayout>
這段代碼也很好懂讼载,就是定義了一個(gè) ImageView轿秧,下面是一行說(shuō)明文字。
OK咨堤,我們正式進(jìn)入主題菇篡,對(duì) NavigationView 進(jìn)行操作。
使用 Navigationview
此示例還是在前文介紹 DrawerLayout 的項(xiàng)目中運(yùn)行的吱型,為了不破壞之前的代碼逸贾,我們新建一個(gè)活動(dòng),在另一個(gè)活動(dòng)中使用 Navigationview津滞。
新建一個(gè)跳轉(zhuǎn)到新活動(dòng)的按鈕铝侵,并寫(xiě)好邏輯:
<Button
android:id="@+id/btn_goto_nav"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="NavigationView"/>
case R.id.btn_goto_nav:
startActivity(new Intent(MainActivity.this, NavActivity.class));
break;
NavActivity 就是我們新建的活動(dòng)。
然后在這個(gè)活動(dòng)中加入 Navigationview 控件触徐,布局中其他還是和前文一樣咪鲜,只是將我們之前自定義的側(cè)滑菜單換成 Navigationview。
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/dl_nav"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.ginkwang.drawertest.NavActivity">
?
<FrameLayout
android:id="@+id/fl_nav_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="NavigationView 抽屜菜單"
android:textColor="@android:color/black"
android:textSize="25sp"/>
</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/layout_nav_header"/>
</android.support.v4.widget.DrawerLayout>
然后在代碼中獲取到 Navigationview撞鹉,并設(shè)置其 menu 的點(diǎn)擊事件:
private NavigationView mNavView;
?
...
mNavView = (NavigationView) findViewById(R.id.nav_view);
mNavView.setCheckedItem(R.id.item_nav_menu_news);
mNavView.setNavigationItemSelectedListener(this);
?
...
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
Toast.makeText(NavActivity.this, item.getTitle(), Toast.LENGTH_LONG).show();
mDlNav.closeDrawers();
return true;
}
邏輯也十分簡(jiǎn)單疟丙,就是打開(kāi)抽屜之后,點(diǎn)擊某一項(xiàng) menu鸟雏,會(huì)將抽屜關(guān)閉享郊,然后提示你當(dāng)前操作哪項(xiàng)的吐司。其中
mNavView.setCheckedItem(R.id.item_nav_menu_news);
表示設(shè)置 menu 的默認(rèn)選中狀態(tài)孝鹊。
最后效果圖:
到這里炊琉,不知道大家看出一個(gè)問(wèn)題沒(méi)有,之前我們定義的 menu item 的 icon 是項(xiàng)目中自帶的 ic_launcher又活。它的本身面目是這樣的:
但是在效果圖中苔咪,卻顯示成了灰色的。
對(duì)于這個(gè)問(wèn)題柳骄,有兩個(gè)解決辦法:
在 Navigationview 的布局中添加 app:itemIconTint="@color/colorPrimary" 屬性团赏,顏色值自定義。
但是這樣設(shè)置后耐薯,還只是能顯示純色舔清,那如果非要顯示原本的圖片該怎么設(shè)置呢?讓我們來(lái)看第二個(gè)解決方法曲初;
在代碼中獲取到 Navigationview 后体谒,設(shè)置其屬性
mNavView.setItemIconTintList(null);
NavigationView 添加分割線
有時(shí)候,側(cè)滑菜單的 item 太多复斥,需要將其按類(lèi)劃分营密,設(shè)置分割線以及類(lèi)標(biāo)注以作明示械媒。
這時(shí)目锭,我們只需要將相應(yīng)的 item 規(guī)劃在一個(gè) group 內(nèi)即可评汰。看代碼:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item
android:id="@+id/item_nav_menu_news"
android:icon="@mipmap/ic_launcher"
android:title="@string/nav_menu_news"/>
?
<item
android:id="@+id/item_nav_menu_star"
android:icon="@mipmap/ic_launcher"
android:title="@string/nav_menu_star"/>
</group>
?
<item android:title="@string/nav_menu_title_common">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/item_nav_menu_share"
android:icon="@mipmap/ic_launcher"
android:title="@string/nav_menu_share"/>
?
<item
android:id="@+id/item_nav_menu_set"
android:icon="@mipmap/ic_launcher"
android:title="@string/nav_menu_set"/>
</group>
</menu>
</item>
?
<item android:title="@string/nav_menu_title_other">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/item_nav_menu_send"
android:icon="@mipmap/ic_launcher"
android:title="@string/nav_menu_send"/>
</group>
</menu>
</item>
</menu>
如果只要分割線的話痢虹,只需要將 item 放在 group 內(nèi)即可被去。如果想要副標(biāo)題指示的話,就需要將其中的 item 規(guī)劃在一個(gè)新的 item 中奖唯,并指定其 title 屬性惨缆。看效果:
NavigationView 頭布局(HeaderLayout)點(diǎn)擊事件
上面代碼中已經(jīng)介紹了 menu 的點(diǎn)擊事件丰捷,只需要在代碼中獲取到 menu 菜單坯墨,然后重寫(xiě) onNavigationItemSelected 即可。
頭布局的點(diǎn)擊事件的獲取和 menu 的一樣病往,也是在代碼中先獲取到頭布局的 view捣染,然后調(diào)用頭布局 view 的 findViewById() 獲取頭布局里面的控件,然后獲取到控件的點(diǎn)擊事件停巷,看代碼:
private View mHeaderView;
private ImageView mIvHeaderImg;
?
...
?
mHeaderView = mNavView.getHeaderView(0);//獲取頭布局
mIvHeaderImg = (ImageView) mHeaderView.findViewById(R.id.iv_nav_img);//獲取頭布局內(nèi) imageview 控件
mIvHeaderImg.setOnClickListener(this);
?
...
?
@Override
public void onClick(View pView) {
Toast.makeText(NavActivity.this, "點(diǎn)擊 ImageView", Toast.LENGTH_LONG).show();
}
整體邏輯也十分簡(jiǎn)單耍攘,就是其中獲取頭布局的代碼 getHeaderView(0) ,可能會(huì)有人不理解畔勤。
其實(shí) Navigationview 本質(zhì)上是一個(gè) RecyclerView蕾各,頭布局通常是第一個(gè)元素,所以獲取頭布局的方式自然是 getHeaderView(0)庆揪。
注意
如果你的 buildToolsVersion 版本是 23.1.0式曲,那獲取頭布局的代碼為:
View headerLayout =
navigationView.inflateHeaderView(R.layout.navigation_header);
參考:
NavigationView獲取Header View的問(wèn)題
結(jié)合 ToolBar,一同構(gòu)建側(cè)滑菜單
寫(xiě)到這里嚷硫,滑動(dòng)菜單的實(shí)現(xiàn)的大部分知識(shí)點(diǎn)都已經(jīng)涵蓋在內(nèi)了检访,但是還有一個(gè)問(wèn)題,就是前文中的例子滑動(dòng)菜單的出現(xiàn)都是我們用手滑動(dòng)屏幕邊緣才出來(lái)的(廢話仔掸!所以叫滑動(dòng)菜單按喙蟆!)起暮。我的意思是卖氨,即誒岸上沒(méi)有一個(gè)提示功能讓人知道滑動(dòng)菜單的存在。Material Design 的建議是在 Toolbar 的左邊添加一個(gè)導(dǎo)航按鈕负懦,點(diǎn)擊按鈕抽屜就會(huì)出現(xiàn)筒捺。這相當(dāng)于給用戶(hù)兩種打開(kāi)抽屜的方式,防止一些用戶(hù)不知道抽屜的存在纸厉。
接下來(lái)我們就來(lái)實(shí)現(xiàn)這個(gè)功能系吭。
添加 Toolbar
我們還是在 NavigationView 的示例項(xiàng)目中操作,還不了解 Toolbar 的朋友請(qǐng)先看一下這篇文章 Android 筆記 —— Toolbar 使用總結(jié)颗品,布局中添加 Toolbar 的代碼就不貼了肯尺,直接看 Activity 中修改的邏輯沃缘。
mToolbar = (Toolbar) findViewById(R.id.tb_nav_bar);
setSupportActionBar(mToolbar);//添加 Toolbar
?
...
?
ActionBar lActionBar = getSupportActionBar();
if (lActionBar != null) {
lActionBar.setDisplayHomeAsUpEnabled(true);//顯示返回按鈕
lActionBar.setHomeAsUpIndicator(R.mipmap.ic_toolbar_menu);//替換返回按鈕圖標(biāo),改為導(dǎo)航按鈕
}
?
...
?
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
mDlNav.openDrawer(Gravity.START);
break;
}
return true;
}
我們先是將 Toolbar 添加進(jìn)來(lái)并獲取则吟,然后通過(guò) getSupportActionBar() 獲取到 ActionBar槐臀,這里 ActionBar 的具體實(shí)現(xiàn)是由 Toolbar 來(lái)完成的。
之后通過(guò) ActionBar.setDisplayHomeAsUpEnabled(true) 顯示 Toolbar 的返回按鈕氓仲,再替換但會(huì)按鈕的圖標(biāo)樣式水慨,改為導(dǎo)航圖標(biāo)。
這時(shí)敬扛,你點(diǎn)擊導(dǎo)航按鈕的話晰洒,菜單還是不會(huì)出現(xiàn),而是返回到上一個(gè)活動(dòng)啥箭,本質(zhì)上它現(xiàn)在還是一個(gè)返回按鈕欢顷。
接著我們?cè)偬鎿Q它的功能,通過(guò)重寫(xiě) onOptionsItemSelected 方法捉蚤,根據(jù) item 的 id 獲取到返回按鈕并對(duì)其進(jìn)行處理抬驴,HomeAsUp 的按鈕 id 是 android.R.id.home。
到此缆巧,我們就完成了 Toolbar 添加導(dǎo)航按鈕進(jìn)行抽屜打開(kāi)的操作了布持。看一下效果:
抽屜不遮擋 Toolbar
前面我們介紹與演示的都是抽屜拉出后遮擋住 Toolbar 的模式陕悬,那相信你也見(jiàn)到或使用過(guò)一些 APP 题暖,就是抽屜拉出后,不遮擋 Toolbar捉超,然后 Toolbar 上的導(dǎo)航按鈕隨著抽屜的拉出狀態(tài)會(huì)變成返回按鈕胧卤。類(lèi)似下面這樣:
接下來(lái)我們就一起來(lái)實(shí)現(xiàn)這樣的效果吧!
首先拼岳,
布局:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
?
<android.support.v7.widget.Toolbar
android:id="@+id/tb_nav_buttom"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:theme="@style/ToolbarTheme"/>
?
<android.support.v4.widget.DrawerLayout
android:id="@+id/dl_nav_buttom"
android:layout_width="match_parent"
android:layout_height="match_parent">
?
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
?
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="抽屜不遮擋 Toolbar"
android:textColor="@android:color/black"
android:textSize="25sp"/>
</FrameLayout>
?
<android.support.design.widget.NavigationView
android:id="@+id/nv_buttom"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/layout_nav_header"
app:menu="@menu/nav_menu"/>
</android.support.v4.widget.DrawerLayout>
</LinearLayout>
整個(gè)布局的實(shí)現(xiàn)邏輯也很簡(jiǎn)單枝誊,就是將 Toolbar 從 DrawerLayout 中隔離出去,放在 DrawerLayout 上面惜纸,最外層是一個(gè)相對(duì)布局叶撒,方向是從上至下的。
然后 Toolbar 中有一個(gè)設(shè)置布局的屬性耐版,我們來(lái)看一下里面定義了什么:
<style name="ToolbarTheme" parent="AppTheme">
<!--左邊導(dǎo)航按鈕顏色-->
<item name="android:textColorSecondary">@color/colorNav</item>
</style>
只是設(shè)置了一個(gè)導(dǎo)航按鈕的顏色祠够,然后將項(xiàng)目父布局的屬性都繼承下來(lái)。
代碼:
來(lái)看一下代碼中都有什么:
public class NavButtomActivity extends AppCompatActivity {
?
private DrawerLayout mDlNavButtom;
private Toolbar mToolbar;
private ActionBarDrawerToggle mActionBarDrawerToggle;
?
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_nav_buttom_toolbar);
initView();
}
?
private void initView() {
mToolbar = (Toolbar) findViewById(R.id.tb_nav_buttom);
mToolbar.setTitle("抽屜不遮擋 Toolbar");//設(shè)置標(biāo)題
mToolbar.setTitleTextColor(getResources().getColor(R.color.colorText));//標(biāo)題顏色
setSupportActionBar(mToolbar);
?
mDlNavButtom = (DrawerLayout) findViewById(R.id.dl_nav_buttom);
mActionBarDrawerToggle = new ActionBarDrawerToggle(this, mDlNavButtom, mToolbar, 0, 0);
mDlNavButtom.setDrawerListener(mActionBarDrawerToggle);
mActionBarDrawerToggle.syncState();//ActionBarDrawerToggle與DrawerLayout的狀態(tài)同步粪牲,
// 并將ActionBarDrawerToggle中的drawer圖標(biāo)古瓤,設(shè)置為ActionBar的Home-Button的icon
}
}
可以看到,我們沒(méi)有按照前文示例中那樣腺阳,將 Toolbar 的返回按鈕替換為我們自己設(shè)置的 icon落君,而是使用 ActionBarDrawerToggle 的默認(rèn)圖標(biāo)滴须。
并調(diào)用 ActionBarDrawerToggle.syncState() 方法將 ActionBarDrawerToggle 與DrawerLayout 的狀態(tài)同步,最終實(shí)現(xiàn)的效果是這樣:
結(jié)束
至此叽奥,側(cè)滑菜單(抽屜)的大部分用法已經(jīng)介紹完了。當(dāng)然痛侍,如果想要在項(xiàng)目中使用抽屜的話朝氓,遠(yuǎn)沒(méi)有這樣復(fù)雜,只需要構(gòu)建工程時(shí)選擇 Navigation Drawer Activity 即可主届。另外赵哲,Github 上面也有更多功能更強(qiáng)大的關(guān)于抽屜的開(kāi)源庫(kù),可供大家選擇君丁。
參考
郭霖 《第一行代碼》
使用DrawerLayout實(shí)現(xiàn)側(cè)拉菜單
android官方側(cè)滑菜單DrawerLayout詳解