1、ToolBar
ToolBar是為了替代ActionBar的,ActionBar被限定只能位于Activity的頂部,不能實(shí)現(xiàn)MaterialDesign的效果,所以Google不建議使用ActionBar,而ToolBar不僅繼承了ActionBar的所有功能,而且靈活性很高,可以配合其他控件完成MaterialDesign效果。
我們新建的項(xiàng)目默認(rèn)都會顯示ActionBar的,這是根據(jù)項(xiàng)目中設(shè)置的主題現(xiàn)實(shí)的??聪翧ndroidManifest.xml文件中的配置。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.test.rowingview">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
可以看到上面使用了android:theme="@style/AppTheme"指定了項(xiàng)目的主題AppTheme,看下是它是如何定義的
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
可以看到這里定義了一個(gè)name為AppTheme,parent為Theme.AppCompat.Light.DarkActionBar的主題,DarkActionBar表示深色主題,那么ActionBar則為深色主題,所以ActionBar上面的元素則為淺色主題。如果我們想使用ToolBar來替代ActionBar就需要更換Theme.AppCompat.Light.DarkActionBar為Theme.AppCompat.Light.NoActionBar(淺色主題)或Theme.AppCompat.NoActionBar(深色主題)。下面我們看下如何使用ToolBar
<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">
<androidx.appcompat.widget.Toolbar
android:id="@+id/tool_bar"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/purple_500" />
</LinearLayout>
可以看到我們使用xmlns:app指定了一個(gè)新命名空間,這是因?yàn)?strong>MaterialDesign中屬性是新系統(tǒng)中新增的,老系統(tǒng)中不存在,那么為了兼容老系統(tǒng),我們不能使用android:attribute這樣的寫法,而是使用app:attribute。
由于我們設(shè)置了項(xiàng)目的主題為淺色主題,那么ToolBar的主題也為淺色主題,那么ToolBar上面的元素則為深色,如果我們想讓ToolBar上的元素為淺色,就需要通過android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"設(shè)置ToolBar的主題為深色。這就會使彈出的菜單項(xiàng)為深色,所以為了美觀通過
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"設(shè)置菜單項(xiàng)為淺色主題,由于app:popupTheme是新增的屬性,所以為了兼容需要使用app:attribute的寫法。
如果我們想讓ToolBar具有和ActionBar相同的功能,就需要調(diào)用setSupportActionBar(tool_bar),我們也可以通過重寫Activity的onCreateOptionsMenu在ToolBar上添加菜單項(xiàng),重寫Activity的onOptionsItemSelected來實(shí)現(xiàn)菜單項(xiàng)的點(diǎn)擊事件,具體代碼如下。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(tool_bar)
//設(shè)置是否允許展示返回按鈕
supportActionBar?.setDisplayHomeAsUpEnabled(true)
//設(shè)置自定義返回按鈕的圖片
supportActionBar?.setHomeAsUpIndicator(R.mipmap.ic_arrow)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main_menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
}
R.id.title -> {
}
R.id.content -> {
}
R.id.scroll_menu -> {
}
}
return true
}
}
上面我們通過setDisplayHomeAsUpEnabled(true)設(shè)置返回按鈕展示,可以在onOptionsItemSelected中監(jiān)聽返回按鈕的點(diǎn)擊,其id默認(rèn)為android.R.id.home。ToolBar的使用還是非常簡單的。
2、滑動(dòng)菜單DrawLayout
DrawerLayout中允許放兩個(gè)子控件,第一個(gè)子控件表示展示的主屏幕內(nèi)容,第二個(gè)子控件表示滑動(dòng)菜單中的內(nèi)容,使用layout_gravity來表示滑動(dòng)菜單的位置。注意:DrawLayout并不是只允許放兩個(gè)控件,可以放多個(gè),只不過第一個(gè)控件為主屏內(nèi)容,其他子控件可以通過layout_gravity來設(shè)置菜單展示的位置。
<?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">
<androidx.appcompat.widget.Toolbar
android:id="@+id/tool_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/purple_500"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/draw_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="主界面" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="@android:color/white"
android:text="Hello World!" />
</androidx.drawerlayout.widget.DrawerLayout>
</LinearLayout>
這樣我們就通過布局文件就實(shí)現(xiàn)了滑動(dòng)菜單,在屏幕左側(cè)向右滑動(dòng)就可以讓滑動(dòng)菜單顯示出來。

