Android各種側滑欄總結
前言
經常我們使用Android APP時,會注意到很多app都有側滑欄,比如網易雲、B站、滴滴、QQ等。

網易雲

滴滴
效果展示
本篇部落格一共實現了5種樣式:
側邊欄樣式一 Android原生風格
側邊欄樣式二 B站 網易雲 滴滴風格
側邊欄樣式三 iOS側滑風格
側邊欄樣式四(仿QQ 5.0.0)風格
側邊欄樣式五(仿QQ 8.0.0)風格

5種側邊欄效果
程式碼實現
側邊欄樣式一 Android原生風格
xml佈局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:orientation="vertical" tools:context=".MainActivity"> <!--標題欄樣式一--> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?actionBarSize" android:background="@color/colorPrimary" app:subtitleTextColor="@android:color/white" app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:title="樣式1" app:titleTextColor="@android:color/white"> </android.support.v7.widget.Toolbar> <!--側邊欄樣式--> <android.support.v4.widget.DrawerLayout android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" tools:openDrawer="start"> <!--主頁內容--> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent"/> </android.support.constraint.ConstraintLayout> <!--側邊欄內容--> <android.support.design.widget.NavigationView android:id="@+id/navigation_view" android:layout_width="240dp" android:layout_height="match_parent" android:layout_gravity="start" app:headerLayout="@layout/navigationview_header" app:menu="@menu/menu_navigation"> <!--新增腳佈局--> <LinearLayout android:layout_width="match_parent" android:layout_height="48dp" android:layout_gravity="bottom" android:gravity="center" android:orientation="horizontal"> <Button android:id="@+id/footer_item_setting" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:adjustViewBounds="true" android:background="@android:color/transparent" android:drawableStart="@drawable/ic_setting" android:drawableLeft="@drawable/ic_setting" android:drawablePadding="1dp" android:gravity="center" android:paddingLeft="5dp" android:text="設定" android:textAlignment="inherit" android:textColor="@color/colorPrimary"/> <Button android:id="@+id/footer_item_out" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@android:color/transparent" android:drawableStart="@drawable/ic_out" android:drawableLeft="@drawable/ic_out" android:drawablePadding="1dp" android:gravity="center" android:paddingLeft="5dp" android:text="退出" android:textAlignment="center" android:textColor="@color/colorPrimary"/> </LinearLayout> </android.support.design.widget.NavigationView> </android.support.v4.widget.DrawerLayout> </LinearLayout>
這裡使用DrawerLayout+NavigationView實現的, NavigationView在標籤裡添加布局可以實現腳佈局。
在menu檔案新增item條目,
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <group android:id="@+id/group1" android:checkableBehavior="single"> <item android:id="@+id/single_1" android:icon="@drawable/ic_android_fill" android:title="安卓" app:actionLayout="@layout/bage_hint"/> <item android:id="@+id/single_2" android:icon="@drawable/ic_apple_fill" android:title="蘋果"/> </group> <group android:id="@+id/group2" android:checkableBehavior="single"> <item android:id="@+id/single_3" android:icon="@drawable/ic_github_line" android:title="github"/> <item android:id="@+id/single_4" android:icon="@drawable/ic_wechat_fill" android:title="wechat"/> </group> <item android:title="子選單"> <menu> <item android:id="@+id/item_2" android:icon="@drawable/ic_taobao_fill" android:title="淘寶"/> <item android:id="@+id/item_1" android:icon="@drawable/ic_baidu_line" android:title="百度"/> <item android:id="@+id/item_3" android:icon="@drawable/ic_qq_fill" android:title="騰訊"/> </menu> </item> </menu>
bage_hint.xml佈局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical" android:orientation="vertical"> <TextView android:id="@+id/msg_bg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/badge_bg" android:gravity="center" android:text="0" android:textColor="@android:color/white" android:textSize="10sp"/> </LinearLayout>
vip_view.xml佈局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical" android:orientation="vertical"> <TextView android:id="@+id/tv_vip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:drawableStart="@drawable/ic_vip" android:drawableLeft="@drawable/ic_vip" android:gravity="center" android:text="開通Vip 99元" android:textColor="@android:color/holo_red_light" android:textSize="12sp"/> </LinearLayout>
其中需要注意的是app:actionLayout="@layout/bage_hint",actionLayout這裡是item擴展布局,佈局是從右向左顯示,當太寬的時候,會遮蓋item的內容。

