Android Material Design
- 今天就来谈谈 android 给我们提供的 Material 库的好用又好看的组件
ToolBar
-
ToolBar这个是来替代ActionBar的
具体的做法很简单:
-
将主题的ActionBar设置为没有
-
然后在需要ToolBar的地方 放在他的布局文件里 在他的创建视图的时候 调用 **setSupportActionBar(binding.toolbar) **这个方法就可以了
传入的就是在布局文件里定义的ToolBar
看代码
-
- 圈出来的地方就是要改变的地方
然后在代码中设置
setSupportActionBar(binding.toolbar) // 将ToolBar 用作 ActionBar
- 由于ActionBar只有Activity才有 所以这个方法也只有Activity才有=
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/design_default_color_primary"
android:theme="@style/Theme.AppCompat.DayNight.DarkActionBar" //指定顶部栏是暗色主题
app:popupTheme="@style/Theme.AppCompat.Light"/> //指定弹出来的菜单是浅色主题
- 既然ActionBar有Action按键 那么ToolBar在Activity中也有 调用的方法还是一样的
//这个方法是将菜单布局加载到ToolBar上
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.toobal,menu)
return true //返回TRUE表示加载完成
}
//在Activity中重写这个方法,就可以实现ToolBar上有action按键的点击事件了
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item.itemId){
R.id.backup -> Toast.makeText(this,"backup",Toast.LENGTH_SHORT).show()
R.id.delete -> Toast.makeText(this,"delete",Toast.LENGTH_SHORT).show()
R.id.setting -> Toast.makeText(this,"setting",Toast.LENGTH_SHORT).show()
}
return true//表示事件点击处理了
}
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/backup"//这里
android:icon="@drawable/ic_backup"
android:title="BackUp"
app:showAsAction="always"/> <!--表示永远显示到ToolBar上 但是空间不够就不显示-->
<item
android:id="@+id/delete"///这里
android:icon="@drawable/ic_delete"
android:title="Delete"
app:showAsAction="ifRoom"/> <!--表示 如果空间足够才显示,空间不够就不显示-->
<item
android:id="@+id/setting"//这里
android:icon="@drawable/ic_settings"
android:title="Delete"
app:showAsAction="never"/> <!--不在ToolBar上显示-->
</menu>
- ToolBar设置一个homeAction按键 默认的图标是一个返回键
- 但是我们可以自定义修改
supportActionBar?.let{
it.setDisplayHomeAsUpEnabled(true) //这个表示显示Home Action按键
it.setHomeAsUpIndicator(R.drawable.align) //这个重新设置Home Action的图标
}
- 然后就是设置这个按键的点击事件了
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item.itemId){
android.R.id.home -> binding.drawerLayout.openDrawer(GravityCompat.START) //看这里
R.id.backup -> Toast.makeText(this,"backup",Toast.LENGTH_SHORT).show()
R.id.delete -> Toast.makeText(this,"delete",Toast.LENGTH_SHORT).show()
R.id.setting -> Toast.makeText(this,"setting",Toast.LENGTH_SHORT).show()
}
return true
}
- 只要他还是个ToolBar 那么他的点击事件的响应就要在 onOptionsItemSelected 这个方法进行处理
- 注意 : home的id 是系统指定的 : android.R.id.home
滑动菜单 — DrawerLayout
-
就是将菜单选项隐藏起来,不放置在主屏幕上,通过滑动的方式来显示
-
DrawerLayout 只允许有两个直接子控件 第一个控件 : 在主屏幕中显示内容,第二个子控件显示滑动菜单中的内容
-
唯一值得注意的是 必须指定 第二个控件的layout_gravity 并且只能是一个方向
NavigationView
- 这里先说明一些 无关的东西
- NavigationView 是 Material库中的所以的添加依赖
dependencies {
implementation 'com.google.android.material:material:1.5.0'
}
- 因为使用了NavigationView 使用到了Material库中的东西 就得将应用的主题换成 Material的
- 具体就是
- 这个就是整个应用的主体
-
改成 Theme.MaterialComponents.DayNight.DarkActionBar
-
现在继续来介绍 NavigationView
他主要分为两个部分 一个 headerlayout 一个是 menu
通过名字可以发现 : headerlayout 是一个布局 menu是一个菜单
看代码
<com.google.android.material.navigation.NavigationView android:id="@+id/navView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="start" <!--注意是app的命名空间--> app:menu="@menu/nav_menu" //引用了一个menu app:headerLayout="@layout/nav_header"/> <!-- 引用了一个布局-->
//nav_menu <menu xmlns:android="http://schemas.android.com/apk/res/android"> <group android:checkableBehavior="single"> //这里的single的意思是里面的选项只能单选 <item android:id="@+id/navCall" android:icon="@drawable/nav_call" android:title="Call"/> <item android:id="@+id/navFriends" android:icon="@drawable/nav_friends" android:title="Friends"/> <item android:id="@+id/navLocation" android:icon="@drawable/nav_location" android:title="Location"/> <item android:id="@+id/navMail" android:icon="@drawable/nav_mail" android:title="Mail"/> <item android:id="@+id/navTask" android:icon="@drawable/nav_task" android:title="Task"/> </group> </menu> //nav_header <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="180dp" android:background="@color/design_default_color_primary"> <de.hdodenhof.circleimageview.CircleImageView android:id="@+id/iconImage" android:layout_width="70dp" android:layout_height="70dp" android:src="@drawable/nav_icon" android:layout_centerInParent="true"/> <TextView android:id="@+id/mailText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:text="tonygreendev@gmail.com" android:textColor="#FFF" android:textSize="14sp"/> <TextView android:id="@+id/userText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@id/mailText" android:text="Tony Green" android:textColor="#FFF" android:textSize="14sp"/> </RelativeLayout>
- 然后为菜单选项键设置监听事件
binding.navView.setCheckedItem(R.id.navCall) //这个来设置默认的选择 binding.navView.setNavigationItemSelectedListener { //设置监听事件的 when(it.itemId){ R.id.navCall -> Toast.makeText(this,"电话",Toast.LENGTH_SHORT).show() R.id.navFriends -> Toast.makeText(this,"朋友",Toast.LENGTH_SHORT).show() R.id.navLocation -> Toast.makeText(this,"地址",Toast.LENGTH_SHORT).show() R.id.navMail -> Toast.makeText(this,"邮箱",Toast.LENGTH_SHORT).show() } binding.drawerLayout.closeDrawers()//关闭滑动菜单 true//表示事件已经处理 }
悬浮式按键 — FloatingActionButton
-
这个可以做一种悬浮的效果 而且还有阴影
-
这个按键没有什么特别的 还是一个按键 有这样几个属性值得关注
-
layout_gravity : 可以指定这个按键位于他父控件的那个位置 前提是他的父容器支持
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:src="@drawable/ic_comment"//指定按键的图标
app:layout_anchor="@id/appBar" //指定锚点
app:layout_anchorGravity="bottom|end"/>//位于锚点的位置
他的点击事件的监听很简单和按键基本没啥区别
binding.fab.setOnClickListener { view ->
/*这个等下介绍*/ Snackbar.make(view,"Data Deleted",Snackbar.LENGTH_SHORT).setAction("Undo"){
Toast.makeText(this,"Data restored",Toast.LENGTH_SHORT).show()
}.show()
}
Snackbar — 可以交互的Toast
- 众所周知Toast只能显示通知不能和用户交互 而SnackBar就是既可以和用户交互又可以显示同时显示通知
Snackbar.make(view,"Data Deleted",Snackbar.LENGTH_SHORT)/*这一部分就是和Toast很类似 Data Deleted 是显示的内容 Snackbar.LENGTH_SHORT显示的时长view: 这个注意一下*/.setAction("Undo"){
//这一部分就是设置的一个按键 (Undo是名字)
//lambda是点击的响应逻辑
Toast.makeText(this,"Data restored",Toast.LENGTH_SHORT).show()
}.show()
- view : 是将一个视图传给他 他会自动去找根视图的,来展示信息 这个view是用来指定 Snackbar是基于谁触发的
CoordinatorLayout
- 这个就是一个加强版的FrameLayout 他可以监听子控件的各种事件并将我们作出合理的响应
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--这个布局的作用是 悬浮按键-->
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@drawable/ic_done"
android:elevation="8dp"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
-
这样就可以监听了悬浮按键的点击事件
在上面的代码中我们点击按键会弹出一个通知 那么这个时候就会自动然悬浮按键上移 流出一个通知的位置
卡片式布局 — MaterialCardView
- 这个布局的实质也还是FrameLayout 只是提供了阴影和圆角的效果
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_margin="5dp"
app:cardCornerRadius="4dp" //这个是用来指定圆角的大小的 >
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/fruitImage"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/fruitName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="5dp"
android:textSize="16sp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
AppBarLayout
- 实际上是一个垂直方向上的LinearLayout 内部做了很多滚动事件的封装
- 滚动事件的处理要用到两个布局
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/design_default_color_primary"
android:theme="@style/Theme.AppCompat.DayNight.DarkActionBar"
app:popupTheme="@style/Theme.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways|snap" //这个就是用来指定滚动时ToolBar的显示 />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
- 在要监听的滚动事件的组件上加上 app:layout_behavior="@string/appbar_scrolling_view_behavior" 就可以了 那么当RecyclerView 滚动的时候 APPBar会作出相应的响应
- scroll — 滚动是消失
- enterAlways — 向上移动时显示
- snap — 隐藏或显示一部分时 自动判断是显示还是隐藏
SwipeRefershLayout —下拉刷新的布局
-
想要实现下拉刷新的效果只需要在需要的布局外套一个 SwipeRefershLayout 这个布局就可以
-
注意 : 必须添加依赖 :
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
-
具体的刷新逻辑得靠我们自己来实现
private fun refreshFruits(adapter:FruitAdapter){
thread{
Thread.sleep(2000)
runOnUiThread {
initFruit()
adapter.notifyDataSetChanged() //这个就是让列表项全部刷新
binding.swipeRefresh.isRefreshing = false //这个的意思是刷新完成后 隐藏刷新的那个图标
}
}
}
//设置刷新的处理时间
binding.swipeRefresh.setOnRefreshListener {
refreshFruits(binding.recyclerView.adapter as FruitAdapter)
}
可折叠的标题栏 — CollapsingToolbarLayout
- 是一个作用域Toolbar基础之上的布局 它不仅仅是展示一个标题了还可以有很多的效果
- 但是 : 他不能单独存在 他只能是AppbarLayout的直接子布局 而 APPBarLayout又只能是CoordinatorLayout的直接子布局
<androidx.coordinatorlayout.widget.CoordinatorLayout
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"
tools:context=".FruitActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="150dp" // 这个可以随便指定
android:fitsSystemWindows="true">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsingToolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/Theme.AppCompat.DayNight.DarkActionBar"
app:contentScrim="@color/design_default_color_primary"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:fitsSystemWindows="true">
<ImageView
android:id="@+id/fruitImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax" //这里
android:fitsSystemWindows="true"/>
<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>
- app:layout_collapseMode=“parallax” 这个属性是来指定折叠时的动画效果 parallax 产生一定的错位偏移 pin 就是位置保持不变
- app:contentScrim="@color/design_default_color_primary" : 用来指定趋于折叠状态时 和 折叠时的状态的背景色
- app:layout_scrollFlags=“scroll|exitUntilCollapsed” 因为 CollapsingToolbarLayout 是APPBarLayout的子布局 所以他会根据APPBarLayout 监听到滚动事件来对子布局产出一定的响应 exitUntilCollapsed 这个就是折叠完成后就不在移出屏幕
NestedScrollView
- 这个视图只是在ScrollView的基础上可以嵌套在一个可以响应滚动事件的的功能
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
tools:context=".FruitActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="150dp"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsingToolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/Theme.AppCompat.DayNight.DarkActionBar"
app:contentScrim="@color/design_default_color_primary"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:fitsSystemWindows="true">
<ImageView
android:id="@+id/fruitImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"
android:fitsSystemWindows="true"/>
<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.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> //这里就是设置的滚动的时间
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginTop="35dp"
app:cardCornerRadius="4dp">
<TextView
android:id="@+id/fruitContentText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"/>
</androidx.cardview.widget.CardView>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<!--主要看这里!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!-->
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:src="@drawable/ic_comment"
app:layout_anchor="@id/appBar"
app:layout_anchorGravity="bottom|end"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
- **app:layout_behavior="@string/appbar_scrolling_view_behavior" ** 这个滚动的事件会被APPBarLayout监听到 然后他的子控件对这个滚动的事件能作出响应
- 重复利用状态栏的空间 : 可以设置属性 : android:fitsSystemWindows=“true” 来让控件出现在状态栏中 如果你想让一个控件在状态栏中显示 那么你就得让他的全部父控件都添加这个属性