1、前言
在日常開發(fā)中昆汹,單個(gè)需求可能不難實(shí)現(xiàn),多個(gè)合在一起可能就會(huì)產(chǎn)生各種問題婴栽,比如最近我在實(shí)現(xiàn)頂部導(dǎo)航加上底部導(dǎo)航再加上側(cè)滑菜單满粗,這三種布局相結(jié)合時(shí),就會(huì)產(chǎn)生布局相互覆蓋愚争,或者獲取不到焦點(diǎn)種種問題映皆。在研究后得到一種比較兼容三種需求的實(shí)現(xiàn)方法,頂部導(dǎo)航欄可以參考上篇文章轰枝,底部導(dǎo)航采用TabLayout結(jié)合Fragment捅彻,側(cè)滑菜單采用官方實(shí)現(xiàn)方式DrawerLayout+NavigationView,就此做個(gè)分享鞍陨。
2沟饥、效果圖
由圖中看到,已經(jīng)是一個(gè)比較常見的布局了湾戳,包括了上下導(dǎo)航欄和側(cè)滑菜單贤旷。
3、底部導(dǎo)航
一般分為 3 —— 5 個(gè)Tab砾脑,比如微信幼驶,展示了有四個(gè)Tab,分別對(duì)應(yīng)不同的板塊(微信韧衣、通訊錄盅藻、發(fā)現(xiàn)购桑、我),現(xiàn)在市面出了少部分的Material Design 風(fēng)格的除外氏淑,大部分都是這樣的一個(gè)框架勃蜘。頁面做Tab切換首先最容易想到的還是TabLayout,切換Fragment對(duì)頁面顯示更加靈活一點(diǎn)假残。
頁面布局
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="@+id/fragment_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</FrameLayout>
<android.support.design.widget.TabLayout
android:id="@+id/bottom_tab_layout"
android:layout_width="match_parent"
android:layout_height="50dp"
app:tabIndicatorHeight="0dp"
app:tabSelectedTextColor="@color/bottom_tab_text_color">
</android.support.design.widget.TabLayout>
</LinearLayout>
TabLayout
默認(rèn)是帶有Indicator
的缭贡,也就是指示條,做底部導(dǎo)航時(shí)是不需要的辉懒,在布局中設(shè)置tabIndicatorHeight="0dp“
即可阳惹。
代碼
private void initView() {
//按鈕圖標(biāo)資源
iconList = Arrays.asList(bottomTabIcon);
iconPressList = Arrays.asList(bottomIconPress);
//由Tab控制切換的Fragment
fragmentList = new ArrayList<>();
fragmentList.add(new HomeFragment());
fragmentList.add(new FavoriteFragment());
fragmentList.add(new FriendsFragment());
fragmentList.add(new PersonFragment());
bottmeTab.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
if(pos_tab != null) {
//將上一次被選中的Tab置為未選中狀態(tài)
bottmeTab.getTabAt(pos_tab).setIcon(iconList.get(pos_tab));
}
fragment = fragmentList.get(tab.getPosition());
ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.fragment_content,fragment).commit();
pos_tab = tab.getPosition();
bottmeTab.getTabAt(pos_tab).setIcon(iconPressList.get(pos_tab));
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
bottmeTab.addTab(bottmeTab.newTab().setIcon(R.mipmap.home).setText("首頁"));
bottmeTab.addTab(bottmeTab.newTab().setIcon(R.mipmap.favorite).setText("關(guān)注"));
bottmeTab.addTab(bottmeTab.newTab().setIcon(R.mipmap.friends).setText("好友"));
bottmeTab.addTab(bottmeTab.newTab().setIcon(R.mipmap.account).setText("我"));
}
添加Tab之前先添加監(jiān)聽器,避免設(shè)置失效眶俩,第一次載入頁面時(shí)莹汤,第一個(gè)Tab是選中狀態(tài),所以可以在第一個(gè)Tab做初始化操作颠印,如果后加監(jiān)聽器第一次onTabSelected
就不會(huì)選中纲岭。由于圖標(biāo)與字之間的距離不能改變,如果想要改變可以對(duì)Tab按鈕做自定義线罕,調(diào)用newTab().setCustomView(View view)
傳入自定義View止潮。
需要注意的是,采用replace的方法切換Fragment闻坚,會(huì)使前一個(gè)Fragment被已銷毀一次沽翔,生命周期一直走到Detach,其中有一些成員變量需要自己保存窿凤,在重新切換回來時(shí)還原仅偎,由于Fragment實(shí)例沒被銷毀,View的狀態(tài)還被保存著雳殊,如果初始化中有繪制View的操作橘沥,會(huì)導(dǎo)致View被繪制倆次,需要在初始化中設(shè)置避免
4夯秃、側(cè)滑菜單
首先看一下基礎(chǔ)的布局
<android.support.v4.widget.DrawerLayout
android:id="@+id/drawer_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<FrameLayout
android:id="@+id/fragment_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</FrameLayout>
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_drawer_layout__one"
app:menu="@menu/activity_drawer_layout__one_drawer"/>
</android.support.v4.widget.DrawerLayout>
這里把TabLayout直接加在Fragment布局下面探赫,會(huì)導(dǎo)致頁面被底部導(dǎo)航欄充滿课梳,可能是DrawerLagout
本身這樣設(shè)計(jì)铅歼,所以要做改動(dòng)险绘,將我們需要切換的Fragment與TabLayout包裝在一起。
<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"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="@+id/fragment_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</FrameLayout>
<android.support.design.widget.TabLayout
android:id="@+id/bottom_tab_layout"
android:layout_width="match_parent"
android:layout_height="50dp"
app:tabIndicatorHeight="0dp"
app:tabSelectedTextColor="@color/bottom_tab_text_color">
</android.support.design.widget.TabLayout>
</LinearLayout>
<android.support.design.widget.NavigationView
android:id="@+id/nav_left"
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/header_nav_left"
app:menu="@menu/menu_nav_left"
app:insetForeground="@android:color/transparent">
</android.support.design.widget.NavigationView>
</android.support.v4.widget.DrawerLayout>
在Fragment和TabLayout外面包裹一層布局色建,當(dāng)然也可以獨(dú)立出去哺呜,include
進(jìn)來,這里放在一起更加直觀箕戳。側(cè)滑菜單包括一個(gè)頭布局header
和一個(gè)菜單menu
:
header.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="130dp"
android:background="@color/colorPrimary"
android:orientation="vertical">
<ImageView
android:id="@+id/icon_person"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:layout_marginLeft="10dp"
android:src="@mipmap/icon_person"/>
<TextView
android:id="@+id/text_person"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/icon_person"
android:layout_marginTop="10dp"
android:layout_marginLeft="20dp"
android:text="用戶名"/>
</RelativeLayout>
menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group
android:id="@+id/group_content"
android:checkableBehavior="single"
>
<item
android:id="@+id/menu_home"
android:icon="@mipmap/home"
android:title="home" />
<item
android:id="@+id/menu_favorite"
android:icon="@mipmap/favorite"
android:title="favorite"/>
</group>
<group
android:id="@+id/group_person"
android:checkableBehavior="single">
<item
android:id="@+id/menu_account"
android:icon="@mipmap/account"
android:title="account"/>
<item
android:id="@+id/menu_messages"
android:icon="@mipmap/comments"
android:title="messages"/>
<item
android:id="@+id/menu_down"
android:icon="@mipmap/down"
android:title="down"/>
</group>
</menu>
<item>
就是定義一個(gè)菜單項(xiàng)某残。
<group>
是對(duì)菜單進(jìn)行分組国撵,如果需要分割線,添加id后玻墅,這一組菜單的頂部就會(huì)顯示一個(gè)分割線介牙,如上圖效果圖中的那條橫線。
結(jié)合后代碼
private void initView() {
iconList = Arrays.asList(bottomTabIcon);
iconPressList = Arrays.asList(bottomIconPress);
fragmentList = new ArrayList<>();
fragmentList.add(new HomeFragment());
fragmentList.add(new FavoriteFragment());
fragmentList.add(new FriendsFragment());
fragmentList.add(new PersonFragment());
bottmeTab.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
if(pos_tab != null) {
bottmeTab.getTabAt(pos_tab).setIcon(iconList.get(pos_tab));
}
fragment = fragmentList.get(tab.getPosition());
ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.fragment_content,fragment).commit();
pos_tab = tab.getPosition();
bottmeTab.getTabAt(pos_tab).setIcon(iconPressList.get(pos_tab));
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
bottmeTab.addTab(bottmeTab.newTab().setIcon(R.mipmap.home).setText("首頁"));
bottmeTab.addTab(bottmeTab.newTab().setIcon(R.mipmap.favorite).setText("關(guān)注"));
bottmeTab.addTab(bottmeTab.newTab().setIcon(R.mipmap.friends).setText("好友"));
bottmeTab.addTab(bottmeTab.newTab().setIcon(R.mipmap.account).setText("我"));
//側(cè)滑菜單部分
navigationView.setItemIconTintList(null);
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
//關(guān)閉左邊導(dǎo)航
int id = item.getItemId();
drawerLayout.closeDrawer(GravityCompat.START);
return true;
}
});
}
菜單中如果使用矢量圖標(biāo)澳厢,需先setItemIconTintList(null)
环础,否則會(huì)采用自身機(jī)制,矢量圖不能完全顯示赏酥,并且點(diǎn)擊后矢量圖標(biāo)顏色改為應(yīng)用內(nèi)部主要顏色喳整。
通過setNavigationItemSelectedListener
設(shè)置監(jiān)聽處理菜單每個(gè)選項(xiàng)點(diǎn)擊事件谆构,用closeDrawer(GravityCompat.START)
關(guān)閉側(cè)滑菜單裸扶。
源碼地址 個(gè)人項(xiàng)目加入了一些其他東西,主要看MainActivity
總結(jié)了一下三種效果結(jié)合在一起實(shí)現(xiàn)的過程搬素,雖然都是容易的實(shí)現(xiàn)呵晨,在這里分享出來希望看過的人能少走彎路。水平有限熬尺,如果有不正確或者不足的地方歡迎指出摸屠,共同分享一起進(jìn)步。