一、效果图

仿摩拜个人中心界面 - 图1iShot2020-08-2200.16.59.gif

二、实现

    1. 布局界面 ``` <?xml version=”1.0” encoding=”utf-8”?>

  1. <RelativeLayout
  2. android:layout_width="match_parent"
  3. android:layout_height="220dp"
  4. app:layout_collapseMode="parallax" >
  5. <ImageView
  6. android:layout_width="match_parent"
  7. android:layout_height="match_parent"
  8. android:src="@mipmap/wuhuang"
  9. android:scaleType="centerCrop"/>
  1. </RelativeLayout>
  2. <androidx.appcompat.widget.Toolbar
  3. android:id="@+id/tb_mt_toolbar"
  4. android:layout_width="match_parent"
  5. android:layout_height="?attr/actionBarSize"
  6. app:layout_collapseMode="pin"
  7. app:contentInsetStart="0dp"
  8. >
  9. <RelativeLayout
  10. android:layout_width="match_parent"
  11. android:layout_height="match_parent">
  12. <ImageView
  13. android:id="@+id/iv_mt_back"
  14. android:layout_width="60dp"
  15. android:layout_height="match_parent"
  16. android:src="@mipmap/ic_navigation_back_white"
  17. android:scaleType="center"/>
  18. <TextView
  19. android:id="@+id/tv_mt_title"
  20. android:layout_width="wrap_content"
  21. android:layout_height="wrap_content"
  22. android:text="膜拜单车"
  23. android:textSize="17sp"
  24. android:textColor="@color/red_FF6D84"
  25. android:layout_centerInParent="true"
  26. android:background="@color/blue_74D3FF"
  27. />
  28. </RelativeLayout>
  29. </androidx.appcompat.widget.Toolbar>
  30. </com.google.android.material.appbar.CollapsingToolbarLayout>
  31. </com.google.android.material.appbar.AppBarLayout>
  32. <include layout="@layout/content_scrolling"/>

  1. - 2. AppBarStateChangeListener

public abstract class AppBarStateChangeListener implements AppBarLayout.OnOffsetChangedListener {

  1. public enum State {
  2. EXPANDED,
  3. COLLAPSED,
  4. IDLE
  5. }
  6. private State mCurrentState = State.IDLE;
  7. @Override
  8. public final void onOffsetChanged(AppBarLayout appBarLayout, int i) {
  9. if (i == 0) {
  10. if (mCurrentState != State.EXPANDED) {
  11. onStateChanged(appBarLayout, State.EXPANDED,i);
  12. }
  13. mCurrentState = State.EXPANDED;
  14. } else if (Math.abs(i) >= appBarLayout.getTotalScrollRange()) {
  15. if (mCurrentState != State.COLLAPSED) {
  16. onStateChanged(appBarLayout, State.COLLAPSED,i);
  17. }
  18. mCurrentState = State.COLLAPSED;
  19. } else {
  20. if (mCurrentState != State.IDLE) {
  21. onStateChanged(appBarLayout, State.IDLE,i);
  22. }
  23. mCurrentState = State.IDLE;
  24. }
  25. onStateChangedAny(appBarLayout,i);
  26. }
  27. public abstract void onStateChanged(AppBarLayout appBarLayout, State state,int i);
  28. public void onStateChangedAny(AppBarLayout appBarLayout, int i){
  29. }

}

  1. - 3. activity

class MeiTuanHomeActivity : AppCompatActivity(R.layout.activity_meituan_home) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState)

  1. //tb_mt_toolbar.setNavigationOnClickListener { finish() }
  2. 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 // } // // })

  1. appbar_mt_appbar.addOnOffsetChangedListener(object : AppBarStateChangeListener() {
  2. override fun onStateChanged(appBarLayout: AppBarLayout?, state: State?, i: Int) {
  3. when (state) {
  4. //折叠 i == -492
  5. State.COLLAPSED -> {
  6. tv_mt_title.text = "个人中心"
  7. }
  8. //展开 i == 0
  9. State.EXPANDED -> {
  10. tv_mt_title.text = "膜拜单车"
  11. }
  12. //中间状态
  13. State.IDLE -> {
  14. //LogUtils.e("alpha = $alpha ------max = $max","----alpha--->")
  15. }
  16. }
  17. }
  18. override fun onStateChangedAny(appBarLayout: AppBarLayout?, verticalOffset: Int) {
  19. super.onStateChangedAny(appBarLayout, verticalOffset)
  20. maxVerticalOffset = appBarLayout?.totalScrollRange?.toFloat()!!
  21. val a = -(verticalOffset / maxVerticalOffset)//渐变从0 - 1
  22. //LogUtils.e("verticalOffset = $verticalOffset ---- a = $a ---------max = $maxVerticalOffset")
  23. if (-verticalOffset <= maxVerticalOffset / 2) {
  24. title = "膜拜单车"
  25. alpha = (1 + verticalOffset / (maxVerticalOffset / 2))
  26. } else {
  27. title = "个人中心"
  28. alpha = -(1 + verticalOffset / (maxVerticalOffset / 2))
  29. }
  30. LogUtils.e("$verticalOffset-----${appBarLayout.totalScrollRange / 2 + verticalOffset}")
  31. //减小判断的范围,在这个范围内调用 settext 方法会一直触发onOffsetChanged,减小范围来减少触发
  32. if (appBarLayout.totalScrollRange / 2 + verticalOffset in -20..20) {
  33. setToolbarTitle()
  34. }
  35. tv_mt_title.alpha = alpha
  36. }
  37. })
  38. }
  39. override fun onResume() {
  40. super.onResume()
  41. //appbar_mt_appbar.addOnOffsetChangedListener(this)
  42. }
  43. override fun onPause() {
  44. super.onPause()
  45. //appbar_mt_appbar.removeOnOffsetChangedListener(this)
  46. }
  47. var alpha = 0f//渐变从1 - 0,到达折叠布局中间,再从 0 - 1
  48. var maxVerticalOffset = 0f//折叠布局最大高度
  49. 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 // }

  1. private fun setToolbarTitle() {
  2. LogUtils.e("settoolbar---$title")
  3. tv_mt_title.text = title
  4. }

}

  1. - 4. 重点
  2. 在监听折叠布局时,使用 AppbarLayout addOnOffsetChangedListener方法,同时使用自定义抽象类AppBarStateChangeListener,一般使用这个方法就可以监听状态onStateChanged,可以很方便监听折叠和展开状态,缺点是无法监听到中间转态,所以又加了一个方法onStateChangedAny,可以在需要时调用。
  3. 监听时设置标题渐变度和文字内容,如果仅仅设置渐变度从 0-1,那么只要实现下面这个就可以了 val a = -(verticalOffset / maxVerticalOffset)//渐变从0 - 1。
  4. 如果想设置渐变度从 1-0 ,再从 0-1,只需要实现下面的方法,

if (-verticalOffset <= maxVerticalOffset / 2) { title = “膜拜单车” alpha = (1 + verticalOffset / (maxVerticalOffset / 2)) } else { title = “个人中心” alpha = -(1 + verticalOffset / (maxVerticalOffset / 2)) }

  1. 注意设置标题时,有个问题,在滚动的范围内设置调用 setText 方法会一直触发监听折叠的方法,原因我也不知道,如果有知道的,或者有更好的解决方法可以留言。我的解决方法是在设置改变标题时,减小改变的条件,在使用时不会一直处在这个条件下就不会一直触发onStateChangedAny方法,不过缺点是如果滑动太快就会无法监听到,所以要在onStateChanged里设置好折叠和展开的标题,确保最终标题准确。
  2. 如果出现监控失效的情况,可以采用下面这种方法监听:

override fun onResume() { super.onResume() //appbar_mt_appbar.addOnOffsetChangedListener(this) }

  1. override fun onPause() {
  2. super.onPause()
  3. //appbar_mt_appbar.removeOnOffsetChangedListener(this)
  4. }

``` 如果想让折叠布局不响应下面的滚动,可以这么设置nsvouter._isNestedScrollingEnabled = false,效果就像哔哩哔哩播放界面,播放时滚动不折叠。

三、地址和参考

demo
高仿美团APP页面滑动标题栏渐变效果