Material Design是由Google的设计师开发的一套界面设计语言,包含了视觉,运动,互动效果等特性.
1.Toolbar
任何一个新建项目都默认显示ActionBar的,这是根据项目中指定的主题显示,打开AndroidMainfest.xml
android:theme="@style/AppTheme"
这个AppTheme在res/values/styles.xml文件力定义的
<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>
现在我们准备使用Toolbar代替ActionBar,因此需要指定一个不带ActionBar的主题,
这里有Theme.AppCompat.NoActionBar和Theme.AppCompat.Light.NoActionBar这两个主题可选.
Theme.AppCompat.NoActionBar表示神色主题,Theme.AppCompat.Light.NoActionBar表示浅色主题
修改activity_main.xml中的代码,使用Toolbar来代替actionBar
<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"
tools:context=".MainActivity"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
</androidx.appcompat.widget.Toolbar>
接下来修改MainActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
}
xmlns:app指定了一个新的命名空间,这是由于许多Material属性是在新系统中新增的,为了兼容老系统,我们就不能使用android:attribute这样的写法,而是应该用app:attribute。
我们使用了android:theme属性,将Toolbar主题指定成了ThemeOverlay.AppCompat.Dark.ActionBa。为了将弹出菜单项也变成神色主题,这里使用了app:popupTheme属性,单独将弹出的菜单指定成深色主题。
1.修改标题栏上的文字内容
<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"
android:networkSecurityConfig="@xml/network_config"
>
<activity android:name=".MainActivity"
android:label="Fruit"><!--修改这里-->
</activity>
</application>
*2.在Toolbar上添加action按钮
右键res目录->New->Directory,创建一个menu目录
右键menu文件夹->New->Menu resource file,创建一个toolbar.xml文件
<?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">
<item
android:id="@+id/backup"
android:icon="@drawable/backup"
android:title="Backup"
app:showAsAction="always"
></item>
<item
android:id="@+id/delete"
android:icon="@drawable/delete"
android:title="Delete"
app:showAsAction="ifRoom"
></item>
<item
android:id="@+id/settings"
android:icon="@drawable/settings"
android:title="Settings"
app:showAsAction="never"
></item>
</menu>
showAsAction主要有以下几种值可以选,always表示永远显示在Woolbar中,如果屏幕空间不够则不显示;ifRoom表示屏幕足够的情况下显示在Toolbar,不够的话就显示在菜单当中;never则表示永远显示在菜单当中。
修改MainaActivity中的代码
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.toolbar,menu)
return true
}
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.settings ->Toast.makeText(this,"Settings",Toast.LENGTH_SHORT).show()
}
return true
}
}
滑动菜单
1.DrawerLayout
修改activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
</androidx.appcompat.widget.Toolbar>
</FrameLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:text="This is menu"
android:background="#fff"
></TextView>
</androidx.drawerlayout.widget.DrawerLayout>
DrawerLayout中放置了两个直接子控件:第一个是FrameLayout,用于作为主屏幕中显示的内容,第二个子控件是一个TextView,用于作为滑动菜单中显示的内容。
第二个子控件有一点需要注意,layout_gravity这个属性是必须指定的,我们需要告诉他滑动菜单时在屏幕的左边还是右边。指定left表示菜单在左边,right表示菜单在右边。start表示会根据系统语言进行判断。
Material Design建议的做法是在Toolbar的最左边加入一个导航按钮
修改MainActivity中的代码
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
supportActionBar?.let {
it.setDisplayHomeAsUpEnabled(true)
it.setHomeAsUpIndicator(R.drawable.menu)
}
}
...
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item.itemId){
android.R.id.home -> drawerLayout.openDrawer(GravityCompat.START)
...
}
return true
}
}
Home按钮的id永远都是android.R.id.home。然后调用DrawerLayout的openDrawer()方法将滑动菜单展示出来。openDrawer()方法要求传入一个Gravity参数,为了保证这里的行为和XML中定义的一致。我们传入了GravityCompat.START
2.NavigationView
这个控件时Material库中提供的,需要引入开源项目CircleImageView.
打开app/build.gradle文件,在dependencies闭包中添加如下内容
dependencies {
...
implementation 'com.google.android.material:material:1.0.0'
implementation 'de.hdodenhof:circleimageview:3.0.1'
}
右击menu文件夹->New->Menu resource file,创建一个nav_menu.xml文件
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item
android:id="@+id/navCall"
android:icon="@drawable/call"
android:title="Call"></item>
<item
android:id="@+id/navFriends"
android:icon="@drawable/friends"
android:title="Friends"></item>
<item
android:id="@+id/navLocation"
android:icon="@drawable/location"
android:title="Location"></item>
<item
android:id="@+id/nav_mail"
android:icon="@drawable/mail"
android:title="Mail"></item>
<item
android:id="@+id/navTask"
android:icon="@drawable/task"
android:title="Task"></item>
</group>
</menu>
< group>表示一个组,checkableBehavior指定为single表示组中所有菜单只能单选
然后右击layout文件夹->New->Layout resource file创建一个nav_header.xml
<?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:padding="10dp"
android:background="@color/colorPrimary"
>
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/iconImage"
android:layout_width="70dp"
android:layout_height="70dp"
android:src="@drawable/icon"
android:layout_centerInParent="true"></de.hdodenhof.circleimageview.CircleImageView>
<TextView
android:id="@+id/mailText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="q327192368@163.com"
android:textColor="#fff"
android:textSize="14sp"
></TextView>
<TextView
android:id="@+id/userText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/mailText"
android:text="Peter Wang"
android:textColor="#fff"
android:textSize="14sp"
></TextView>
</RelativeLayout>
在RelativeLayout中CircleImageView是一个用于将图片圆形化的控件
修改activity_main.xml中的代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.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/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
</androidx.appcompat.widget.Toolbar>
</FrameLayout>
<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:menu="@menu/nav_menu"
app:headerLayout="@layout/nav_header"
></com.google.android.material.navigation.NavigationView>
</androidx.drawerlayout.widget.DrawerLayout>
通过app:menu和app:headerLayout属性将我们刚才准备好的menu和headerLayout设置进去
最后修改以下MainActivity中的代码
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
supportActionBar?.let {
it.setDisplayHomeAsUpEnabled(true)
it.setHomeAsUpIndicator(R.drawable.menu)
}
navView.setCheckedItem(R.id.navCall)
navView.setNavigationItemSelectedListener {
drawerLayout.closeDrawers()
true
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.toolbar,menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item.itemId){
android.R.id.home -> 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.settings -> Toast.makeText(this,"Settings", Toast.LENGTH_SHORT).show()
}
return true
}
}
这里先调用了NavigationView的setCheckedItem()方法将Call菜单项设置为默认选中。接着调用setNavigationItemSelectedListener()方法来者之一个菜单项选中时间的监听器,这里调用DrawerLayout的closeDrawers()方法,将滑动菜单关闭,并返回true表示此事件已经被处理。
悬浮按钮和可交互提示
1.FloatingActionButton
修改activity_main.xml文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.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/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
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/done"
></com.google.android.material.floatingactionbutton.FloatingActionButton>
</FrameLayout>
...
</androidx.drawerlayout.widget.DrawerLayout>
layout_gravity属性指定将这个控件放置在屏幕右下角,其中end的工作原理和之前的start是一样的。
elevation悬浮高度,高度值越大投影范围越大,投影效果越淡。
修改MainActivity中的代码,处理点击事件
fab.setOnClickListener{
Toast.makeText(this,"你点击了悬浮按钮",Toast.LENGTH_SHORT).show()
}
2.Snackbar
Snackbar并不是Toast的替代品,他们有着不同的应用场景
修改MainActivity中的代码
fab.setOnClickListener{
Snackbar.make(it,"删除数据!",Snackbar.LENGTH_SHORT).setAction("Undo"){
Toast.makeText(this,"数据恢复",Toast.LENGTH_SHORT).show()
}
.show()
}
这里调用了Snackbar的make()方法来创建,第一个参数只需传入一个View,Snackbar会使用这个View找到最外层的布局,用于展示信息。setAction()方法来设置一个动作。
3.CoordinatorLayout
CoordinatorLayout是一个加强版的FrameLayout,由AndroidX库提供,它可以监听所有子控件的各种事件,并自动帮我们做出最为合理的响应。
修改activity_main.xml中的代码
<androidx.drawerlayout.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/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
...
</androidx.coordinatorlayout.widget.CoordinatorLayout>
...
</androidx.drawerlayout.widget.DrawerLayout>
卡片式布局
1.MaterialCardView
MaterialCardView也是一个FrameLayout,只是额外提供了圆角和阴影等效果,看上去有立体效果。
基本用法:
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="4dp"
android:elevation="5dp">
<TextView
android:id="@+id/infoText"
android:layout_width="match_parent"
android:layout_height="match_parent">
</TextView>
</com.google.android.material.card.MaterialCardView>
app:cardCorneRadius属性指定卡片圆角的弧度。
在app/build.gradle文件中声明库的依赖
implementation 'androidx.recyclerview::recyclerview:1.0.0'
implementation 'com.github.bumptech.glide:glide:4.9.0'
修改atcivity_main.xml中的代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.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/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
...新增这里,添加recyclerview
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.recyclerview.widget.RecyclerView>
<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/done"
></com.google.android.material.floatingactionbutton.FloatingActionButton>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
...
</androidx.drawerlayout.widget.DrawerLayout>
创建一个Fruit实体类
class Fruit(val name:String,val imageId:Int) {
}
为Recyclerview指定一个自定义布局,在layout目录创建fruit_item.xml
<com.google.android.material.card.MaterialCardView 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="match_parent"
android:layout_height="100dp"
android:scaleType="centerCrop"></ImageView>
<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"
></TextView>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
ImageView中我们使用了scaleType属性,这个属性可以指定图片的缩放模式,centerCrop模式,它可以让图片保持原有比例填充满ImageView,并将超出屏幕的部分裁掉。
为RecyclerView准备一个适配器,新建FruitAdapter类。
class FruitAdapter(val context:Context,val fruitList: List<Fruit>):
RecyclerView.Adapter<FruitAdapter.ViewHolder>() {
inner class ViewHolder(view : View) :RecyclerView.ViewHolder(view){
val fruitImage:ImageView = view.findViewById(R.id.fruitImage)
val fruitName : TextView = view.findViewById(R.id.fruitName)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view =LayoutInflater.from(context).inflate(R.layout.fruit_item,parent,false)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val fruit =fruitList[position]
holder.fruitName.text=fruit.name
Glide.with(context).load(fruit.imageId).into(holder.fruitImage)
}
override fun getItemCount()= fruitList.size
}
调用Glide.with()方法帮我们压缩图片避免内存溢出。
最后修改MainActivity中的代码
class MainActivity : AppCompatActivity() {
val fruits = mutableListOf(Fruit("Banana",R.drawable.banana),Fruit("Apple",R.drawable.apple),
Fruit("Orange",R.drawable.orange),Fruit("cherry",R.drawable.cherry), Fruit
("Pear",R.drawable.pear), Fruit("Pineapple",R.drawable.pineapple)
)
val fruitList = ArrayList<Fruit>()
override fun onCreate(savedInstanceState: Bundle?) {
...
initFruits()
val layoutManager = GridLayoutManager(this,2)
recyclerView.layoutManager = layoutManager
val adapter = FruitAdapter(this,fruitList)
recyclerView.adapter=adapter
}
private fun initFruits(){
fruitList.clear()
repeat(50){
val index = (0 until fruits.size).random()
fruitList.add(fruits[index])
}
}
...
}
GridLayoutManger构造函数接收的参数第一个是Context第二个是列数。
但是我们的Toolbar被RecyclerView挡住了
2.AppBarLayout
AppBarLayout实际上是一个垂直方向的LinearLayout,内部做了很多滚动事件的封装。
修改activity_main.xml中的代码
<androidx.drawerlayout.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/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout <----在这儿里增加了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/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
</androidx.appcompat.widget.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"> <------在这里增加了behavior属性
</androidx.recyclerview.widget.RecyclerView>
...
</androidx.drawerlayout.widget.DrawerLayout>
将Toolbar嵌套在AppBarLayout中,第二部给RecyclerView指定一个布局行为
这样就不遮挡了,但我们还可以继续优化
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways|snap"> <-------增加属性
scroll表示当RecyclerView向上滑动的时候,Toolbar会向上滑动实现隐藏
enterAlways表示当RecyclerView向下滑动的时候,Toolbar会跟着下滑重现显示
snap表示当Toolbar还没有完全显示或隐藏时候,会根据当前滑动的距离自行选择
3.下拉刷新
SwipeRefreshLayout用于实现下拉刷新功能的核心类,将支持下拉刷新功能的组件放置到SwiperRefreshLayout中,就可以让这个控件支持下拉刷新。
修改actvity_main.xml中的代码
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swiperRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<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">
</androidx.recyclerview.widget.RecyclerView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
修改MainActivity中的代码
class MainActivity : AppCompatActivity() {
val fruits = mutableListOf(Fruit("Banana",R.drawable.banana),Fruit("Apple",R.drawable.apple),
Fruit("Orange",R.drawable.orange),Fruit("cherry",R.drawable.cherry), Fruit
("Pear",R.drawable.pear), Fruit("Pineapple",R.drawable.pineapple)
)
val fruitList = ArrayList<Fruit>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
//下拉刷新功能
swiperRefresh.setColorSchemeResources(R.color.colorPrimary)
swiperRefresh.setOnRefreshListener {
refreshFruits(adapter)
}
}
private fun refreshFruits(adapter: FruitAdapter){
thread {
Thread.sleep(2000)
runOnUiThread {
initFruits()
adapter.notifyDataSetChanged()
swiperRefresh.isRefreshing=false
}
}
}
...
}
调用SwiperRefreshLayout的setColorSchemeResource()方法来设置下拉刷新进度条的颜色。
接着调用setOnRefreshListener()方法来设置一个下拉刷新的监听器。
最后调用SwipeRefreshLayout的setRefreshing()发布方法并传入false,表示刷新事件结束,并隐藏进度条
可折叠式标题栏
1.CollapsingToolbarLayout
CollapsingToolbarLayout是不能独立存在的,它在设计的时候被限定只能作为AppBarLayout的直接子布局来使用,它被设计限定只能作为AppBarLayout的直接子布局来使用。而AppBarLayout又必须是coordinatorLayout的子布局。
1).首先使用CoordinatorLayout作为最外层布局1
<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=".MainActivity">
</androidx.coordinatorlayout.widget.CoordinatorLayout>
2).在CoordinatorLayout内嵌套AppBarLayout
<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=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="250dp">
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
接下来我们在AppBarLayout中在嵌套一个CollapsingToolbarLayout
<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=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="250dp">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsingToolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:contentScrim="@color/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed"></com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
app:contentScrim属性用于指定CollapsingToolbarLayout在折叠状态以及折叠后的背景色/
exitUntilCollapsed表示当CollapsingToolbarLayout随着滚动完成折叠之后就保留在界面上,不移出屏幕。
接下来在CollapsingToolbarLayout中定义标题栏的内容。
<ImageView
android:id="@+id/fruitImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax">
</ImageView>
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin">
</androidx.appcompat.widget.Toolbar>
app:layout_collapseMode用于指定控件在CollapsingToolbarLayout折叠模式
pin表示在折叠过程中保持不变
parallax表示在折叠过程中会产生一定的错位偏移。
接着在AppBarLayout下面添加NestedScrollView
<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=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="250dp">
...
</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">
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
NestedScrollView在ScrollView基础之上还增加了嵌套响应滚动事件的功能。
由于coordinatorLayout本身就可以响应滚动事件,因此我们在它内部就需要使用NestedScrollView或RecyclerView这样的布局。
不管是ScrollView还是NestedScrollView,他们内部只允许存在一个直接子布局,通常会先嵌套一个LinearLayout。
并在LinearLayout中放置具体内容
...
<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"
>
<com.google.android.material.card.MaterialCardView
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"></TextView>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
最后添加在加入表示评论的悬浮按钮
...
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
...
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:src="@drawable/commit"
app:layout_anchor="@id/appBar"
app:layout_anchorGravity="end|bottom"
></com.google.android.material.floatingactionbutton.FloatingActionButton>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
FloatingActionButton中使用app:layout_anchor属性指定一个锚点,设置为AppBarLayout,这样悬浮按钮就会出现在水果标题栏的区域内。
修改FruitActivity中的代码
class FruitActivity : AppCompatActivity() {
companion object{
const val FRUIT_NAME = "fruit_name"
const val FRUIT_ID ="fruit_image_id"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_fruit)
val fruitName = intent.getStringExtra(FRUIT_NAME) ?:""
val fruitImageId = intent.getIntExtra(FRUIT_ID,0)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
collapsingToolbar.title = fruitName
Glide.with(this).load(fruitImageId).into(fruitImageView)
fruitContentText.text= generateFruitContent(fruitName)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item.itemId){
android.R.id.home ->{
finish()
return true
}
}
return super.onOptionsItemSelected(item)
}
private fun generateFruitContent(fruitName : String) =fruitName.repeat(500)
}
最后修改FruitAdapter中的代码
...
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view =LayoutInflater.from(context).inflate(R.layout.fruit_item,parent,false)
val holder =ViewHolder(view)
holder.itemView.setOnClickListener {
val position = holder.adapterPosition
val fruit =fruitList[position]
val intent = Intent(context,FruitActivity::class.java).apply {
putExtra(FruitActivity.FRUIT_NAME,fruit.name)
putExtra(FruitActivity.FRUIT_ID,fruit.imageId)
}
context.startActivity(intent)
}
return holder
}
2.充分利用系统状态栏空间
借助android:fitsSystemWindows=“true”,需要将ImageView布局结构中所有父布局都设置上
<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=".MainActivity"
android:fitsSystemWindows="true" <-------------------------
>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="250dp"
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/ThemeOverlay.AppCompat.Dark.ActionBar"
app:contentScrim="@color/colorPrimary"
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"> <-------------------------
</ImageView>
...
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
...
</androidx.coordinatorlayout.widget.CoordinatorLayout>
我们还必须在程序主题中将状态栏颜色指定成透明色才行,在主题中将android:statusBarColor属性的值指定成@android:color/transparent
打开res/values/stytles.xml文件
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
<style name="FruitActivityTheme" parent="AppTheme"> <-----
<item name="android:statusBarColor">@android:color/transparent</item> <-----新增
</style> <------
</resources>
最后修改AndroidManifest.xml让FruitActivity使用这个主题
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.materialdesign">
<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=".FruitActivity"
android:label="@string/title_activity_fruit"
android:theme="@style/FruitActivityTheme"></activity> <--------
...
</application>
</manifest>
Kotlin课堂:编写好用的工具方法
1.
fun max(vararg nums : Int) :Int{
var maxNum = Int.MIN_VALUE
for(num in nums){
maxNum = max(maxNum,num)
}
return maxNum
}
在max()函数的参数声明中使用了vararg关键字,表示它可以接收任意多个参数。
fun <T : Comparable <T>> max(vararg nums : T) : T{
if(nums.isEmpty()) throw RuntimeException("Params can not be empty")
var maxNum = nums[0]
for(num in nums){
if(num > maxNum)
maxNum =num
}
}
return maxNum
}
Java中规定,所有类型的数字都是可以比较的,因此必须实现Comparable接口。我们借助泛型可以修改成接收任意多个实现Comparable接口的参数
2.简化Toast的用法
fun String.showToast(context : Context,duration: Int =Toast.LENGTH_SHORT) {
Toast.makeText(context,this,duration).show()
}
这样调用即可
"This is Toast".showToast(context)
3.简化Snackbar的用法
fun View.showSnackbar(text : String,actionText : String? =null,duration : Int = Snackbar.LENGTH_SHORT,
block:(() -> Unit)? = null){
val snackbar =Snackbar.make(this,text,duration)
if(actionText !=null&&block !=null){
snackbar.setAction(actionText){
block()
}
}
snackbar.show()
}
这样调用即可
view.showSnackbar("This is Snackbar","Action"){
//处理具体逻辑
}
Git时间:版本控制工具的高级用法
1.分支的用法
创建一个分支
git branch version1.0
输入git branch这个命令检查一下
切换到version1.0这个分支上
git checkout version1.0
完成分支合并代码
git checkout master
git merge version1.0
当我们不需要分支时,将它删除
git branch -D version1.0
2.远程版本库协作
比如现在有一个远程版本库的Git地址时https://github.com/example/test.git,就可以使用以下命令将代码下载到本地
git clone https://github.com/example/test.git
把本地修改的内容同步到远程版本库,借助push命令来完成
git push origin master
origin指定的时远程版本库的git地址,master部分指定的是同步到哪一个分支上。
将远程版本库上修改同步到本地。Git提供两种命令,分别是fetch和pull。
git fetch origin master
执行完之后,远程版本库上修改同步到本地,但并不会合并到任何分支上,而是会村梵高origin/master分支上,这时我们可以通过diff命令来查看远程版本库上到底修改了那些东西
git diff origin/master
之后在调用merge命令将origin/master分支上的修改合并到主分支上即可
git merge origin/master
而pull命令则是相当于将fetch和merge这两个命令放在一起执行,他能从远程版本库上获取最新代码并且合并到本地
git pull origin master