item擴充套件資訊
Style1Activity.class程式碼:
public class Style1Activity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_style1); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); NavigationView navigationview = (NavigationView) findViewById(R.id.navigation_view); final DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); setSupportActionBar(toolbar);//將toolbar與ActionBar關聯 ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawer, toolbar, 0, 0); drawer.setDrawerListener(toggle);//初始化狀態 toggle.syncState(); /*---------------------------新增頭佈局和尾佈局-----------------------------*/ //獲取xml頭佈局view View headerView = navigationview.getHeaderView(0); //新增頭佈局的另外一種方式 //View headview=navigationview.inflateHeaderView(R.layout.navigationview_header); //尋找頭部裡面的控制元件 ImageView imageView = headerView.findViewById(R.id.iv_head); imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getApplicationContext(), "點選了頭像", Toast.LENGTH_LONG).show(); } }); navigationview.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) { return false; } }); ColorStateList csl = (ColorStateList) getResources().getColorStateList(R.color.nav_menu_text_color); //設定item的條目顏色 navigationview.setItemTextColor(csl); //去掉預設顏色顯示原來顏色設定為null顯示本來圖片的顏色 navigationview.setItemIconTintList(csl); //設定單個訊息數量 LinearLayout llAndroid = (LinearLayout) navigationview.getMenu().findItem(R.id.single_1).getActionView(); TextView msg= (TextView) llAndroid.findViewById(R.id.msg_bg); msg.setText("99+"); //設定條目點選監聽 navigationview.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) { //安卓 Toast.makeText(getApplicationContext(), menuItem.getTitle(), Toast.LENGTH_LONG).show(); //設定哪個按鈕被選中 //menuItem.setChecked(true); //關閉側邊欄 //drawer.closeDrawers(); return false; } }); } }
側邊欄樣式二 B站 網易雲 滴滴
第二種方式第一種方式實現類似,只是調整了一下Toolbar與DrawerLayout順序,國內大部分APP應用採用這種側滑方式。
<?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" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:openDrawer="start"> <!--主頁內容--> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <!--標題欄樣式一--> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?actionBarSize" android:background="@color/colorPrimary" app:subtitleTextColor="@android:color/white" app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:title="樣式2" app:titleTextColor="@android:color/white"> </android.support.v7.widget.Toolbar> <!--主頁內容--> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent"/> </android.support.constraint.ConstraintLayout> </LinearLayout> <!--側邊欄內容--> <android.support.design.widget.NavigationView android:id="@+id/navigation_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="start" app:headerLayout="@layout/navigationview_header" app:insetForeground="@android:color/transparent" app:menu="@menu/menu_navigation"> <!--新增腳佈局--> <LinearLayout android:layout_width="match_parent" android:layout_height="48dp" android:layout_gravity="bottom" android:gravity="center" android:orientation="horizontal"> <Button android:id="@+id/footer_item_setting" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:adjustViewBounds="true" android:background="@android:color/transparent" android:drawableStart="@drawable/ic_setting" android:drawableLeft="@drawable/ic_setting" android:drawablePadding="1dp" android:gravity="center" android:paddingLeft="5dp" android:text="設定" android:textAlignment="inherit" android:textColor="@color/colorPrimary"/> <Button android:id="@+id/footer_item_out" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@android:color/transparent" android:drawableStart="@drawable/ic_out" android:drawableLeft="@drawable/ic_out" android:drawablePadding="1dp" android:gravity="center" android:paddingLeft="5dp" android:text="退出" android:textAlignment="center" android:textColor="@color/colorPrimary"/> </LinearLayout> </android.support.design.widget.NavigationView> </android.support.v4.widget.DrawerLayout>

狀態列遮擋
但是還是注意幾點側滑時隱藏狀態列,這裡我們可以將狀態列變成透明色,xml根佈局設定為android:fitsSystemWindows="true",否則主佈局會進入狀態列,在Activity當中新增如下程式碼實現狀態列透明:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //5.x開始需要把顏色設定透明,否則導航欄會呈現系統預設的淺灰色 Window window = activity.getWindow(); View decorView = window.getDecorView(); //兩個 flag 要結合使用,表示讓應用的主體內容佔用系統狀態列的空間 int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; decorView.setSystemUiVisibility(option); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(Color.TRANSPARENT); //導航欄顏色也可以正常設定 //window.setNavigationBarColor(Color.TRANSPARENT); } else { Window window = activity.getWindow(); WindowManager.LayoutParams attributes = window.getAttributes(); int flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; int flagTranslucentNavigation = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; attributes.flags |= flagTranslucentStatus; //attributes.flags |= flagTranslucentNavigation; window.setAttributes(attributes); } }
接著一個bug未解決,又出現一個問題,那就是側滑時,狀態列存在陰影

