Kaynağa Gözat

feat: 一级二级tab支持双击回到顶部

pengwuliang 10 ay önce
ebeveyn
işleme
174d0f78d1
23 değiştirilmiş dosya ile 232 ekleme ve 118 silme
  1. 24 0
      app/src/main/java/com/adealink/weparty/commonui/widget/CommonTabLayout.kt
  2. 14 6
      app/src/main/java/com/adealink/weparty/module/userlist/HomeUserListFragment.kt
  3. 6 1
      app/src/main/java/com/adealink/weparty/module/userlist/fragment/HomeBaseUserListFragment.kt
  4. 6 0
      app/src/main/java/com/adealink/weparty/ui/IScrollManager.kt
  5. 21 2
      app/src/main/java/com/adealink/weparty/ui/home/BaseHomeFragment.kt
  6. 6 1
      module/follow/src/main/java/com/adealink/weparty/follow/FansListFragment.kt
  7. 6 1
      module/follow/src/main/java/com/adealink/weparty/follow/FollowListFragment.kt
  8. 14 2
      module/follow/src/main/java/com/adealink/weparty/follow/FriendHomeFragment.kt
  9. 6 1
      module/follow/src/main/java/com/adealink/weparty/follow/FriendListFragment.kt
  10. 13 2
      module/message/src/main/java/com/adealink/weparty/message/MessageFragment.kt
  11. 15 4
      module/message/src/main/java/com/adealink/weparty/message/MessageHomeFragment.kt
  12. 6 1
      module/message/src/main/java/com/adealink/weparty/message/conversationlist/ConversationListFragment.kt
  13. 18 5
      module/moment/src/main/java/com/adealink/weparty/moment/usermoment/MomentFragment.kt
  14. 3 2
      module/moment/src/main/java/com/adealink/weparty/moment/usermoment/square/MomentListFragment.kt
  15. 6 1
      module/profile/src/main/java/com/adealink/weparty/profile/me/MeFragment.kt
  16. 1 0
      module/profile/src/main/res/layout/fragment_me.xml
  17. 18 68
      module/room/src/main/java/com/adealink/weparty/room/roomlist/RoomListFragment.kt
  18. 7 1
      module/room/src/main/java/com/adealink/weparty/room/roomlist/follow/FollowRoomListFragment.kt
  19. 6 1
      module/room/src/main/java/com/adealink/weparty/room/roomlist/friends/FriendsRoomListFragment.kt
  20. 9 1
      module/room/src/main/java/com/adealink/weparty/room/roomlist/mine/MineRoomListFragment.kt
  21. 6 1
      module/room/src/main/java/com/adealink/weparty/room/roomlist/popular/PopularRoomListFragment.kt
  22. 5 1
      module/room/src/main/java/com/adealink/weparty/room/roomlist/recently/RecentlyRoomListFragment.kt
  23. 16 16
      module/room/src/main/res/layout/fragment_room_list.xml

+ 24 - 0
app/src/main/java/com/adealink/weparty/commonui/widget/CommonTabLayout.kt

@@ -175,6 +175,7 @@ class CommonTabLayout @JvmOverloads constructor(
         tabFragmentStateAdapter: BaseTabFragmentStateAdapter?,
         currentItem: Int,
         showIndicator: Boolean = true,
+        scrollToTop: (() -> Unit)? = null
     ) {
         this.viewPager2 = viewPager2
         this.tabFragmentStateAdapter = tabFragmentStateAdapter
@@ -198,6 +199,9 @@ class CommonTabLayout @JvmOverloads constructor(
             }.attach()
         }
         tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
+            private var lastClickTime = 0L
+            private var lastClickPosition = -1
+
             override fun onTabSelected(tab: TabLayout.Tab?) {
                 updateTabView(tab, true, null, showIndicator)
             }
@@ -207,6 +211,14 @@ class CommonTabLayout @JvmOverloads constructor(
             }
 
             override fun onTabReselected(tab: TabLayout.Tab?) {
+                val currentTime = System.currentTimeMillis()
+                val position = tab?.position ?: 0
+                if (lastClickPosition == position && currentTime - lastClickTime < 300) {
+                    scrollToTop?.invoke()
+                }
+
+                lastClickTime = currentTime
+                lastClickPosition = position
             }
         })
     }
@@ -225,6 +237,7 @@ class CommonTabLayout @JvmOverloads constructor(
         viewPager2: ViewPager2?,
         tabFragmentStateAdapter: BaseActivityTabFragmentStateAdapter?,
         currentItem: Int,
+        scrollToTop: (() -> Unit)? = null
     ) {
         this.viewPager2 = viewPager2
         viewPager2?.let {
@@ -246,6 +259,9 @@ class CommonTabLayout @JvmOverloads constructor(
             }.attach()
         }
         tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
+            private var lastClickTime = 0L
+            private var lastClickPosition = -1
+
             override fun onTabSelected(tab: TabLayout.Tab?) {
                 updateTabView(tab, true)
             }
@@ -255,6 +271,14 @@ class CommonTabLayout @JvmOverloads constructor(
             }
 
             override fun onTabReselected(tab: TabLayout.Tab?) {
+                val currentTime = System.currentTimeMillis()
+                val position = tab?.position ?: 0
+                if (lastClickPosition == position && currentTime - lastClickTime < 300) {
+                    scrollToTop?.invoke()
+                }
+
+                lastClickTime = currentTime
+                lastClickPosition = position
             }
         })
     }

+ 14 - 6
app/src/main/java/com/adealink/weparty/module/userlist/HomeUserListFragment.kt

@@ -26,6 +26,7 @@ import com.adealink.weparty.module.userlist.fragment.HomeNewUserListFragment
 import com.adealink.weparty.module.userlist.fragment.HomeOnLineUserListFragment
 import com.adealink.weparty.module.userlist.viewmodel.HomeUserListViewModel
 import com.adealink.weparty.module.userlist.viewmodel.HomeUserTabType
