687 lines
29 KiB
Kotlin
687 lines
29 KiB
Kotlin
package com.gh.gamecenter.wrapper
|
|
|
|
import android.graphics.Typeface
|
|
import android.os.Bundle
|
|
import android.view.LayoutInflater
|
|
import android.view.View
|
|
import android.view.ViewStub
|
|
import android.widget.LinearLayout
|
|
import androidx.core.graphics.ColorUtils
|
|
import androidx.core.view.updateLayoutParams
|
|
import androidx.fragment.app.Fragment
|
|
import androidx.viewpager.widget.ViewPager
|
|
import com.gh.common.iinterface.IMultiTab
|
|
import com.gh.common.iinterface.ISmartRefreshContent
|
|
import com.gh.common.iinterface.ISuperiorChain
|
|
import com.gh.common.prioritychain.PriorityChain
|
|
import com.gh.common.util.ViewPagerFragmentHelper
|
|
import com.gh.gamecenter.R
|
|
import com.gh.gamecenter.common.base.adapter.FragmentAdapter
|
|
import com.gh.gamecenter.common.base.fragment.BaseLazyFragment
|
|
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
|
|
import com.gh.gamecenter.common.constant.EntranceConsts
|
|
import com.gh.gamecenter.common.databinding.ReuseLoadingBinding
|
|
import com.gh.gamecenter.common.databinding.ReuseNoConnectionBinding
|
|
import com.gh.gamecenter.common.databinding.ReuseNoneDataBinding
|
|
import com.gh.gamecenter.common.exposure.ExposureSource
|
|
import com.gh.gamecenter.common.json.json
|
|
import com.gh.gamecenter.common.utils.*
|
|
import com.gh.gamecenter.common.view.TabIndicatorView
|
|
import com.gh.gamecenter.common.view.WrapContentDraweeView
|
|
import com.gh.gamecenter.databinding.TabItemMainBinding
|
|
import com.gh.gamecenter.entity.BottomTab
|
|
import com.gh.gamecenter.entity.MultiTabNav
|
|
import com.gh.gamecenter.fragment.ReloadFragment
|
|
import com.gh.gamecenter.home.custom.CustomPageFragment
|
|
import com.google.android.material.tabs.TabLayout
|
|
import kotlin.math.abs
|
|
|
|
/**
|
|
* 多Tab导航页 基类
|
|
*/
|
|
abstract class BaseTabWrapperFragment : BaseLazyFragment(), IMultiTab {
|
|
protected var viewPager: ViewPager? = null
|
|
protected var tabLayout: TabLayout? = null
|
|
protected var indicatorView: TabIndicatorView? = null
|
|
protected var noDataStub: ViewStub? = null
|
|
protected var noConnectionStub: ViewStub? = null
|
|
protected var loadingStub: ViewStub? = null
|
|
protected var noConnectionBinding: ReuseNoConnectionBinding? = null
|
|
protected var noDataBinding: ReuseNoneDataBinding? = null
|
|
protected var loadingBinding: ReuseLoadingBinding? = null
|
|
|
|
protected var tabBindingList = arrayListOf<TabItemMainBinding>()
|
|
protected var tabImageStyleList = arrayListOf<Int>()
|
|
|
|
protected var fragmentList = ArrayList<Fragment>()
|
|
|
|
protected var multiTabNavId = ""
|
|
protected var multiTabNavName = ""
|
|
protected var bottomTabId = ""
|
|
protected var bottomTabName = ""
|
|
protected var defaultSelectedTab = -1
|
|
protected var lastSelectedPosition = 0
|
|
|
|
protected var backgroundColor: Int = 0
|
|
protected var backgroundWhiteColor: Int = 0
|
|
protected var amwayPrimaryColor: Int = 0
|
|
protected var tabDefaultColor: Int = 0
|
|
protected var tabSelectedColor: Int = 0
|
|
protected var tabDefaultLightColor: Int = 0
|
|
|
|
abstract fun provideViewModel(): TabWrapperViewModel
|
|
protected open fun provideTabLayout(): TabLayout? = mCachedView.findViewById(R.id.tabLayout)
|
|
protected open fun provideViewPager(): ViewPager? = mCachedView.findViewById(R.id.viewPager)
|
|
protected open fun provideIndicatorView(): TabIndicatorView? = mCachedView.findViewById(R.id.indicatorView)
|
|
protected open fun provideIndicatorWidth(): Int = DEFAULT_INDICATOR_WIDTH
|
|
protected open fun provideTabTextSize(): Float = DEFAULT_TAB_TEXT_SIZE
|
|
protected open fun provideTabDefaultColor(): Int = TAB_DEFAULT_COLOR
|
|
protected open fun provideTabSelectedColor(): Int = TAB_SELECTED_COLOR
|
|
|
|
override fun provideMultiTabId(): String = multiTabNavId
|
|
override fun provideMultiTabName(): String = multiTabNavName
|
|
override fun provideCurrentTabEntity(): MultiTabNav.LinkMultiTabNav? = getCurrentTabEntity()
|
|
override fun provideLastSelectedPosition(): Int = lastSelectedPosition
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
savedInstanceState?.let { defaultSelectedTab = it.getInt(LAST_SELECTED_POSITION) }
|
|
multiTabNavId = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, "") ?: ""
|
|
multiTabNavName = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, "") ?: ""
|
|
bottomTabName = arguments?.getString(EntranceConsts.KEY_BOTTOM_TAB_NAME, "") ?: ""
|
|
bottomTabId = arguments?.getString(EntranceConsts.KEY_BOTTOM_TAB_ID, "") ?: ""
|
|
}
|
|
|
|
override fun onSaveInstanceState(outState: Bundle) {
|
|
viewPager?.let { outState.putInt(LAST_SELECTED_POSITION, it.currentItem) }
|
|
super.onSaveInstanceState(outState)
|
|
}
|
|
|
|
override fun onFragmentFirstVisible() {
|
|
super.onFragmentFirstVisible()
|
|
noDataStub = mCachedView.findViewById(R.id.reuse_no_data_stub)
|
|
noConnectionStub = mCachedView.findViewById(R.id.reuse_no_connection_stub)
|
|
loadingStub = mCachedView.findViewById(R.id.reuse_loading_stub)
|
|
|
|
backgroundColor = com.gh.gamecenter.common.R.color.ui_background.toColor(requireContext())
|
|
backgroundWhiteColor = com.gh.gamecenter.common.R.color.ui_surface.toColor(requireContext())
|
|
amwayPrimaryColor = com.gh.gamecenter.common.R.color.amway_primary_color.toColor(requireContext())
|
|
tabDefaultColor = provideTabDefaultColor().toColor(requireContext())
|
|
tabSelectedColor = provideTabSelectedColor().toColor(requireContext())
|
|
tabDefaultLightColor = TAB_DEFAULT_COLOR_LIGHT.toColor(requireContext())
|
|
|
|
if (multiTabNavId.isNotEmpty()) {
|
|
provideViewModel().tabListLiveData.observe(viewLifecycleOwner) {
|
|
if (defaultSelectedTab == -1) {
|
|
defaultSelectedTab = provideViewModel().defaultTabPosition
|
|
}
|
|
|
|
initViewPager(ArrayList(it))
|
|
|
|
// 当 tab 少于或等于一个的时候隐藏顶部 tab 栏,停用 nestedScroll
|
|
if (it.size <= 1) {
|
|
hideTab()
|
|
}
|
|
|
|
showNoConnection(false)
|
|
showLoading(false)
|
|
showNoData(it.isEmpty())
|
|
}
|
|
|
|
provideViewModel().errorLiveData.observe(viewLifecycleOwner) {
|
|
showLoading(false)
|
|
showNoData(false)
|
|
showNoConnection(true)
|
|
}
|
|
|
|
SensorsBridge.trackEvent(
|
|
"ViewSeveralTabPage",
|
|
"bottom_tab",
|
|
bottomTabName,
|
|
"several_tab_page_name",
|
|
multiTabNavName,
|
|
"several_tab_page_id",
|
|
multiTabNavId
|
|
)
|
|
}
|
|
}
|
|
|
|
private fun showNoConnection(show: Boolean) {
|
|
if (show) {
|
|
if (noConnectionBinding == null) {
|
|
noConnectionStub?.setOnInflateListener { _, inflated ->
|
|
noConnectionBinding = ReuseNoConnectionBinding.bind(inflated)
|
|
noConnectionBinding?.root?.visibility = View.VISIBLE
|
|
noConnectionBinding?.connectionReloadTv?.setOnClickListener {
|
|
provideViewModel().getMultiTabNav()
|
|
showNoData(false)
|
|
showNoConnection(false)
|
|
showLoading(true)
|
|
}
|
|
}
|
|
noConnectionStub?.inflate()
|
|
} else {
|
|
noConnectionBinding?.root?.visibility = View.VISIBLE
|
|
}
|
|
} else {
|
|
noConnectionBinding?.root?.visibility = View.GONE
|
|
}
|
|
}
|
|
|
|
private fun showNoData(show: Boolean) {
|
|
if (show) {
|
|
if (noDataBinding == null) {
|
|
noDataStub?.setOnInflateListener { _, inflated ->
|
|
noDataBinding = ReuseNoneDataBinding.bind(inflated)
|
|
noDataBinding?.root?.visibility = View.VISIBLE
|
|
}
|
|
noDataStub?.inflate()
|
|
} else {
|
|
noDataBinding?.root?.visibility = View.VISIBLE
|
|
}
|
|
} else {
|
|
noDataBinding?.root?.visibility = View.GONE
|
|
}
|
|
}
|
|
|
|
private fun showLoading(show: Boolean) {
|
|
if (show) {
|
|
if (loadingBinding == null) {
|
|
loadingStub?.setOnInflateListener { _, inflated ->
|
|
loadingBinding = ReuseLoadingBinding.bind(inflated)
|
|
loadingBinding?.root?.visibility = View.VISIBLE
|
|
}
|
|
loadingStub?.inflate()
|
|
} else {
|
|
loadingBinding?.root?.visibility = View.VISIBLE
|
|
}
|
|
} else {
|
|
loadingBinding?.root?.visibility = View.GONE
|
|
}
|
|
}
|
|
|
|
abstract fun hideTab()
|
|
|
|
override fun onBackPressed(): Boolean {
|
|
viewPager?.run {
|
|
val currentFragment = fragmentList.safelyGetInRelease(currentItem)
|
|
if (currentFragment is ToolbarFragment) {
|
|
return currentFragment.onBackPressed()
|
|
}
|
|
}
|
|
return super.onBackPressed()
|
|
}
|
|
|
|
protected open fun initViewPager(tabEntityList: ArrayList<MultiTabNav.LinkMultiTabNav>) {
|
|
viewPager = provideViewPager()
|
|
tabLayout = provideTabLayout()
|
|
indicatorView = provideIndicatorView()
|
|
|
|
val fragmentList = generateFragments(tabEntityList).apply { fragmentList = this }
|
|
val tabTitleList = arrayListOf<String>()
|
|
|
|
tabEntityList.forEach { tabTitleList.add(it.name) }
|
|
|
|
viewPager?.run {
|
|
offscreenPageLimit = fragmentList.size
|
|
doOnScroll(
|
|
onPageSelected = { position ->
|
|
onPageSelected(position)
|
|
tabEntityList.safelyGetInRelease(position)?.let { logTabSelected(it, position) }
|
|
},
|
|
onPageScrolled = { position, positionOffset, _ ->
|
|
onPageScrolled(position, positionOffset, tabEntityList)
|
|
}
|
|
)
|
|
setRestoredCurItem(this)
|
|
adapter = FragmentAdapter(childFragmentManager, fragmentList, tabTitleList)
|
|
}
|
|
|
|
tabLayout?.setupWithViewPager(viewPager)
|
|
indicatorView?.setupWithTabLayout(tabLayout)
|
|
indicatorView?.setupWithViewPager(viewPager)
|
|
indicatorView?.setIndicatorWidth(provideIndicatorWidth())
|
|
|
|
initTabView(tabEntityList)
|
|
showTabGuideIfExists(tabEntityList)
|
|
}
|
|
|
|
private fun showTabGuideIfExists(tabEntityList: ArrayList<MultiTabNav.LinkMultiTabNav>) {
|
|
val guidePosition = tabEntityList.indexOfFirst { it.guide != null }
|
|
if (guidePosition != -1) {
|
|
val tabEntity = tabEntityList[guidePosition]
|
|
val guide = tabEntity.guide
|
|
guide?.bottomTabId = bottomTabId
|
|
guide?.multiTabId = tabEntity.id
|
|
showTabGuide(true, guide, guidePosition)
|
|
} else {
|
|
showTabGuide(false)
|
|
}
|
|
}
|
|
|
|
abstract fun showTabGuide(shouldShow: Boolean, guide: BottomTab.Guide? = null, guidePosition: Int = -1)
|
|
|
|
protected open fun generateFragments(tabEntityList: ArrayList<MultiTabNav.LinkMultiTabNav>): ArrayList<Fragment> {
|
|
val fragmentList = arrayListOf<Fragment>()
|
|
for ((index, tabEntity) in tabEntityList.withIndex()) {
|
|
val bundle = Bundle()
|
|
bundle.putAll(arguments)
|
|
bundle.putBoolean(EntranceConsts.KEY_IS_FROM_MAIN_WRAPPER, false)
|
|
bundle.putBoolean(EntranceConsts.KEY_IS_FROM_TAB_WRAPPER, true)
|
|
bundle.putInt(EntranceConsts.KEY_TAB_COUNT, tabEntityList.size)
|
|
bundle.putInt(EntranceConsts.KEY_TAB_INDEX, index)
|
|
bundle.putString(EntranceConsts.KEY_TAB_NAME, tabEntity.name)
|
|
bundle.putInt(EntranceConsts.KEY_POSITION, index)
|
|
|
|
val bottomTabExposureSource = arguments?.getParcelableArrayList<ExposureSource>(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST)
|
|
val multiTabNavId = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, "") ?: ""
|
|
val multiTabNavName = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, "") ?: ""
|
|
val multiTabNavExposureSource = ExposureSource("多tab导航页", "${multiTabNavName}+${multiTabNavId}")
|
|
val tabExposureSource = ExposureSource("顶部tab", tabEntity.name)
|
|
val exposureSourceList = if (bottomTabExposureSource != null) {
|
|
bottomTabExposureSource + multiTabNavExposureSource + tabExposureSource
|
|
} else {
|
|
arrayListOf(multiTabNavExposureSource, tabExposureSource)
|
|
}
|
|
bundle.putParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST, exposureSourceList.toArrayList())
|
|
|
|
val fragment = when (tabEntity.link?.type) {
|
|
ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE -> {
|
|
val superiorChain = if (this is ISuperiorChain) this else null
|
|
bundle.putString(EntranceConsts.KEY_CUSTOM_PAGE_ID, tabEntity.link.link)
|
|
bundle.putString(EntranceConsts.KEY_CUSTOM_PAGE_NAME, tabEntity.link.text)
|
|
CustomPageFragment().setSuperiorChain(superiorChain).with(bundle)
|
|
}
|
|
|
|
else -> tabEntity.link?.let { ViewPagerFragmentHelper.createFragment(this, bundle, it, true) }
|
|
?: ReloadFragment()
|
|
}
|
|
fragmentList.add(fragment)
|
|
}
|
|
return fragmentList
|
|
}
|
|
|
|
/**
|
|
* 设置 ViewPager 默认的选中项
|
|
*/
|
|
protected open fun setRestoredCurItem(viewPager: ViewPager) {
|
|
// 设置默认 position 避免 position 为 0 的 fragment 被先加载显示再跳转至具体页面
|
|
tryCatchInRelease {
|
|
val field = ViewPager::class.java.getDeclaredField("mRestoredCurItem")
|
|
field.isAccessible = true
|
|
field.set(viewPager, defaultSelectedTab)
|
|
lastSelectedPosition = defaultSelectedTab
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 初始化每个需要显示的 TabView
|
|
*/
|
|
protected open fun initTabView(tabEntityList: ArrayList<MultiTabNav.LinkMultiTabNav>) {
|
|
for (i in 0 until tabEntityList.size) {
|
|
val tabView = tabLayout?.getTabAt(i) ?: continue
|
|
val tabEntity = tabEntityList[i]
|
|
val tabTitle = if (tabView.text != null) tabView.text.toString() else ""
|
|
val tabViewBinding = generateTabView(tabTitle)
|
|
val tabImageStyle = if (tabEntity.img.isEmpty()) {
|
|
TAB_IMAGE_EMPTY
|
|
} else if (tabEntity.showImgOnSelected == true) {
|
|
TAB_IMAGE_ALWAYS_ON
|
|
} else {
|
|
TAB_IMAGE_HIDE_ON_SELECTED
|
|
}
|
|
|
|
tabBindingList.add(tabViewBinding)
|
|
tabImageStyleList.add(tabImageStyle)
|
|
|
|
if (tabEntity.img.isNotEmpty()) {
|
|
tabViewBinding.titleTv.visibility = View.GONE
|
|
tabViewBinding.invisibleTitleTv.visibility = View.GONE
|
|
tabViewBinding.titleIv.setFixedHeight(16)
|
|
// 部分设备加载图片时会获取到错误的最小宽度,这里为它做个 64DP 的保底尺寸
|
|
tabViewBinding.titleIv.setTag(ImageUtils.TAG_TARGET_WIDTH, 64F.dip2px())
|
|
tabViewBinding.titleIv.display(tabEntity.img)
|
|
tabViewBinding.titleIv.registerLoadingCallback(object : WrapContentDraweeView.LoadingCallback {
|
|
override fun loaded() {
|
|
postRunnable {
|
|
viewPager?.run {
|
|
// 图片 layout 完成后,更新 tabBar 和 indicatorView 的位置,避免偏移
|
|
tabLayout?.setScrollPosition(currentItem, 0F, false)
|
|
indicatorView?.generatePath(currentItem, 0F)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
} else {
|
|
tabViewBinding.titleTv.visibility = View.VISIBLE
|
|
tabViewBinding.invisibleTitleTv.visibility = View.INVISIBLE
|
|
}
|
|
tabView.customView = tabViewBinding.root
|
|
tabView.view.setPadding(0, 0, 0, 0)
|
|
tabView.view.setOnClickListener {
|
|
onTabViewClick()
|
|
}
|
|
|
|
// 第一个和最后一个分别设置额外的外边距
|
|
if (i == 0) {
|
|
tabView.view.updateLayoutParams {
|
|
this as LinearLayout.LayoutParams
|
|
setMargins(getFirstTabLeftMargin(), 0, 0, 0)
|
|
}
|
|
} else if (i == tabEntityList.size - 1) {
|
|
tabView.view.updateLayoutParams {
|
|
this as LinearLayout.LayoutParams
|
|
setMargins(0, 0, getLastTabRightMargin(), 0)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected open fun getFirstTabLeftMargin(): Int = 10F.dip2px()
|
|
|
|
protected open fun getLastTabRightMargin(): Int = 10F.dip2px()
|
|
|
|
protected open fun onTabViewClick() {}
|
|
|
|
/**
|
|
* 生成 TabView
|
|
*/
|
|
protected open fun generateTabView(title: String): TabItemMainBinding {
|
|
val binding = TabItemMainBinding.inflate(LayoutInflater.from(requireContext()))
|
|
binding.titleTv.run {
|
|
text = title
|
|
textSize = provideTabTextSize()
|
|
setTextColor(tabDefaultColor)
|
|
}
|
|
binding.invisibleTitleTv.run {
|
|
text = title
|
|
textSize = provideTabTextSize()
|
|
}
|
|
return binding
|
|
}
|
|
|
|
protected open fun onPageSelected(position: Int) {
|
|
notifyChildFragmentLifecycle(position)
|
|
}
|
|
|
|
abstract fun onPageScrolled(position: Int, positionOffset: Float, tabEntityList: ArrayList<MultiTabNav.LinkMultiTabNav>)
|
|
|
|
protected open fun logTabSelected(tabEntity: MultiTabNav.LinkMultiTabNav, position: Int) {
|
|
SensorsBridge.trackEvent("SeveralTabPageTabSelect", json {
|
|
"position" to position
|
|
"tab_content" to tabEntity.name
|
|
"link_type" to tabEntity.link?.type
|
|
"link_id" to tabEntity.link?.link
|
|
"link_text" to tabEntity.link?.text
|
|
"bottom_tab" to bottomTabName
|
|
"several_tab_page_name" to multiTabNavName
|
|
"several_tab_page_id" to multiTabNavId
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 安全地从根据 position 获取 fragment
|
|
*/
|
|
protected fun getFragment(position: Int): Fragment? {
|
|
return if (position < fragmentList.size) {
|
|
fragmentList.safelyGetInRelease(position)
|
|
} else {
|
|
null
|
|
}
|
|
}
|
|
|
|
protected fun getValidSmartRefreshContent(position: Int? = viewPager?.currentItem): ISmartRefreshContent? {
|
|
if (position == null) return null
|
|
val fragment = getFragment(position)
|
|
return if (fragment?.isAdded == true) fragment as? ISmartRefreshContent else null
|
|
}
|
|
|
|
// 滑动切换Tab是否使用缩放效果
|
|
protected open fun isTabScaleEnabled(): Boolean = true
|
|
|
|
/**
|
|
* 更新 tab 的样式
|
|
* @param selectedPosition 选中的 tab
|
|
* @param positionOffset 与选中 tab 的位置偏移,为负时在选中的 tab 左边,为正时在选中 tab 的右边
|
|
* @param currentTabDefaultColor tab默认颜色
|
|
* @param currentTabSelectedColor tab选中颜色
|
|
*/
|
|
protected open fun updateTabStyle(selectedPosition: Int, positionOffset: Float, currentTabDefaultColor: Int, currentTabSelectedColor: Int) {
|
|
if (tabBindingList.isEmpty()) return
|
|
|
|
val prePosition = selectedPosition - 1
|
|
val nextPosition = selectedPosition + 1
|
|
|
|
// positionOffset 小于零,表示 indicator 当前位置处于选中的 tab 的左边
|
|
val indicatorOnLeft = positionOffset < 0F
|
|
|
|
val selectedTabBinding = tabBindingList.safelyGetInRelease(selectedPosition) ?: return
|
|
val selectedTabImageStyle = tabImageStyleList.safelyGetInRelease(selectedPosition) ?: return
|
|
|
|
// 前一个 tab、当前选中的 tab、后一个 tab 的显示比例
|
|
val preScaleRatio = 1 + abs(positionOffset) / 4
|
|
val selectedScaleRatio = 1 + (1 - abs(positionOffset)) / 4
|
|
val nextScaleRatio = 1 + positionOffset / 4
|
|
|
|
// 处理前一个 tab
|
|
if (prePosition != -1) {
|
|
tabBindingList[prePosition].run {
|
|
if (indicatorOnLeft) {
|
|
when (tabImageStyleList[prePosition]) {
|
|
TAB_IMAGE_ALWAYS_ON -> {
|
|
if (isTabScaleEnabled()) {
|
|
titleIv.scaleX = preScaleRatio
|
|
titleIv.scaleY = preScaleRatio
|
|
}
|
|
}
|
|
|
|
TAB_IMAGE_HIDE_ON_SELECTED -> {
|
|
if (isTabScaleEnabled()) {
|
|
titleIv.scaleX = preScaleRatio
|
|
titleIv.scaleY = preScaleRatio
|
|
}
|
|
titleIv.visibility = View.VISIBLE
|
|
titleTv.visibility = View.GONE
|
|
invisibleTitleTv.visibility = View.GONE
|
|
}
|
|
|
|
else -> {
|
|
titleIv.visibility = View.GONE
|
|
titleTv.visibility = View.VISIBLE
|
|
invisibleTitleTv.visibility = View.INVISIBLE
|
|
}
|
|
}
|
|
|
|
if (isTabScaleEnabled()) {
|
|
titleTv.scaleX = preScaleRatio
|
|
titleTv.scaleY = preScaleRatio
|
|
}
|
|
titleTv.setTextColor(
|
|
ColorUtils.blendARGB(
|
|
currentTabDefaultColor,
|
|
currentTabSelectedColor,
|
|
abs(positionOffset)
|
|
)
|
|
)
|
|
} else {
|
|
titleTv.setTextColor(currentTabDefaultColor)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 更新当前选中的 tab
|
|
selectedTabBinding.run {
|
|
// 更新当前选中的 tab 的图片显示状态
|
|
when (selectedTabImageStyle) {
|
|
TAB_IMAGE_ALWAYS_ON -> {
|
|
if (isTabScaleEnabled()) {
|
|
val scaleRatio = (1 + (1 - abs(positionOffset)) * 0.25).toFloat()
|
|
titleIv.scaleX = scaleRatio
|
|
titleIv.scaleY = scaleRatio
|
|
}
|
|
}
|
|
|
|
else -> {
|
|
titleIv.visibility = View.GONE
|
|
titleTv.visibility = View.VISIBLE
|
|
invisibleTitleTv.visibility = View.INVISIBLE
|
|
|
|
indicatorView?.visibility = View.VISIBLE
|
|
}
|
|
}
|
|
|
|
// 选中常驻型的图片时隐藏 indicatorView
|
|
indicatorView?.goneIf(selectedTabImageStyle == TAB_IMAGE_ALWAYS_ON)
|
|
|
|
if (isTabScaleEnabled()) {
|
|
titleTv.scaleX = selectedScaleRatio
|
|
titleTv.scaleY = selectedScaleRatio
|
|
}
|
|
titleTv.setTextColor(
|
|
ColorUtils.blendARGB(
|
|
currentTabDefaultColor,
|
|
currentTabSelectedColor,
|
|
1 - abs(positionOffset)
|
|
)
|
|
)
|
|
}
|
|
|
|
// 处理后一个 tab
|
|
if (nextPosition < tabBindingList.size) {
|
|
tabBindingList[nextPosition].run {
|
|
if (!indicatorOnLeft) {
|
|
when (tabImageStyleList[nextPosition]) {
|
|
TAB_IMAGE_ALWAYS_ON -> {
|
|
if (isTabScaleEnabled()) {
|
|
titleIv.scaleX = nextScaleRatio
|
|
titleIv.scaleY = nextScaleRatio
|
|
}
|
|
}
|
|
|
|
TAB_IMAGE_HIDE_ON_SELECTED -> {
|
|
if (isTabScaleEnabled()) {
|
|
titleIv.scaleX = nextScaleRatio
|
|
titleIv.scaleY = nextScaleRatio
|
|
}
|
|
titleIv.visibility = View.VISIBLE
|
|
titleTv.visibility = View.GONE
|
|
invisibleTitleTv.visibility = View.GONE
|
|
}
|
|
|
|
else -> {
|
|
titleIv.visibility = View.GONE
|
|
titleTv.visibility = View.VISIBLE
|
|
invisibleTitleTv.visibility = View.INVISIBLE
|
|
}
|
|
}
|
|
if (isTabScaleEnabled()) {
|
|
titleTv.scaleX = nextScaleRatio
|
|
titleTv.scaleY = nextScaleRatio
|
|
}
|
|
titleTv.setTextColor(
|
|
ColorUtils.blendARGB(
|
|
currentTabDefaultColor,
|
|
currentTabSelectedColor,
|
|
positionOffset
|
|
)
|
|
)
|
|
} else {
|
|
titleTv.setTextColor(currentTabDefaultColor)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 多 tab 切换的时候可能会出现某些 tab 的文字没有回归到原始大小的问题的问题 (positionOffset 不保证连续)
|
|
for ((index, binding) in tabBindingList.withIndex()) {
|
|
if (index != prePosition && index != selectedPosition && index != nextPosition) {
|
|
if (binding.titleTv.scaleX != 1F) {
|
|
binding.titleTv.scaleX = 1F
|
|
binding.titleTv.scaleY = 1F
|
|
}
|
|
binding.titleTv.setTextColor(currentTabDefaultColor)
|
|
if (binding.titleIv.scaleX != 1F) {
|
|
binding.titleIv.scaleX = 1F
|
|
binding.titleIv.scaleY = 1F
|
|
}
|
|
}
|
|
|
|
if (index == selectedPosition) {
|
|
if (positionOffset == 0F) {
|
|
binding.titleTv.setTextColor(currentTabSelectedColor)
|
|
}
|
|
|
|
binding.titleTv.setTypeface(binding.titleTv.typeface, Typeface.BOLD)
|
|
} else {
|
|
if (positionOffset == 0F) {
|
|
binding.titleTv.setTextColor(currentTabDefaultColor)
|
|
}
|
|
|
|
binding.titleTv.setTypeface(null, Typeface.NORMAL)
|
|
}
|
|
}
|
|
}
|
|
|
|
protected open fun notifyChildFragmentLifecycle(currentSelectedPosition: Int) {
|
|
tryWithDefaultCatch {
|
|
// 补充 Viewpager Fragment 的生命周期, 先调用旧选中 fragment 的 onPause 再当前的 onResume
|
|
// 避免部分被内嵌的 Fragment 不能正常运作
|
|
if (fragmentList.size > lastSelectedPosition) {
|
|
val fragment: Fragment = fragmentList[lastSelectedPosition]
|
|
if (!fragment.isAdded) return
|
|
|
|
fragment.onPause()
|
|
val childFragmentManager = fragment.childFragmentManager
|
|
val fragments = childFragmentManager.fragments
|
|
for (childFragment in fragments) {
|
|
childFragment.onPause()
|
|
}
|
|
}
|
|
if (fragmentList.size > currentSelectedPosition) {
|
|
val fragment: Fragment = fragmentList[currentSelectedPosition]
|
|
if (!fragment.isAdded) return
|
|
|
|
fragment.onResume()
|
|
val childFragmentManager = fragment.childFragmentManager
|
|
val fragments = childFragmentManager.fragments
|
|
for (childFragment in fragments) {
|
|
childFragment.onResume()
|
|
}
|
|
}
|
|
lastSelectedPosition = currentSelectedPosition
|
|
}
|
|
}
|
|
|
|
protected fun getCurrentTabEntity(): MultiTabNav.LinkMultiTabNav? =
|
|
provideViewPager()?.currentItem?.let {
|
|
provideViewModel().tabListLiveData.value?.safelyGetInRelease(it)
|
|
}
|
|
|
|
// 默认页面是否为自定义页面
|
|
protected fun isDefaultCustomPageFragment(): Boolean = getFragment(provideViewModel().defaultTabPosition) is CustomPageFragment
|
|
abstract fun addTabGuideHandlerIfExists(chain: PriorityChain)
|
|
|
|
override fun onDarkModeChanged() {
|
|
super.onDarkModeChanged()
|
|
backgroundColor = com.gh.gamecenter.common.R.color.ui_background.toColor(requireContext())
|
|
backgroundWhiteColor = com.gh.gamecenter.common.R.color.ui_surface.toColor(requireContext())
|
|
amwayPrimaryColor = com.gh.gamecenter.common.R.color.amway_primary_color.toColor(requireContext())
|
|
tabDefaultColor = provideTabDefaultColor().toColor(requireContext())
|
|
tabSelectedColor = provideTabSelectedColor().toColor(requireContext())
|
|
tabDefaultLightColor = TAB_DEFAULT_COLOR_LIGHT.toColor(requireContext())
|
|
}
|
|
|
|
companion object {
|
|
const val LAST_SELECTED_POSITION = "last_selected_position"
|
|
|
|
var TAB_SELECTED_COLOR: Int = com.gh.gamecenter.common.R.color.text_primary
|
|
var TAB_DEFAULT_COLOR: Int = com.gh.gamecenter.common.R.color.text_secondary
|
|
|
|
var TAB_DEFAULT_COLOR_LIGHT: Int = com.gh.gamecenter.common.R.color.search_text_color_light
|
|
var DEFAULT_TAB_TEXT_SIZE = 16F
|
|
var DEFAULT_INDICATOR_WIDTH = 18
|
|
|
|
const val TAB_IMAGE_EMPTY = 0
|
|
const val TAB_IMAGE_HIDE_ON_SELECTED = 1
|
|
const val TAB_IMAGE_ALWAYS_ON = 2
|
|
}
|
|
} |