如果我們想實(shí)現(xiàn)滑動(dòng)菜單時(shí)和導(dǎo)航按鈕進(jìn)行聯(lián)動(dòng)該怎么實(shí)現(xiàn)呢?其實(shí)很簡單,修改下MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(tool_bar)
//設(shè)置是否允許展示返回按鈕
supportActionBar?.setDisplayHomeAsUpEnabled(true)
//設(shè)置自定義返回按鈕的圖片
// supportActionBar?.setHomeAsUpIndicator(R.mipmap.ic_arrow)
val drawerToggle = ActionBarDrawerToggle(
this,
draw_layout,
tool_bar,
R.string.draw_open,
R.string.draw_close
)
drawerToggle.syncState()
draw_layout.addDrawerListener(drawerToggle)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main_menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
}
R.id.title -> {
}
R.id.content -> {
}
R.id.scroll_menu -> {
}
}
return true
}
}
代碼很簡單:
- 首先調(diào)用
supportActionBar?.setDisplayHomeAsUpEnabled(true)展示導(dǎo)航按鈕(其id為android.R.id.home); - 然后創(chuàng)建
ActionBarDrawerToggle對象( DrawerLayout.DrawerListener的子類); - 然后
draw_layout.addDrawerListene()設(shè)置監(jiān)聽; -
最后記得調(diào)用drawerToggle.syncState()進(jìn)行狀態(tài)同步,否則無效。
這樣就實(shí)現(xiàn)了滑動(dòng)菜單和導(dǎo)航按鈕的聯(lián)動(dòng)。
3、NavigationView
剛才我們直接通過TextView來作為滑動(dòng)菜單的頁面,其實(shí)Google提供了專門的控件NavigationView來實(shí)現(xiàn)滑動(dòng)菜單。NavigationView中有兩個(gè)屬性app:headerLayout和app:menu,app:headerLayout是用來展示菜單的頭布局的,app:menu是用來展示菜單項(xiàng)的。使用起來很簡單,這里我們修改下activity_main的布局。
<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">
<androidx.appcompat.widget.Toolbar
android:id="@+id/tool_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/purple_500"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/draw_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="主界面" />
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigation_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/head_navigation"
app:menu="@menu/main_menu" />
</androidx.drawerlayout.widget.DrawerLayout>
</LinearLayout>
效果如下:

可以通過
navigation_view.setNavigationItemSelectedListener()監(jiān)聽菜單中item的點(diǎn)擊事件。
4、CoordinatorLayout
CoordinatorLayout是一個(gè)加強(qiáng)版的FrameLayout,可以監(jiān)聽所有子控件的事件,并自動(dòng)幫我們實(shí)現(xiàn)最合理的響應(yīng)。先看個(gè)效果

可以看到FloatingActionButton被Snackbar遮住了,如果能讓CoordinatorLayout監(jiān)聽到Snackbar的彈出事件,那么它會自動(dòng)將FloatingActionButton上移。看下使用CoordinatorLayout之后的布局文件
<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">
<androidx.appcompat.widget.Toolbar
android:id="@+id/tool_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/purple_500"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/draw_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="主界面" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/float_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@mipmap/ic_arrow" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigation_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/head_navigation"
app:menu="@menu/main_menu" />
</androidx.drawerlayout.widget.DrawerLayout>
</LinearLayout>
再次看下實(shí)現(xiàn)的效果:

可以看到FloatingActionButton不再被Snackbar遮住了,并且FloatingActionButton會根據(jù)SnackBar的彈出和消失上下移動(dòng)位置,SnackBar并不是CoordinatorLayout的子控件,它是如何監(jiān)聽彈出和消失的呢?從Snackbar的用法不難看出:
Snackbar.make(float_button, text, duration).show()
第一個(gè)參數(shù)我們傳入就是FloatingActionButton,而FloatingActionButton是CoordinatorLayout的子類,因此可以監(jiān)聽到彈出事件。如果我們傳入的不是
FloatingActionButton而是DrawLayout則無法監(jiān)聽到SnackBar的彈出和隱藏。
5、AppBarLayout
先看個(gè)效果

