一、效果图
二、实现
布局界面 ``` <?xml version=”1.0” encoding=”utf-8”?>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="220dp"
app:layout_collapseMode="parallax" >
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@mipmap/wuhuang"
android:scaleType="centerCrop"/>
</RelativeLayout>
<androidx.appcompat.widget.Toolbar
android:id="@+id/tb_mt_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:contentInsetStart="0dp"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_mt_back"
android:layout_width="60dp"
android:layout_height="match_parent"
android:src="@mipmap/ic_navigation_back_white"
android:scaleType="center"/>
<TextView
android:id="@+id/tv_mt_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="膜拜单车"
android:textSize="17sp"
android:textColor="@color/red_FF6D84"
android:layout_centerInParent="true"
android:background="@color/blue_74D3FF"
/>
</RelativeLayout>
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_scrolling"/>
- 2. AppBarStateChangeListener
public abstract class AppBarStateChangeListener implements AppBarLayout.OnOffsetChangedListener {
public enum State {
EXPANDED,
COLLAPSED,
IDLE
}
private State mCurrentState = State.IDLE;
@Override
public final void onOffsetChanged(AppBarLayout appBarLayout, int i) {
if (i == 0) {
if (mCurrentState != State.EXPANDED) {
onStateChanged(appBarLayout, State.EXPANDED,i);
}
mCurrentState = State.EXPANDED;
} else if (Math.abs(i) >= appBarLayout.getTotalScrollRange()) {
if (mCurrentState != State.COLLAPSED) {
onStateChanged(appBarLayout, State.COLLAPSED,i);
}
mCurrentState = State.COLLAPSED;
} else {
if (mCurrentState != State.IDLE) {
onStateChanged(appBarLayout, State.IDLE,i);
}
mCurrentState = State.IDLE;
}
onStateChangedAny(appBarLayout,i);
}
public abstract void onStateChanged(AppBarLayout appBarLayout, State state,int i);
public void onStateChangedAny(AppBarLayout appBarLayout, int i){
}
}
- 3. activity
class MeiTuanHomeActivity : AppCompatActivity(R.layout.activity_meituan_home) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState)
//tb_mt_toolbar.setNavigationOnClickListener { finish() }
iv_mt_back.setOnClickListener { finish() }
// ctl_mt_collapsing.run { // title = “个人中心” // setExpandedTitleColor(ContextCompat.getColor(this@MeiTuanHomeActivity,R.color.transparent)) // setCollapsedTitleTextColor(ContextCompat.getColor(this@MeiTuanHomeActivity,R.color.red)) // expandedTitleGravity = Gravity.CENTER_HORIZONTAL // collapsedTitleGravity = Gravity.CENTER_HORIZONTAL // }
// appbar_mt_appbar.addOnOffsetChangedListener(object : AppBarStateChangeListener(){ // override fun onStateChanged(appBarLayout: AppBarLayout?, state: State?, i: Int) { // LogUtils.e(i) // //对标题设置渐变,向上滑动时i —> 0—- -448,标题先是变不见,后又可见(alpha 0-1) // // when(state){ // //折叠 i == -492 // State.COLLAPSED -> { // alpha = 1f // } // //展开 i == 0 // State.EXPANDED -> { // alpha = 0f // } // //中间状态 // State.IDLE -> { // val a = - (i / 200) // val max = 0.3f.coerceAtLeast(a.toFloat()) // alpha = max // LogUtils.e(“alpha = $alpha ———max = $max”,”——alpha—->”) // } // } // tv_mt_title.alpha = alpha // } // // })
appbar_mt_appbar.addOnOffsetChangedListener(object : AppBarStateChangeListener() {
override fun onStateChanged(appBarLayout: AppBarLayout?, state: State?, i: Int) {
when (state) {
//折叠 i == -492
State.COLLAPSED -> {
tv_mt_title.text = "个人中心"
}
//展开 i == 0
State.EXPANDED -> {
tv_mt_title.text = "膜拜单车"
}
//中间状态
State.IDLE -> {
//LogUtils.e("alpha = $alpha ------max = $max","----alpha--->")
}
}
}
override fun onStateChangedAny(appBarLayout: AppBarLayout?, verticalOffset: Int) {
super.onStateChangedAny(appBarLayout, verticalOffset)
maxVerticalOffset = appBarLayout?.totalScrollRange?.toFloat()!!
val a = -(verticalOffset / maxVerticalOffset)//渐变从0 - 1
//LogUtils.e("verticalOffset = $verticalOffset ---- a = $a ---------max = $maxVerticalOffset")
if (-verticalOffset <= maxVerticalOffset / 2) {
title = "膜拜单车"
alpha = (1 + verticalOffset / (maxVerticalOffset / 2))
} else {
title = "个人中心"
alpha = -(1 + verticalOffset / (maxVerticalOffset / 2))
}
LogUtils.e("$verticalOffset-----${appBarLayout.totalScrollRange / 2 + verticalOffset}")
//减小判断的范围,在这个范围内调用 settext 方法会一直触发onOffsetChanged,减小范围来减少触发
if (appBarLayout.totalScrollRange / 2 + verticalOffset in -20..20) {
setToolbarTitle()
}
tv_mt_title.alpha = alpha
}
})
}
override fun onResume() {
super.onResume()
//appbar_mt_appbar.addOnOffsetChangedListener(this)
}
override fun onPause() {
super.onPause()
//appbar_mt_appbar.removeOnOffsetChangedListener(this)
}
var alpha = 0f//渐变从1 - 0,到达折叠布局中间,再从 0 - 1
var maxVerticalOffset = 0f//折叠布局最大高度
var title = ""
// override fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) { // if (appBarLayout != null) { // //确保折叠和展开状态时,标题一定是对的 // if (abs(verticalOffset) in -appBarLayout.totalScrollRange + 1 .. -appBarLayout.totalScrollRange + 5){//折叠状态 // tv_mt_title.text = “个人中心” // }else if (verticalOffset in -1 downTo -5){//展开状态 // tv_mt_title.text = “膜拜单车” // } // } // maxVerticalOffset = appBarLayout?.totalScrollRange?.toFloat()!! // val a = -(verticalOffset / maxVerticalOffset)//渐变从0 - 1 // //LogUtils.e(“verticalOffset = $verticalOffset —— a = $a ————-max = $maxVerticalOffset”) // if (-verticalOffset <= maxVerticalOffset / 2) { // title = “膜拜单车” // alpha = (1 + verticalOffset / (maxVerticalOffset / 2)) // } else { // title = “个人中心” // alpha = -(1 + verticalOffset / (maxVerticalOffset / 2)) // } // //Toast.makeText(this, “$verticalOffset”, Toast.LENGTH_SHORT).show() //// tv_mt_title.text = title // LogUtils.e(“$verticalOffset——-${appBarLayout.totalScrollRange / 2 + verticalOffset}”) // //减小判断的范围,在这个范围内调用 settext 方法会一直触发onOffsetChanged,减小范围来减少触发 // if (appBarLayout.totalScrollRange / 2 + verticalOffset in -20..20){ // setToolbarTitle() // } // tv_mt_title.alpha = alpha // }
private fun setToolbarTitle() {
LogUtils.e("settoolbar---$title")
tv_mt_title.text = title
}
}
- 4. 重点
在监听折叠布局时,使用 AppbarLayout 的addOnOffsetChangedListener方法,同时使用自定义抽象类AppBarStateChangeListener,一般使用这个方法就可以监听状态onStateChanged,可以很方便监听折叠和展开状态,缺点是无法监听到中间转态,所以又加了一个方法onStateChangedAny,可以在需要时调用。
监听时设置标题渐变度和文字内容,如果仅仅设置渐变度从 0-1,那么只要实现下面这个就可以了 val a = -(verticalOffset / maxVerticalOffset)//渐变从0 - 1。
如果想设置渐变度从 1-0 ,再从 0-1,只需要实现下面的方法,
if (-verticalOffset <= maxVerticalOffset / 2) { title = “膜拜单车” alpha = (1 + verticalOffset / (maxVerticalOffset / 2)) } else { title = “个人中心” alpha = -(1 + verticalOffset / (maxVerticalOffset / 2)) }
注意设置标题时,有个问题,在滚动的范围内设置调用 setText 方法会一直触发监听折叠的方法,原因我也不知道,如果有知道的,或者有更好的解决方法可以留言。我的解决方法是在设置改变标题时,减小改变的条件,在使用时不会一直处在这个条件下就不会一直触发onStateChangedAny方法,不过缺点是如果滑动太快就会无法监听到,所以要在onStateChanged里设置好折叠和展开的标题,确保最终标题准确。
如果出现监控失效的情况,可以采用下面这种方法监听:
override fun onResume() { super.onResume() //appbar_mt_appbar.addOnOffsetChangedListener(this) }
override fun onPause() {
super.onPause()
//appbar_mt_appbar.removeOnOffsetChangedListener(this)
}
``` 如果想让折叠布局不响应下面的滚动,可以这么设置nsvouter._isNestedScrollingEnabled = false,效果就像哔哩哔哩播放界面,播放时滚动不折叠。