+import com.adealink.weparty.ui.IScrollManager
 
 /**
  * 首页用户列表
@@ -33,7 +34,7 @@ import com.adealink.weparty.module.userlist.viewmodel.HomeUserTabType
  * Date: 2025/2/24
  */
 @RouterUri(path = [UserList.HOME.PATH], desc = "首页用户列表")
-class HomeUserListFragment : BaseFragment(R.layout.fragment_home_user_list) {
+class HomeUserListFragment : BaseFragment(R.layout.fragment_home_user_list), IScrollManager {
 
     companion object {
         //先这样实现,后续加入弹窗队列
@@ -56,6 +57,9 @@ class HomeUserListFragment : BaseFragment(R.layout.fragment_home_user_list) {
 
             )
     }
+    private val pageAdapter by fastLazy {
+        PageAdapter()
+    }
 
     @BindExtra(name = AppModule.Main.EXTRA_MAIN_SUB_TAB)
     var subTabKey: String? = null
@@ -72,7 +76,6 @@ class HomeUserListFragment : BaseFragment(R.layout.fragment_home_user_list) {
             height = DisplayUtil.getStatusBarHeight(requireActivity())
         }
 
-        val pageAdapter = PageAdapter()
         binding.viewPager.adapter = pageAdapter
 
         val defaultPos = if (subTabKey == "online") {
@@ -86,7 +89,10 @@ class HomeUserListFragment : BaseFragment(R.layout.fragment_home_user_list) {
         binding.commonTabLayout.createMediatorAndAttach(
             viewPager2 = binding.viewPager,
             tabFragmentStateAdapter = pageAdapter,
-            currentItem = defaultPos
+            currentItem = defaultPos,
+            scrollToTop = {
+                scrollToTop()
+            }
         )
 
         binding.btnFilter.onClick {
@@ -105,11 +111,13 @@ class HomeUserListFragment : BaseFragment(R.layout.fragment_home_user_list) {
         }
     }
 
-    override fun onResume() {
-        super.onResume()
+    override fun scrollToTop() {
+        val fg = pageAdapter.getFragment(this, binding.viewPager.currentItem)
+        if (fg is IScrollManager) {
+            fg.scrollToTop()
+        }
     }
 
-
     inner class PageAdapter : BaseTabFragmentStateAdapter(this) {
         override fun getTabName(pos: Int): String {
             return tabList.getOrNull(pos)?.title ?: ""

+ 6 - 1
app/src/main/java/com/adealink/weparty/module/userlist/fragment/HomeBaseUserListFragment.kt

@@ -37,6 +37,7 @@ import com.adealink.weparty.module.userlist.viewmodel.HomeUserTabType
 import com.adealink.weparty.module.userlist.viewmodel.IUserListViewModel
 import com.adealink.weparty.stat.constant.Page
 import com.adealink.weparty.stat.reportEnterPage
+import com.adealink.weparty.ui.IScrollManager
 import com.adealink.weparty.ui.home.util.HomeLocalService
 import com.qmuiteam.qmui.widget.popup.QMUIPopup
 import com.qmuiteam.qmui.widget.popup.QMUIPopups
@@ -48,7 +49,7 @@ import kotlinx.coroutines.launch
  * Created by XiaoDongLin.
  * Date: 2025/2/24
  */
-abstract class HomeBaseUserListFragment : BaseFragment(R.layout.layout_home_user_list) {
+abstract class HomeBaseUserListFragment : BaseFragment(R.layout.layout_home_user_list), IScrollManager {
 
     override val isLazyLoad: Boolean = true
 
@@ -181,6 +182,10 @@ abstract class HomeBaseUserListFragment : BaseFragment(R.layout.layout_home_user
         }
     }
 
+    override fun scrollToTop() {
+        binding.rvUserList.scrollToPosition(0)
+    }
+
     /**
      * 要扽列表动画完成之后,不然会定位错误
      */

+ 6 - 0
app/src/main/java/com/adealink/weparty/ui/IScrollManager.kt

@@ -0,0 +1,6 @@
+package com.adealink.weparty.ui
+
+interface IScrollManager {
+
+    fun scrollToTop()
+}

+ 21 - 2
app/src/main/java/com/adealink/weparty/ui/home/BaseHomeFragment.kt

@@ -62,6 +62,7 @@ import com.adealink.weparty.module.task.UserTaskManager
 import com.adealink.weparty.module.task.invite.InviteRewardManager
 import com.adealink.weparty.module.userlist.UserList
 import com.adealink.weparty.module.wallet.WalletModule
+import com.adealink.weparty.ui.IScrollManager
 import com.adealink.weparty.ui.home.util.HomeLocalService
 import com.adealink.weparty.ui.home.util.HomeUIUtil
 import com.adealink.weparty.ui.tab.HOME_TABS
@@ -122,8 +123,8 @@ abstract class BaseHomeFragment : BaseFragment, ITabManager {
             false
         ) { tabLayout, position ->
             tabLayout.setCustomView(R.layout.layout_main_tab_normal)
-            tabLayout.customView?.let {
-                val customBinding = LayoutMainTabNormalBinding.bind(it)
+            tabLayout.customView?.let { customView ->
+                val customBinding = LayoutMainTabNormalBinding.bind(customView)
                 val tab = getTab(position) ?: return@let
                 onConfigureTab(tab, customBinding)
                 updateTabView(
@@ -134,6 +135,9 @@ abstract class BaseHomeFragment : BaseFragment, ITabManager {
             }
         }.attach()
         tlTab.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
+            private var lastClickTime = 0L
+            private var lastClickPosition = -1
+
             override fun onTabSelected(tab: TabLayout.Tab?) {
                 HomeUIUtil.selectedHomeTab = HOME_TABS[tab?.position ?: 0].type
                 updateTabView(tab, true, tab?.position ?: 0)
@@ -149,6 +153,14 @@ abstract class BaseHomeFragment : BaseFragment, ITabManager {
             }
 
             override fun onTabReselected(tab: TabLayout.Tab?) {
+                val currentTime = System.currentTimeMillis()
+                val position = tab?.position ?: 0
+                if (lastClickPosition == position && currentTime - lastClickTime < 300) {
+                    scrollToTop(position)
+                }
+
+                lastClickTime = currentTime
+                lastClickPosition = position
             }
 
         })
@@ -156,6 +168,13 @@ abstract class BaseHomeFragment : BaseFragment, ITabManager {
         setDefaultTab(tabKey = mainTabKey)
     }
 
+    protected fun scrollToTop(pos: Int) {
+        val fg = homePagerAdapter.getFragment(this, pos)
+        if (fg is IScrollManager) {
+            fg.scrollToTop()
+        }
+    }
+
     fun updateTabView(
         tabView: TabLayout.Tab?,
         isSelected: Boolean,

+ 6 - 1
module/follow/src/main/java/com/adealink/weparty/follow/FansListFragment.kt

@@ -22,8 +22,9 @@ import com.adealink.weparty.module.follow.data.RelationEmptyErrorType
 import com.adealink.weparty.module.follow.data.RelationItemData
 import com.adealink.weparty.module.follow.data.RelationShipType
 import com.adealink.weparty.module.follow.data.RelationUserItemData
+import com.adealink.weparty.ui.IScrollManager
 
-class FansListFragment : BaseFragment(R.layout.fragment_fans_list) {
+class FansListFragment : BaseFragment(R.layout.fragment_fans_list), IScrollManager {
     private val binding by viewBinding(FragmentFansListBinding::bind)
     private val followViewModel by viewModels<FollowViewModel> { FollowViewModelFactory() }
     private val listAdapter by fastLazy { MultiTypeListAdapter(BaseListDiffUtil()) }
@@ -86,4 +87,8 @@ class FansListFragment : BaseFragment(R.layout.fragment_fans_list) {
             binding.refreshLayout.setEnableLoadMore(false)
         }
     }
+
+    override fun scrollToTop() {
+        binding.rvFans.scrollToPosition(0)
+    }
 }

+ 6 - 1
module/follow/src/main/java/com/adealink/weparty/follow/FollowListFragment.kt

@@ -20,8 +20,9 @@ import com.adealink.weparty.module.follow.data.RelationEmptyErrorType
 import com.adealink.weparty.module.follow.data.RelationItemData
 import com.adealink.weparty.module.follow.data.RelationShipType
 import com.adealink.weparty.module.follow.data.RelationUserItemData
+import com.adealink.weparty.ui.IScrollManager
 
-class FollowListFragment: BaseFragment(R.layout.fragment_follow_list) {
+class FollowListFragment: BaseFragment(R.layout.fragment_follow_list), IScrollManager {
     private val binding by viewBinding(FragmentFollowListBinding::bind)
 
     private val followViewModel by viewModels<FollowViewModel> { FollowViewModelFactory() }
@@ -80,4 +81,8 @@ class FollowListFragment: BaseFragment(R.layout.fragment_follow_list) {
             binding.refreshLayout.setEnableLoadMore(false)
         }
     }
+
+    override fun scrollToTop() {
+        binding.rvFollow.scrollToPosition(0)
+    }
 }

+ 14 - 2
module/follow/src/main/java/com/adealink/weparty/follow/FriendHomeFragment.kt

@@ -3,6 +3,7 @@ package com.adealink.weparty.follow
 import android.graphics.Typeface
 import androidx.fragment.app.Fragment
 import com.adealink.frame.aab.util.getCompatString
+import com.adealink.frame.base.fastLazy
 import com.adealink.frame.mvvm.view.viewBinding
 import com.adealink.frame.router.annotation.RouterUri
 import com.adealink.weparty.commonui.BaseFragment
@@ -14,17 +15,21 @@ import com.adealink.weparty.follow.databinding.LayoutFriendTabBinding
 import com.adealink.weparty.module.follow.Follow
 import com.adealink.weparty.stat.constant.Page
 import com.adealink.weparty.stat.reportEnterPage
+import com.adealink.weparty.ui.IScrollManager
 import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
 import com.google.android.material.tabs.TabLayout.Tab
 import com.google.android.material.tabs.TabLayoutMediator
 
 @RouterUri(path = [Follow.Relationship.PATH], desc = "首页好友页面")
-class FriendHomeFragment : BaseFragment(R.layout.fragment_friend_home) {
+class FriendHomeFragment : BaseFragment(R.layout.fragment_friend_home), IScrollManager {
     private val binding by viewBinding(FragmentFriendHomeBinding::bind)
 
+    private val pageAdapter by fastLazy {
+        PageAdapter()
+    }
+
     override fun initViews() {
         super.initViews()
-        val pageAdapter = PageAdapter()
         binding.viewPager.adapter = pageAdapter
         TabLayoutMediator(
             binding.friendTabLayout, binding.viewPager
@@ -92,4 +97,11 @@ class FriendHomeFragment : BaseFragment(R.layout.fragment_friend_home) {
         }
     }
 
+    override fun scrollToTop() {
+        val fg = pageAdapter.getFragment(this, binding.viewPager.currentItem)
+        if (fg is IScrollManager) {
+            fg.scrollToTop()
+        }
+    }
+
 }

+ 6 - 1
module/follow/src/main/java/com/adealink/weparty/follow/FriendListFragment.kt

@@ -22,8 +22,9 @@ import com.adealink.weparty.module.follow.data.RelationEmptyErrorType
 import com.adealink.weparty.module.follow.data.RelationItemData
 import com.adealink.weparty.module.follow.data.RelationShipType
 import com.adealink.weparty.module.follow.data.RelationUserItemData
+import com.adealink.weparty.ui.IScrollManager
 
-class FriendListFragment: BaseFragment(R.layout.fragment_friend_list) {
+class FriendListFragment: BaseFragment(R.layout.fragment_friend_list), IScrollManager {
     private val binding by viewBinding(FragmentFriendListBinding::bind)
 
     private val followViewModel by viewModels<FollowViewModel> { FollowViewModelFactory() }
@@ -86,4 +87,8 @@ class FriendListFragment: BaseFragment(R.layout.fragment_friend_list) {
             binding.refreshLayout.setEnableLoadMore(false)
         }
     }
+
+    override fun scrollToTop() {
+        binding.rvFollow.scrollToPosition(0)
+    }
 }

+ 13 - 2
module/message/src/main/java/com/adealink/weparty/message/MessageFragment.kt

@@ -29,21 +29,25 @@ import com.adealink.weparty.module.moment.dot.momentMsgDot
 import com.adealink.weparty.permission.PermissionUtils
 import com.adealink.weparty.stat.constant.Page
 import com.adealink.weparty.stat.reportEnterPage
+import com.adealink.weparty.ui.IScrollManager
 import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
 import com.google.android.material.tabs.TabLayout.Tab
 import com.google.android.material.tabs.TabLayoutMediator
 import io.rong.push.RongPushClient.ConversationType
 
-class MessageFragment: BaseFragment(R.layout.fragment_message) {
+class MessageFragment: BaseFragment(R.layout.fragment_message), IScrollManager {
     private val binding by viewBinding(FragmentMessageBinding::bind)
 
     private val conversationListTypeLists by fastLazy {
         listOf(ConversationListType.All, ConversationListType.Online, ConversationListType.UnRead, ConversationListType.FrequentChat, ConversationListType.Follow)
     }
 
+    private val pageAdapter by fastLazy {
+        PageAdapter()
+    }
+
     override fun initViews() {
         super.initViews()
-        val pageAdapter = PageAdapter()
         binding.viewPager.adapter = pageAdapter
         binding.viewPager.isUserInputEnabled = false
         binding.viewPager.offscreenPageLimit = 1
@@ -174,4 +178,11 @@ class MessageFragment: BaseFragment(R.layout.fragment_message) {
         DialogTaskManager.submit(WomemChatDiamondDialogTask(activity))
 
     }
+
+    override fun scrollToTop() {
+        val fg = pageAdapter.getFragment(this, binding.viewPager.currentItem)
+        if (fg is IScrollManager) {
+            fg.scrollToTop()
+        }
+    }
 }

+ 15 - 4
module/message/src/main/java/com/adealink/weparty/message/MessageHomeFragment.kt

@@ -7,6 +7,7 @@ import androidx.fragment.app.activityViewModels
 import androidx.lifecycle.lifecycleScope
 import com.adealink.frame.aab.util.getCompatString
 import com.adealink.frame.base.Rlt
+import com.adealink.frame.base.fastLazy
 import com.adealink.frame.mvvm.view.viewBinding
 import com.adealink.frame.router.Router
 import com.adealink.frame.router.annotation.BindExtra
@@ -31,11 +32,12 @@ import com.adealink.weparty.module.message.Message
 import com.adealink.weparty.module.profile.ProfileModule
 import com.adealink.weparty.module.profile.data.Gender
 import com.adealink.weparty.module.search.Search
+import com.adealink.weparty.ui.IScrollManager
 import kotlinx.coroutines.launch
 import com.adealink.weparty.R as APP_R
 
 @RouterUri(path = [Message.Home.PATH], desc = "消息首页")
-class MessageHomeFragment : BaseFragment(R.layout.fragment_message_home) {
+class MessageHomeFragment : BaseFragment(R.layout.fragment_message_home), IScrollManager {
 
     companion object {
         var backFromConversationActivity = false
@@ -44,7 +46,7 @@ class MessageHomeFragment : BaseFragment(R.layout.fragment_message_home) {
 
     private val binding by viewBinding(FragmentMessageHomeBinding::bind)
     private val conversationListViewModel by activityViewModels<WeConversationSettingViewModel>()
-
+    private val pageAdapter by fastLazy { PageAdapter() }
 
     @BindExtra(name = AppModule.Main.EXTRA_MAIN_SUB_TAB)
     var subTabKey: String? = null
@@ -74,7 +76,6 @@ class MessageHomeFragment : BaseFragment(R.layout.fragment_message_home) {
             height = DisplayUtil.getStatusBarHeight(requireActivity())
         }
 
-        val pageAdapter = PageAdapter()
         binding.viewPager.adapter = pageAdapter
         binding.viewPager.isUserInputEnabled = false
         val defaultPos = if (subTabKey == "message") {
@@ -88,7 +89,10 @@ class MessageHomeFragment : BaseFragment(R.layout.fragment_message_home) {
         binding.commonTabLayout.createMediatorAndAttach(
             viewPager2 = binding.viewPager,
             tabFragmentStateAdapter = pageAdapter,
-            currentItem = defaultPos
+            currentItem = defaultPos,
+            scrollToTop = {
+                scrollToTop()
+            }
         )
 
         binding.btnClearUnreadMsg.onClick(5000) {
@@ -170,4 +174,11 @@ class MessageHomeFragment : BaseFragment(R.layout.fragment_message_home) {
 
         }
     }
+
+    override fun scrollToTop() {
+        val fg = pageAdapter.getFragment(this, binding.viewPager.currentItem)
+        if (fg is IScrollManager) {
+            fg.scrollToTop()
+        }
+    }
 }

+ 6 - 1
module/message/src/main/java/com/adealink/weparty/message/conversationlist/ConversationListFragment.kt

@@ -32,13 +32,14 @@ import com.adealink.weparty.module.profile.Profile
 import com.adealink.weparty.module.room.Room
 import com.adealink.weparty.room.data.EnterRoomInfo
 import com.adealink.weparty.room.data.JoinRoomFrom
+import com.adealink.weparty.ui.IScrollManager
 import com.scwang.smart.refresh.layout.api.RefreshLayout
 import com.scwang.smart.refresh.layout.constant.RefreshState
 import com.scwang.smart.refresh.layout.simple.SimpleMultiListener
 
 @RouterUri(path = [Message.Conversation.LIST], desc = "会话列表")
 class ConversationListFragment : BaseFragment(R.layout.fragment_conversationlist),
-    IViewProviderListener<BaseUiConversation> {
+    IViewProviderListener<BaseUiConversation>, IScrollManager {
     @BindExtra(name = Message.Common.EXTRA_CONVERSATION_PAGE_TYPE, desc = "会话列表页面类型")
     var conversationListType: Int = ConversationListType.All.type
 
@@ -301,4 +302,8 @@ class ConversationListFragment : BaseFragment(R.layout.fragment_conversationlist
     override fun onViewLongClick(clickType: Int, data: BaseUiConversation): Boolean {
         return false
     }
+
+    override fun scrollToTop() {
+        binding.conversationList.scrollToPosition(0)
+    }
 }

+ 18 - 5
module/moment/src/main/java/com/adealink/weparty/moment/usermoment/MomentFragment.kt

@@ -36,11 +36,12 @@ import com.adealink.weparty.moment.stat.MomentStatEvent
 import com.adealink.weparty.moment.usermoment.pub.MomentPublishActivity
 import com.adealink.weparty.moment.usermoment.square.MomentListFragment
 import com.adealink.weparty.moment.viewmodel.MomentViewModel
+import com.adealink.weparty.ui.IScrollManager
 import com.adealink.weparty.widget.skin.appOtherBgSkin
 import kotlinx.coroutines.launch
 
 @RouterUri(path = [Moment.Home.PATH], desc = "主页Moment Tab")
-class MomentFragment : BaseFragment(R.layout.fragment_moment), IMomentListener {
+class MomentFragment : BaseFragment(R.layout.fragment_moment), IMomentListener, IScrollManager {
 
     override val isLazyLoad: Boolean = true
 
@@ -52,6 +53,10 @@ class MomentFragment : BaseFragment(R.layout.fragment_moment), IMomentListener {
 
     private val likeEffectRes = listOf("moment_like_large_a.svga", "moment_like_large_b.svga")
 
+    private val pageAdapter by lazy {
+        MomentTabAdapter()
+    }
+
     companion object {
         const val TAB_INDEX_SQUARE = 0
         const val TAB_INDEX_FOLLOW = 1
@@ -71,8 +76,6 @@ class MomentFragment : BaseFragment(R.layout.fragment_moment), IMomentListener {
             height = DisplayUtil.getStatusBarHeight(requireActivity())
         }
 
-
-        val pageAdapter = MomentTabAdapter()
         binding.momentListViewPage.adapter = pageAdapter
         binding.momentListViewPage.isSaveEnabled = false
         (binding.momentListViewPage.getChildAt(0) as? RecyclerView)?.setOverScrollModeToNever()
@@ -87,7 +90,10 @@ class MomentFragment : BaseFragment(R.layout.fragment_moment), IMomentListener {
         binding.commonTabLayout.createMediatorAndAttach(
             viewPager2 = binding.momentListViewPage,
             tabFragmentStateAdapter = pageAdapter,
-            currentItem = defaultPos
+            currentItem = defaultPos,
+            scrollToTop = {
+                scrollToTop()
+            }
         )
         binding.ivCreate.setOnClickListener {
             startActivity(Intent(context, MomentPublishActivity::class.java))
@@ -138,7 +144,7 @@ class MomentFragment : BaseFragment(R.layout.fragment_moment), IMomentListener {
             binding.svLike.updateLayoutParams<ConstraintLayout.LayoutParams> {
                 topMargin = it - binding.svLike.height
             }
-            binding.svLike.setAsset( likeEffectRes.random(), )
+            binding.svLike.setAsset(likeEffectRes.random())
         }
     }
 
@@ -217,4 +223,11 @@ class MomentFragment : BaseFragment(R.layout.fragment_moment), IMomentListener {
 
         createBtnHiding = false
     }
+
+    override fun scrollToTop() {
+        val fg = pageAdapter.getFragment(this, binding.momentListViewPage.currentItem)
+        if (fg is IScrollManager) {
+            fg.scrollToTop()
+        }
+    }
 }

+ 3 - 2
module/moment/src/main/java/com/adealink/weparty/moment/usermoment/square/MomentListFragment.kt

@@ -87,6 +87,7 @@ import com.adealink.weparty.moment.viewmodel.MomentViewModelFactory
 import com.adealink.weparty.moment.widget.GiftTextView
 import com.adealink.weparty.stat.constant.Page
 import com.adealink.weparty.stat.reportEnterPage
+import com.adealink.weparty.ui.IScrollManager
 import com.adealink.weparty.viewmodel.parentFragmentViewModels
 import kotlinx.coroutines.launch
 import kotlin.math.abs
@@ -95,7 +96,7 @@ import com.adealink.weparty.R as APP_R
 @RouterUri(path = [Moment.MomentList.PATH], desc = "Moment 列表")
 class MomentListFragment : BaseFragment(R.layout.fragment_moment_list), ReplyOperationListener,
     IMomentCallBack, MomentOperationListener, MomentInputDialog.MomentReplyCallback,
-    IMomentListener, IGetUserInfoListener {
+    IMomentListener, IGetUserInfoListener, IScrollManager {
 
     override val isLazyLoad: Boolean = true
 
@@ -496,7 +497,7 @@ class MomentListFragment : BaseFragment(R.layout.fragment_moment_list), ReplyOpe
         }
     }
 
-    fun scrollToTop() {
+    override fun scrollToTop() {
         binding.rvMoment.smoothScrollToPosition(0)
     }
 

+ 6 - 1
module/profile/src/main/java/com/adealink/weparty/profile/me/MeFragment.kt

@@ -70,6 +70,7 @@ import com.adealink.weparty.profile.viewmodel.ProfileViewModel
 import com.adealink.weparty.profile.viewmodel.ProfileViewModelFactory
 import com.adealink.weparty.stat.constant.Page
 import com.adealink.weparty.stat.reportEnterPage
+import com.adealink.weparty.ui.IScrollManager
 import com.adealink.weparty.url.H5Page
 import com.adealink.weparty.url.urlConfigService
 import com.adealink.weparty.widget.skin.appOtherBgSkin
@@ -79,7 +80,7 @@ import kotlinx.coroutines.launch
 import com.adealink.weparty.R as APP_R
 
 @RouterUri(path = [Profile.Me.PATH], desc = "首页我的")
-class MeFragment : BaseFragment(R.layout.fragment_me) {
+class MeFragment : BaseFragment(R.layout.fragment_me), IScrollManager {
 
     private val binding by viewBinding(FragmentMeBinding::bind)
     private val profileViewModel by viewModels<ProfileViewModel> { ProfileViewModelFactory() }
@@ -529,4 +530,8 @@ class MeFragment : BaseFragment(R.layout.fragment_me) {
             }
         }
     }
+
+    override fun scrollToTop() {
+        binding.scrollLayout.smoothScrollTo(0, 0)
+    }
 }

+ 1 - 0
module/profile/src/main/res/layout/fragment_me.xml

@@ -33,6 +33,7 @@
     </androidx.appcompat.widget.LinearLayoutCompat>
 
     <ScrollView
+        android:id="@+id/scroll_layout"
         android:layout_width="match_parent"
         android:layout_height="0dp"
         app:layout_constraintBottom_toBottomOf="parent"

+ 18 - 68
module/room/src/main/java/com/adealink/weparty/room/roomlist/RoomListFragment.kt

@@ -1,15 +1,10 @@
 package com.adealink.weparty.room.roomlist
 
-import android.graphics.Typeface
 import android.os.Bundle
-import android.util.TypedValue
-import android.view.LayoutInflater
-import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.core.view.updateLayoutParams
 import androidx.fragment.app.Fragment
 import androidx.recyclerview.widget.RecyclerView
 import androidx.viewpager2.widget.ViewPager2
-import com.adealink.frame.aab.util.getCompatColor
 import com.adealink.frame.aab.util.getCompatString
 import com.adealink.frame.base.Rlt
 import com.adealink.frame.base.fastLazy
@@ -21,10 +16,7 @@ import com.adealink.frame.router.annotation.RouterUri
 import com.adealink.frame.util.DisplayUtil
 import com.adealink.weparty.AppModule
 import com.adealink.weparty.commonui.BaseFragment
-import com.adealink.weparty.commonui.ext.dp
-import com.adealink.weparty.commonui.ext.gone
 import com.adealink.weparty.commonui.ext.setOverScrollModeToNever
-import com.adealink.weparty.commonui.ext.show
 import com.adealink.weparty.commonui.recycleview.adapter.BaseTabFragmentStateAdapter
 import com.adealink.weparty.module.room.Room
 import com.adealink.weparty.module.search.Search
@@ -32,16 +24,13 @@ import com.adealink.weparty.room.R
 import com.adealink.weparty.room.data.EnterRoomInfo
 import com.adealink.weparty.room.data.JoinRoomFrom
 import com.adealink.weparty.room.databinding.FragmentRoomListBinding
-import com.adealink.weparty.room.databinding.TabRoomListBinding
 import com.adealink.weparty.room.roomlist.mine.MineRoomListFragment
 import com.adealink.weparty.room.roomlist.popular.PopularRoomListFragment
+import com.adealink.weparty.ui.IScrollManager
 import com.adealink.weparty.ui.listener.setOnGameClick
-import com.google.android.material.tabs.TabLayout
-import com.google.android.material.tabs.TabLayoutMediator
-import com.adealink.weparty.R as APP_R
 
 @RouterUri(path = [Room.RoomList.PATH], desc = "首页房间列表")
-class RoomListFragment : BaseFragment(R.layout.fragment_room_list) {
+class RoomListFragment : BaseFragment(R.layout.fragment_room_list), IScrollManager {
 
     companion object {
         const val TAB_INDEX_MINE = 0
@@ -67,11 +56,8 @@ class RoomListFragment : BaseFragment(R.layout.fragment_room_list) {
     private var currentPageIndex = TAB_INDEX_POPULAR
 
     override fun initViews() {
-        val statusBarHeight = activity?.let {
-            DisplayUtil.getStatusBarHeight(it)
-        } ?: 0
-        binding.clTop.updateLayoutParams<ConstraintLayout.LayoutParams> {
-            height = 48.5f.dp() + statusBarHeight
+        binding.spaceStatusBar.updateLayoutParams {
+            height = DisplayUtil.getStatusBarHeight(requireActivity())
         }
 
         roomListPageAdapter = RoomListPageAdapter()
@@ -90,63 +76,20 @@ class RoomListFragment : BaseFragment(R.layout.fragment_room_list) {
     }
 
     private fun initTabs() {
-        val activity = activity ?: return
-        TabLayoutMediator(binding.tabLayout, binding.roomListViewPage) { tab, position ->
-            val tabBinding = TabRoomListBinding.inflate(
-                LayoutInflater.from(activity),
-                null,
-                false
-            )
-            tab.customView = tabBinding.root
-            tabBinding.tabTitle.text = roomListPageAdapter.getTabName(position)
-            updateTabView(tab, currentPageIndex == TABS[position])
-        }.attach()
-        binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
-            override fun onTabSelected(tab: TabLayout.Tab?) {
-                updateTabView(tab, true)
+        binding.commonTabLayout.createMediatorAndAttach(
+            viewPager2 = binding.roomListViewPage,
+            tabFragmentStateAdapter = roomListPageAdapter,
+            currentItem = 0,
+            scrollToTop = {
+                scrollToTop()
             }
-
-            override fun onTabUnselected(tab: TabLayout.Tab?) {
-                updateTabView(tab, false)
-            }
-
-            override fun onTabReselected(tab: TabLayout.Tab?) {
-            }
-        })
+        )
         binding.roomListViewPage.registerOnPageChangeCallback(object :
             ViewPager2.OnPageChangeCallback() {
             override fun onPageSelected(position: Int) {
                 currentPageIndex = TABS[position]
             }
         })
-
-        val defaultPos = if (subTabKey == "hot") {
-            0
-        } else if (subTabKey == "mine") {
-            1
-        } else {
-            0
-        }
-        binding.roomListViewPage.setCurrentItem(defaultPos, false)
-    }
-
-    private fun updateTabView(tab: TabLayout.Tab?, isSelected: Boolean) {
-        tab?.customView?.let {
-            val customViewBinding = TabRoomListBinding.bind(it)
-            if (isSelected) {
-                customViewBinding.tabTitle.setMarqueeEnable(true)
-                customViewBinding.tabTitle.setTextColor(getCompatColor(APP_R.color.black))
-                customViewBinding.tabTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18f)
-                customViewBinding.tabTitle.setTypeface(null, Typeface.BOLD)
-                customViewBinding.ivSelected.show()
-            } else {
-                customViewBinding.tabTitle.setMarqueeEnable(false)
-                customViewBinding.tabTitle.setTextColor(getCompatColor(APP_R.color.color_222222))
-                customViewBinding.tabTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
-                customViewBinding.tabTitle.setTypeface(null, Typeface.NORMAL)
-                customViewBinding.ivSelected.gone()
-            }
-        }
     }
 
     private fun onRoomCreateClick() {
@@ -199,4 +142,11 @@ class RoomListFragment : BaseFragment(R.layout.fragment_room_list) {
             }
         }
     }
+
+    override fun scrollToTop() {
+        val fg = roomListPageAdapter.getFragment(this, binding.roomListViewPage.currentItem)
+        if (fg is IScrollManager) {
+            fg.scrollToTop()
+        }
+    }
 }

+ 7 - 1
module/room/src/main/java/com/adealink/weparty/room/roomlist/follow/FollowRoomListFragment.kt

@@ -26,10 +26,12 @@ import com.adealink.weparty.room.roomlist.RoomListErrorEmptyData
 import com.adealink.weparty.room.roomlist.RoomListErrorEmptyType
 import com.adealink.weparty.room.roomlist.viewmodel.FollowRoomListViewModel
 import com.adealink.weparty.room.viewmodel.RoomViewModelFactory
+import com.adealink.weparty.ui.IScrollManager
 import com.scwang.smart.refresh.layout.api.RefreshLayout
 import com.scwang.smart.refresh.layout.listener.OnRefreshLoadMoreListener
 
-class FollowRoomListFragment : BaseFragment(R.layout.fragment_follow_room_list), RoomItemViewBinder.IRoomClickCallback {
+class FollowRoomListFragment : BaseFragment(R.layout.fragment_follow_room_list), RoomItemViewBinder.IRoomClickCallback,
+    IScrollManager {
     private val binding by viewBinding(FragmentFollowRoomListBinding::bind)
 
     private val recentRoomListViewModel by viewModels<FollowRoomListViewModel>() { RoomViewModelFactory() }
@@ -148,4 +150,8 @@ class FollowRoomListFragment : BaseFragment(R.layout.fragment_follow_room_list),
                 EnterRoomInfo(roomInfo.roomId, JoinRoomFrom.ROOM_LIST.from)
             ).start()
     }
+
+    override fun scrollToTop() {
+        binding.rvRoomList.scrollToPosition(0)
+    }
 }

+ 6 - 1
module/room/src/main/java/com/adealink/weparty/room/roomlist/friends/FriendsRoomListFragment.kt

@@ -26,10 +26,11 @@ import com.adealink.weparty.room.roomlist.RoomListErrorEmptyData
 import com.adealink.weparty.room.roomlist.RoomListErrorEmptyType
 import com.adealink.weparty.room.roomlist.viewmodel.FriendsRoomListViewModel
 import com.adealink.weparty.room.viewmodel.RoomViewModelFactory
+import com.adealink.weparty.ui.IScrollManager
 import com.scwang.smart.refresh.layout.api.RefreshLayout
 import com.scwang.smart.refresh.layout.listener.OnRefreshLoadMoreListener
 
-class FriendsRoomListFragment : BaseFragment(R.layout.fragment_friends_room_list), RoomItemViewBinder.IRoomClickCallback {
+class FriendsRoomListFragment : BaseFragment(R.layout.fragment_friends_room_list), RoomItemViewBinder.IRoomClickCallback, IScrollManager {
 
     private val binding by viewBinding(FragmentFriendsRoomListBinding::bind)
 
@@ -149,4 +150,8 @@ class FriendsRoomListFragment : BaseFragment(R.layout.fragment_friends_room_list
                 EnterRoomInfo(roomInfo.roomId, JoinRoomFrom.ROOM_LIST.from)
             ).start()
     }
+
+    override fun scrollToTop() {
+        binding.rvRoomList.scrollToPosition(0)
+    }
 }

+ 9 - 1
module/room/src/main/java/com/adealink/weparty/room/roomlist/mine/MineRoomListFragment.kt

@@ -15,8 +15,9 @@ import com.adealink.weparty.room.roomlist.friends.FriendsRoomListFragment
 import com.adealink.weparty.room.roomlist.recently.RecentlyRoomListFragment
 import com.adealink.weparty.stat.constant.Page
 import com.adealink.weparty.stat.reportEnterPage
+import com.adealink.weparty.ui.IScrollManager
 
-class MineRoomListFragment : BaseFragment(R.layout.fragment_mine_room_list) {
+class MineRoomListFragment : BaseFragment(R.layout.fragment_mine_room_list), IScrollManager {
     companion object {
         const val TAB_INDEX_RECENTLY = 0
         const val TAB_INDEX_FOLLOW = 1
@@ -104,4 +105,11 @@ class MineRoomListFragment : BaseFragment(R.layout.fragment_mine_room_list) {
             }
         }
     }
+
+    override fun scrollToTop() {
+        val fg = roomListPageAdapter.getFragment(this, binding.roomListViewPage.currentItem)
+        if(fg is IScrollManager) {
+            fg.scrollToTop()
+        }
+    }
 }

+ 6 - 1
module/room/src/main/java/com/adealink/weparty/room/roomlist/popular/PopularRoomListFragment.kt

@@ -17,8 +17,9 @@ import com.adealink.weparty.room.roomlist.viewmodel.PopularRoomListViewModel
 import com.adealink.weparty.room.viewmodel.RoomViewModelFactory
 import com.adealink.weparty.stat.constant.Page
 import com.adealink.weparty.stat.reportEnterPage
+import com.adealink.weparty.ui.IScrollManager
 
-class PopularRoomListFragment : BaseFragment(R.layout.fragment_popular_room_list) {
+class PopularRoomListFragment : BaseFragment(R.layout.fragment_popular_room_list), IScrollManager {
 
     private val binding by viewBinding(FragmentPopularRoomListBinding::bind)
     private val listAdapter by fastLazy { MultiTypeListAdapter(BaseListDiffUtil()) }
@@ -107,4 +108,8 @@ class PopularRoomListFragment : BaseFragment(R.layout.fragment_popular_room_list
         }
         roomListViewModel.onMoreCountrySelect(selectedCountry)
     }
+
+    override fun scrollToTop() {
+        binding.rvRoomList.scrollToPosition(0)
+    }
 }

+ 5 - 1
module/room/src/main/java/com/adealink/weparty/room/roomlist/recently/RecentlyRoomListFragment.kt

@@ -26,11 +26,12 @@ import com.adealink.weparty.room.roomlist.RoomListErrorEmptyData
 import com.adealink.weparty.room.roomlist.RoomListErrorEmptyType
 import com.adealink.weparty.room.roomlist.viewmodel.RecentRoomListViewModel
 import com.adealink.weparty.room.viewmodel.RoomViewModelFactory
+import com.adealink.weparty.ui.IScrollManager
 import com.scwang.smart.refresh.layout.api.RefreshLayout
 import com.scwang.smart.refresh.layout.listener.OnRefreshLoadMoreListener
 
 class RecentlyRoomListFragment : BaseFragment(R.layout.fragment_recently_room_list),
-    RoomItemViewBinder.IRoomClickCallback {
+    RoomItemViewBinder.IRoomClickCallback, IScrollManager {
 
     private val binding by viewBinding(FragmentRecentlyRoomListBinding::bind)
     private val recentRoomListViewModel by viewModels<RecentRoomListViewModel>() { RoomViewModelFactory() }
@@ -165,4 +166,7 @@ class RecentlyRoomListFragment : BaseFragment(R.layout.fragment_recently_room_li
             ).start()
     }
 
+    override fun scrollToTop() {
+        binding.rvRoomList.scrollToPosition(0)
+    }
 }

+ 16 - 16
module/room/src/main/res/layout/fragment_room_list.xml

@@ -1,34 +1,34 @@
 <?xml version="1.0" encoding="utf-8"?>
 <androidx.constraintlayout.widget.ConstraintLayout 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">
 
+    <Space
+        android:id="@+id/space_status_bar"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        app:layout_constraintTop_toTopOf="parent" />
+
     <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/cl_top"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        app:layout_constraintTop_toTopOf="parent"
-        tools:layout_height="92dp">
+        app:layout_constraintTop_toBottomOf="@id/space_status_bar">
 
-        <com.google.android.material.tabs.TabLayout
-            android:id="@+id/tab_layout"
+        <com.adealink.weparty.commonui.widget.CommonTabLayout
+            android:id="@+id/common_tab_layout"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginHorizontal="@dimen/room_list_horizontal_padding"
-            app:layout_constrainedWidth="true"
+            android:layout_marginTop="6.5dp"
+            android:layout_marginBottom="7.5dp"
+            app:indicator_color="@color/color_222222"
             app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintEnd_toStartOf="@id/iv_search"
-            app:layout_constraintHorizontal_bias="0"
             app:layout_constraintStart_toStartOf="parent"
-            app:tabBackground="@null"
-            app:tabGravity="fill"
-            app:tabIndicatorHeight="0dp"
-            app:tabMode="scrollable"
-            app:tabPaddingEnd="28dp"
-            app:tabPaddingStart="0dp"
-            app:tabRippleColor="@null" />
+            app:layout_constraintTop_toTopOf="parent"
+            app:normal_tab_color="@color/color_tab_normal"
+            app:normal_text_size="16sp"
+            app:selected_text_size="21sp" />
 
         <androidx.appcompat.widget.AppCompatImageView
             android:id="@+id/iv_search"