一、效果图
二、实现
布局界面 ``` <?xml version=”1.0” encoding=”utf-8”?>
<RelativeLayoutandroid:layout_width="match_parent"android:layout_height="220dp"app:layout_collapseMode="parallax" ><ImageViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:src="@mipmap/wuhuang"android:scaleType="centerCrop"/>
</RelativeLayout><androidx.appcompat.widget.Toolbarandroid:id="@+id/tb_mt_toolbar"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"app:layout_collapseMode="pin"app:contentInsetStart="0dp"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:id="@+id/iv_mt_back"android:layout_width="60dp"android:layout_height="match_parent"android:src="@mipmap/ic_navigation_back_white"android:scaleType="center"/><TextViewandroid: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;@Overridepublic 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 == -492State.COLLAPSED -> {tv_mt_title.text = "个人中心"}//展开 i == 0State.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 - 1var 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,效果就像哔哩哔哩播放界面,播放时滚动不折叠。