狀態列陰影
最新的NavigationView控制元件作為側滑內容,而谷歌把它寫死了,一直都會有那個狀態列陰影,所以為NavigationView控制元件新增app:insetForeground=”@android:color/transparent” ,就可以完美解決這個問題。

最終結果
【注意】如果NavigationView預設Menu佈局無法滿足你們公司UI設計獅的要求,那麼就要可以使用這個絕招了,通過Fragment自定義NavigationView佈局。

NavigationView自定義佈局
getSupportFragmentManager().beginTransaction().replace(R.id.navigation_view, new NavigationViewFragment()).commit();
定義一個Fragment填充NavigationView,這樣就可以顯示Fragment得佈局了。
側邊欄樣式三 iOS風格側滑

IOS側邊欄
這種風格側邊欄這裡使用DrawerLayout兩個子佈局之間切換實現,具體程式碼:
<?xml version="1.0" encoding="utf-8"?> <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"> <!--主頁內容--> <FrameLayout android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="match_parent"> <include layout="@layout/activity_content3"/> </FrameLayout> <!--側邊欄內容--> <FrameLayout android:id="@+id/menu_frame" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="start"> <include layout="@layout/left_menu3"/> </FrameLayout> </android.support.v4.widget.DrawerLayout>
Activity程式碼:
public class Style3Activity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); //隱藏狀態列時,獲取狀態列高度 int statusBarHeight = ScreenInfoUtils.getStatusBarHeight(this); //隱藏狀態列 ScreenInfoUtils.fullScreen(this); //初始化佈局 setContentView(R.layout.activity_style3); //初始化狀態列的高度 View statusbar = (View) findViewById(R.id.view_statusbar); ConstraintLayout.LayoutParams params = new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT, statusBarHeight); statusbar.setLayoutParams(params); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); final DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); setSupportActionBar(toolbar);//將toolbar與ActionBar關聯 ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawerLayout, toolbar, 0, 0); drawerLayout.addDrawerListener(toggle);//初始化狀態 toggle.syncState(); //蒙層顏色 drawerLayout.setScrimColor(getResources().getColor(R.color.colorGray)); drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() { @Override public void onDrawerStateChanged(int newState) { } @Override public void onDrawerSlide(@NonNull View drawerView, float slideOffset) { View mContent = drawerLayout.getChildAt(0); View mMenu = drawerView; ViewHelper.setTranslationX(mContent, mMenu.getMeasuredWidth() * slideOffset); } @Override public void onDrawerOpened(@NonNull View drawerView) { } @Override public void onDrawerClosed(@NonNull View drawerView) { } }); } }
側邊欄樣式四(仿QQ 5.0.0)

縮放側邊欄
樣式四與樣式三差不多,只是onDrawerSlide裡面跑的邏輯不一樣
// 滑動的過程中執行 slideOffset:從0到1 //主頁內容 View content = drawerLayout.getChildAt(0); //側邊欄 View menu = drawerView; // float scale = 1 - slideOffset;//1~0 float leftScale = (float) (1 - 0.3 * scale); float rightScale = (float) (0.7f + 0.3 * scale);//0.7~1 //menu.setScaleX(leftScale);//1~0.7 menu.setScaleY(leftScale);//1~0.7 //content.setScaleX(rightScale); content.setScaleY(rightScale); content.setTranslationX(menu.getMeasuredWidth() * slideOffset);//0~width Log.d(TAG, "slideOffset=" + slideOffset + ",leftScale=" + leftScale + ",rightScale=" + rightScale);
側邊欄樣式五(仿QQ 8.0.0)
樣式五其實跟樣式三差不多,它只是側邊欄全部鋪滿螢幕,這個時候側邊欄設定為match_parent仍然無法鋪滿螢幕。
View leftMenu = findViewById(R.id.menu_frame); //獲取側邊欄預設寬度 ViewGroup.LayoutParams leftParams = leftMenu.getLayoutParams(); //獲取螢幕寬度 final int width = ScreenInfoUtils.getWindowWidth(this); //獲取螢幕高度 final int height = ScreenInfoUtils.getFullActivityHeight(this); //設定側邊的寬高(如果不重新設定,即時設定match_parent也只能佔螢幕百分80) leftParams.width = width; leftParams.height = FrameLayout.LayoutParams.MATCH_PARENT; leftMenu.setLayoutParams(leftParams);
這個時候我們可以將側邊檢視的寬高設定為螢幕的寬高,就可以解決這個問題。

側邊欄全屏
總結
這些都是平常玩Android應用所看到的側邊欄,全部總結在這裡,如果以後還看到其他型別的側邊欄再做補充。
github 地址