從上面效果可以看出,當(dāng)滑動(dòng)控件向上滑動(dòng)時(shí),隱藏ToolBar,向下滑動(dòng)時(shí)展示ToolBar。實(shí)現(xiàn)這個(gè)效果就需要使用AppBarLayout,它是一個(gè)垂直方向的LinearLayout。實(shí)現(xiàn)步驟如下:
- 1、使用AppBarLayout包裹ToolBar
- 2、給滑動(dòng)控件指定app:layout_behavior="@string/appbar_scrolling_view_behavior"
將滑動(dòng)控件的滾動(dòng)事件告訴AppBarLayout - 3、在AppBarLayout接收到滾動(dòng)事件時(shí),通過
app:layout_scrollFlags="scroll|enterAlways|snap"設(shè)置其子控件如何去響應(yīng)滾動(dòng)事件。
其中scroll表示當(dāng)滑動(dòng)控件向上滑動(dòng)時(shí),ToolBar會跟著向上滑動(dòng)并隱藏;enterAlways表示滑動(dòng)控件向下滑動(dòng)時(shí),ToolBar會跟著向下滑動(dòng)并顯示;
snap表示ToolBar還未完全顯示或隱藏時(shí),會根據(jù)當(dāng)前滾動(dòng)的距離,自動(dòng)選擇顯示還是隱藏;
具體代碼如下:
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/tool_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/purple_500"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_scrollFlags="enterAlways|scroll|snap"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recy_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
6、可折疊式標(biāo)題欄
先來看個(gè)效果:

要實(shí)現(xiàn)這個(gè)效果就需要使用可折疊式標(biāo)題欄CollapsingToolbarLayout,它是作用在ToolBar基礎(chǔ)上的布局,使用它可以讓ToolBar實(shí)現(xiàn)的效果更加豐富。它被限定作為AppBarLayout的子布局,而AppBarLayout又必須是CoordinatorLayout的子布局,直接上代碼:
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="250dp">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:contentScrim="@color/purple_200"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/ic_pager1"
app:layout_collapseMode="parallax" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recy_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/darker_gray"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:src="@mipmap/ic_arrow"
app:layout_anchor="@id/app_bar"
app:layout_anchorGravity="bottom|end" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
可以看到給CollapsingToolbarLayout設(shè)置了屬性app:contentScrim="@color/purple_200"表示CollapsingToolbarLayout在趨于折疊或折疊之后的顏色,其中app:layout_scrollFlags之前我們使用過,只不過之前是設(shè)置在ToolBar上的,現(xiàn)在設(shè)置給了CollapsingToolbarLayout,其中exitUntilCollapsed表示CollapsingToolbarLayout隨著滾動(dòng)折疊之后保留在屏幕上不再移出屏幕。
我們在CollapsingToolbarLayout中定義了一個(gè)ImageView和一個(gè)ToolBar。很簡單,只不過app:layout_collapseMode比較陌生,它是用于指定CollapsingToolbarLayout在折疊過程中,子控件的折疊形式,給ToolBar設(shè)置成了pin表示折疊過程中位置保持始終不變,給ImageView設(shè)置成parallax隨著折疊產(chǎn)生一定的位移。
充分利用系統(tǒng)狀態(tài)欄空間
如果我們想使用系統(tǒng)狀態(tài)欄的空間可以使用如下代碼:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.decorView.systemUiVisibility=View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
window.statusBarColor=ContextCompat.getColor(this,android.R.color.transparent)
}else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
}
但是對于CoordinatorLayout、AppBarLayout、CollapsingToolbarLayout這種嵌套結(jié)構(gòu)的布局好像并不奏效,對于這種布局我們只需要將ImageView以及其所有父控件都增加android:fitsSystemWindows="true"才有效。