1、ToolBar
ToolBar是为了替代ActionBar的,ActionBar被限定只能位于Activity的顶部,不能实现MaterialDesign的效果,所以Google不建议使用ActionBar,而ToolBar不仅继承了ActionBar的所有功能,而且灵活性很高,可以配合其他控件完成MaterialDesign效果。
我们新建的项目默认都会显示ActionBar的,这是根据项目中设置的主题现实的。看下AndroidManifest.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"
指定了项目的主题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>
可以看到这里定义了一个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
指定了一个新命名空间,这是因为MaterialDesign中属性是新系统中新增的,老系统中不存在,那么为了兼容老系统,我们不能使用android:attribute这样的写法,而是使用app:attribute。
由于我们设置了项目的主题为浅色主题,那么ToolBar的主题也为浅色主题,那么ToolBar上面的元素则为深色,如果我们想让ToolBar上的元素为浅色,就需要通过android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
设置ToolBar的主题为深色。这就会使弹出的菜单项为深色,所以为了美观通过
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
设置菜单项为浅色主题,由于app:popupTheme
是新增的属性,所以为了兼容需要使用app:attribute
的写法。
如果我们想让ToolBar具有和ActionBar相同的功能,就需要调用setSupportActionBar(tool_bar)
,我们也可以通过重写Activity的onCreateOptionsMenu
在ToolBar上添加菜单项,重写Activity的onOptionsItemSelected
来实现菜单项的点击事件,具体代码如下。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(tool_bar)
//设置是否允许展示返回按钮
supportActionBar?.setDisplayHomeAsUpEnabled(true)
//设置自定义返回按钮的图片
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)
设置返回按钮展示,可以在onOptionsItemSelected
中监听返回按钮的点击,其id默认为android.R.id.home
。ToolBar的使用还是非常简单的。
2、滑动菜单DrawLayout
DrawerLayout中允许放两个子控件,第一个子控件表示展示的主屏幕内容,第二个子控件表示滑动菜单中的内容,使用layout_gravity
来表示滑动菜单的位置。注意:DrawLayout并不是只允许放两个控件,可以放多个,只不过第一个控件为主屏内容,其他子控件可以通过layout_gravity
来设置菜单展示的位置。
<?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>
这样我们就通过布局文件就实现了滑动菜单,在屏幕左侧向右滑动就可以让滑动菜单显示出来。
image.png
如果我们想实现滑动菜单时和导航按钮进行联动该怎么实现呢?其实很简单,修改下MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(tool_bar)
//设置是否允许展示返回按钮
supportActionBar?.setDisplayHomeAsUpEnabled(true)
//设置自定义返回按钮的图片
// 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
}
}
代码很简单:
- 首先调用
supportActionBar?.setDisplayHomeAsUpEnabled(true)
展示导航按钮(其id为android.R.id.home); - 然后创建
ActionBarDrawerToggle
对象( DrawerLayout.DrawerListener的子类); - 然后
draw_layout.addDrawerListene()
设置监听; -
最后记得调用drawerToggle.syncState()进行状态同步,否则无效。
这样就实现了滑动菜单和导航按钮的联动。
3、NavigationView
刚才我们直接通过TextView来作为滑动菜单的页面,其实Google提供了专门的控件NavigationView来实现滑动菜单。NavigationView中有两个属性app:headerLayout和app:menu
,app:headerLayout
是用来展示菜单的头布局的,app:menu
是用来展示菜单项的。使用起来很简单,这里我们修改下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()
监听菜单中item的点击事件。
4、CoordinatorLayout
CoordinatorLayout是一个加强版的FrameLayout,可以监听所有子控件的事件,并自动帮我们实现最合理的响应。先看个效果
image.png
可以看到FloatingActionButton被Snackbar遮住了,如果能让CoordinatorLayout监听到Snackbar的弹出事件,那么它会自动将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>
再次看下实现的效果:
可以看到FloatingActionButton不再被Snackbar遮住了,并且FloatingActionButton会根据SnackBar的弹出和消失上下移动位置,SnackBar并不是CoordinatorLayout的子控件,它是如何监听弹出和消失的呢?从Snackbar的用法不难看出:
Snackbar.make(float_button, text, duration).show()
第一个参数我们传入就是FloatingActionButton,而FloatingActionButton是CoordinatorLayout的子类,因此可以监听到弹出事件。如果我们传入的不是
FloatingActionButton而是DrawLayout则无法监听到SnackBar的弹出和隐藏。
5、AppBarLayout
先看个效果
从上面效果可以看出,当滑动控件向上滑动时,隐藏ToolBar,向下滑动时展示ToolBar。实现这个效果就需要使用AppBarLayout,它是一个垂直方向的LinearLayout。实现步骤如下:
- 1、使用AppBarLayout包裹ToolBar
- 2、给滑动控件指定app:layout_behavior="@string/appbar_scrolling_view_behavior"
将滑动控件的滚动事件告诉AppBarLayout - 3、在AppBarLayout接收到滚动事件时,通过
app:layout_scrollFlags="scroll|enterAlways|snap"
设置其子控件如何去响应滚动事件。
其中scroll
表示当滑动控件向上滑动时,ToolBar会跟着向上滑动并隐藏;enterAlways
表示滑动控件向下滑动时,ToolBar会跟着向下滑动并显示;
snap
表示ToolBar还未完全显示或隐藏时,会根据当前滚动的距离,自动选择显示还是隐藏;
具体代码如下:
<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、可折叠式标题栏
先来看个效果:
要实现这个效果就需要使用可折叠式标题栏CollapsingToolbarLayout,它是作用在ToolBar基础上的布局,使用它可以让ToolBar实现的效果更加丰富。它被限定作为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
设置了属性app:contentScrim="@color/purple_200"
表示CollapsingToolbarLayout
在趋于折叠或折叠之后的颜色,其中app:layout_scrollFlags
之前我们使用过,只不过之前是设置在ToolBar上的,现在设置给了CollapsingToolbarLayout
,其中exitUntilCollapsed
表示CollapsingToolbarLayout随着滚动折叠之后保留在屏幕上不再移出屏幕。
我们在CollapsingToolbarLayout
中定义了一个ImageView和一个ToolBar。很简单,只不过app:layout_collapseMode
比较陌生,它是用于指定CollapsingToolbarLayout
在折叠过程中,子控件的折叠形式,给ToolBar设置成了pin表示折叠过程中位置保持始终不变,给ImageView设置成parallax随着折叠产生一定的位移。
充分利用系统状态栏空间
如果我们想使用系统状态栏的空间可以使用如下代码:
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这种嵌套结构的布局好像并不奏效,对于这种布局我们只需要将ImageView以及其所有父控件都增加android:fitsSystemWindows="true"才有效。
网友评论