1. 自测 场景
简易场景 { // 只考虑最后一个 Fragment
case1: Activity + Fragment
case2: Activity + FragmentA + FragmentB 「只考虑 FragmentB 自身的变化」
case3: Activity + ViewPager + Fragment
case4: Activity + ViewPager2 + Fragment
}
复杂场景 {
case1: Activity + FragmentA + FragmentB
// FragmentA 可见性 变化,对 FragmentB 的影响
}
2. 自测 场景结果
2.1.1 简易场景 case1
case1: Activity + Fragment { replace = remove + add add(int, fragment) onCreate() -> onActivityCreated() -> onResume() remove(fragment) onPause() -> onDestroy() hide(fragment) onHiddenChanged(true) show(fragment) onHiddenChanged(false) 息屏(fragmentA) onPause() 亮屏(fragmentA) onResume() }
2.1.2 简易场景 case2
case2: Activity + FragmentA + FragmentB 「只考虑 FragmentB 自身的变化」{ replace = remove + add add(int, fragment) onCreate() -> onActivityCreated() -> onResume() remove(fragment) onPause() -> onDestroy() hide(fragment) onHiddenChanged(true) show(fragment) onHiddenChanged(false) 息屏(fragmentA) onPause() 亮屏(fragmentA) onResume() }
2.1.3 简易场景 case3
case3: Activity + ViewPager + Fragment { viewpager.count = 7 viewpager.offscreenPageLimit = 1 { 初始化 index = 0 { text0 text0: onCreate() -> onActivityCreated() -> onResume() text1: onCreate() -> onActivityCreated() -> onResume() } 右滑 第 1 次 { text1 text0: setUserVisibleHint(false) text1: setUserVisibleHint(true) text2: onCreate() -> onActivityCreated() -> onResume() } 息屏 { text1 text0: onPause() text1: onPause() text2: onPause() } 亮屏 { text1 text0: onResume() text1: onResume() text2: onResume() } 右滑 第 2 次 { text2 text0: onPause() text1: setUserVisibleHint(false) text2: setUserVisibleHint(true) text3: onCreate() -> onActivityCreated() -> onResume() } } viewpager.count = 7 viewpager.offscreenPageLimit = 2 { 初始化 index = 0 { text0 text0: onCreate() -> onActivityCreated() -> onResume() text1: onCreate() -> onActivityCreated() -> onResume() text2: onCreate() -> onActivityCreated() -> onResume() } 右滑 第 1 次 { text1 text0: setUserVisibleHint(false) text1: setUserVisibleHint(true) text3: onCreate() -> onActivityCreated() -> onResume() } 右滑 第 2 次 { text2 text1: setUserVisibleHint(false) text2: setUserVisibleHint(true) text4: onCreate() -> onActivityCreated() -> onResume() } } }
2.1.4 简易场景 case4
case4: Activity + ViewPager2 + Fragment { viewpager2.count = 7 viewpager2.offscreenPageLimit = 1 { 初始化 index = 0 { text0 text0: onCreate() -> onActivityCreated() -> onResume() text1: onCreate() -> onActivityCreated() } 右滑 第 1 次 { text1 text0: onPause() text1: onResume() text2: onCreate() -> onActivityCreated() } 息屏 { text1 text1: onPause() } 亮屏 { text1 text1: onResume() } 右滑 第 2 次 { text2 text1: onPause() text2: onResume() text3: onCreate() -> onActivityCreated() } 左滑 第 1 次 { text1 text2: onPause() text1: onResume() } } viewpager2.count = 7 viewpager2.offscreenPageLimit = 2 { 初始化 index = 0 { text0 text0: onCreate() -> onActivityCreated() -> onResume() text1: onCreate() -> onActivityCreated() text2: onCreate() -> onActivityCreated() } 右滑 第 1 次 { text1 text0: onPause() text1: onResume() text3: onCreate() -> onActivityCreated() } 右滑 第 2 次 { text2 text1: onPause() text2: onResume() text4: onCreate() -> onActivityCreated() } 左滑 第 1 次 { text1 text1: onResume() text2: onPause() } } }
2.2.1 复杂场景 case1
考虑到场景复杂,父容器 和 子容器,只做1个通知即可。代码如下:
/**
* 作为父Fragment, visible 变化后,通知child
*/
fun dispatch2Child(fragment: Fragment, visible: Boolean) {
val childFragments = fragment.childFragmentManager.fragments
// 没有 child
if (childFragments.isEmpty()) {
return
}
// 当前Fragment可见性修改,通知下去
childFragments.forEachIndexed { index, child ->
if (child.isAdded && child is OnVisibleListener) {
child.onParentFragmentVisibleChanged(visible)
}
}
}
class ChildFragment: Fragment(), OnVisibleListener {
override fun onParentFragmentVisibleChanged(parentVisible: Boolean) {
// TODO 给一个默认 逻辑;可被复写
}
}
3. 代码实现
1. BaseFragment // 基类
2. VisibleJobImpl // 对接 BaseFragment,和 具体功能的实现类
3. VisibleLogJob // 提供 BaseFragment 生命周期的日志功能
4. VFunctionVP2Job、VFunctionLayoutJob、VFunctionVPJob // 适配 ViewPager、ViewPager2 等场景
abstract class BaseFragment : Fragment(), OnVisibleListener {
var logTag: String? = ""
private var visibleJob = VisibleJobImpl()
protected abstract fun getLayoutId(): Int
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
visibleJob.setLogTag(logTag)
visibleJob.setOnVisibleListener(this)
visibleJob.onCreate(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val customJob = onCreateFunctionJob()
if (null != customJob) {
visibleJob.setFunctionJob(customJob)
} else {
// 自动查找 场景
val functionJob = when (VisibleJobImpl.findParentContainerCase(view.parent, 3)) {
VisibleJobImpl.CASE_CONTAINER_LAYOUT -> VFunctionLayoutJob()
VisibleJobImpl.CASE_CONTAINER_VIEWPAGER -> VFunctionVPJob()
VisibleJobImpl.CASE_CONTAINER_VIEWPAGER2 -> VFunctionVP2Job()
else -> null
}
visibleJob.setFunctionJob(functionJob)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(getLayoutId(), container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
visibleJob.onActivityCreated()
}
override fun onResume() {
super.onResume()
visibleJob.onResume()
}
override fun onPause() {
visibleJob.onPause()
super.onPause()
}
override fun onDestroy() {
visibleJob.onDestroy()
super.onDestroy()
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
visibleJob.setUserVisibleHint(isVisibleToUser)
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
visibleJob.onHiddenChanged(hidden)
}
open fun onCreateFunctionJob(): OnVisibleLifecycleJob? {
return null
}
fun getVisibleJobImpl(): VisibleJobImpl {
return visibleJob
}
private var nextShouldVisible = false // 父容器改变时,下一次是否需要修改可变性
override fun onParentFragmentVisibleChanged(parentVisible: Boolean) {
visibleJob.print("onParentFragmentVisibleChanged($parentVisible)")
if (parentVisible) { // 父容器,不可见 -> 可见
if (nextShouldVisible) {
visibleJob.updateVisible(true)
}
} else { // 父容器,可见 -> 不可见
if (isVisible) {
visibleJob.updateVisible(false)
nextShouldVisible = true
}
}
}
open override fun onFragmentFirstVisible() {
}
open override fun onFragmentVisible() {
}
open override fun onFragmentInvisible() {
}
}
class VisibleJobImpl {
private var logJob: VisibleLogJob? = null // 日志实现
private var functionJob: OnVisibleLifecycleJob? = null // 功能实现
private var hostFragment: Fragment? = null // 宿主Fragment
private var visibleListener: OnVisibleListener? = null
fun setOnVisibleListener(listener: OnVisibleListener) {
this.visibleListener = listener
}
fun setFunctionJob(functionJob: OnVisibleLifecycleJob?) {
print("setFunctionJob: ${functionJob?.javaClass?.simpleName}")
this.functionJob = functionJob
}
fun onCreate(fragment: Fragment) {
this.hostFragment = fragment
logJob = VisibleLogJob()
logJob?.onCreate(fragment, this)
functionJob?.onCreate(fragment, this)
}
fun onActivityCreated() {
logJob?.onActivityCreated(this)
functionJob?.onActivityCreated(this)
}
fun onResume() {
logJob?.onResume(this)
functionJob?.onResume(this)
}
fun onPause() {
logJob?.onPause(this)
functionJob?.onPause(this)
}
fun onDestroy() {
logJob?.onDestroy(this)
functionJob?.onPause(this)
this.hostFragment = null // 清空
}
fun setUserVisibleHint(isVisibleToUser: Boolean) {
logJob?.setUserVisibleHint(this, isVisibleToUser)
functionJob?.setUserVisibleHint(this, isVisibleToUser)
}
fun onHiddenChanged(hidden: Boolean) {
logJob?.onHiddenChanged(this, hidden)
functionJob?.onHiddenChanged(this, hidden)
}
private var logHeader = ""
private var hasFirstVisible = false
private var nowVisible = false
/**
* className: ==FragmentA
*/
fun setLogTag(className: String? = "") {
this.logHeader = "xxq==$className" // 案例: "xxq====FragmentA"
}
/**
* 修改可见性
*/
fun updateVisible(visible: Boolean) {
val fragment = hostFragment ?: return
if (nowVisible == visible) {
return
}
nowVisible = visible
// 当前Fragment 可见性修改
if (visible) {
callVisible()
} else {
callInvisible()
}
// 可见性 变化后,分发给 child
dispatch2Child(fragment, visible)
}
/**
* 可见
*/
private fun callVisible() {
if (!hasFirstVisible) {
hasFirstVisible = true
visibleListener?.onFragmentFirstVisible()
print("onFragmentFirstVisible")
}
visibleListener?.onFragmentVisible()
print("onFragmentVisible")
}
/**
* 不可见
*/
private fun callInvisible() {
visibleListener?.onFragmentInvisible()
print("onFragmentInvisible")
}
/**
* 打印日志,adb shell setprop log.tag.xxq V
*/
fun print(method: String?) {
if (Log.isLoggable("xxq", Log.DEBUG)) {
Log.d("xxq", "$logHeader.$method")
}
}
companion object {
const val CASE_CONTAINER_KEY = "case_container_key"
const val CASE_CONTAINER_LAYOUT = 0 // layout 加载 Fragment
const val CASE_CONTAINER_VIEWPAGER = 1 // viewpager 加载 Fragment
const val CASE_CONTAINER_VIEWPAGER2 = 2 // viewpager2 加载 Fragment
/**
* 作为父Fragment, visible 变化后,通知child
*/
fun dispatch2Child(fragment: Fragment, visible: Boolean) {
val childFragments = fragment.childFragmentManager.fragments
// 没有 child
if (childFragments.isEmpty()) {
return
}
// 当前Fragment可见性修改,通知下去
childFragments.forEachIndexed { index, child ->
if (child.isAdded && child is OnVisibleListener) {
child.onParentFragmentVisibleChanged(visible)
}
}
}
/**
* 向上查找容器,最多 level 层
*/
fun findParentContainerCase(view: ViewParent?, level: Int = 3): Int {
if (null == view) { // ViewPager2 时,viewParent 为空
return CASE_CONTAINER_VIEWPAGER2
}
if (level <= 0) { // 寻找几层,没有,默认 Layout
return CASE_CONTAINER_LAYOUT
}
if (view is ViewPager) { // ViewPager 校验
return CASE_CONTAINER_VIEWPAGER
}
// 递归查找
return findParentContainerCase(view.parent, level - 1)
}
}
}
class VisibleLogJob : OnVisibleLifecycleJob {
override fun onCreate(fragment: Fragment, jobImpl: VisibleJobImpl) {
jobImpl.print("onCreate")
}
override fun onActivityCreated(jobImpl: VisibleJobImpl) {
jobImpl.print("onActivityCreated")
}
override fun onResume(jobImpl: VisibleJobImpl) {
jobImpl.print("onResume")
}
override fun onPause(jobImpl: VisibleJobImpl) {
jobImpl.print("onPause")
}
override fun onDestroy(jobImpl: VisibleJobImpl) {
jobImpl.print("onDestroy")
}
override fun setUserVisibleHint(jobImpl: VisibleJobImpl, isVisibleToUser: Boolean) {
jobImpl.print("setUserVisibleHint($isVisibleToUser)")
}
override fun onHiddenChanged(jobImpl: VisibleJobImpl, hidden: Boolean) {
jobImpl.print("onHiddenChanged($hidden)")
}
}
class VFunctionVPJob : VFunctionLayoutJob() {
private var hostFragment: Fragment? = null
override fun onCreate(fragment: Fragment, jobImpl: VisibleJobImpl) {
this.hostFragment = fragment
}
override fun onActivityCreated(jobImpl: VisibleJobImpl) {
}
override fun onResume(jobImpl: VisibleJobImpl) {
if (hostFragment?.userVisibleHint == true) {
jobImpl.updateVisible(true)
}
}
override fun onPause(jobImpl: VisibleJobImpl) {
if (hostFragment?.userVisibleHint == true) {
jobImpl.updateVisible(false)
}
}
override fun onDestroy(jobImpl: VisibleJobImpl) {
this.hostFragment = null
}
override fun setUserVisibleHint(jobImpl: VisibleJobImpl, isVisibleToUser: Boolean) {
jobImpl.updateVisible(isVisibleToUser)
}
override fun onHiddenChanged(jobImpl: VisibleJobImpl, hidden: Boolean) {
jobImpl.updateVisible(!hidden)
}
}
class VFunctionVP2Job : VFunctionLayoutJob() {
override fun onCreate(fragment: Fragment, jobImpl: VisibleJobImpl) {
}
override fun onActivityCreated(jobImpl: VisibleJobImpl) {
}
override fun onResume(jobImpl: VisibleJobImpl) {
jobImpl.updateVisible(true)
}
override fun onPause(jobImpl: VisibleJobImpl) {
jobImpl.updateVisible(false)
}
override fun onDestroy(jobImpl: VisibleJobImpl) {
}
override fun setUserVisibleHint(jobImpl: VisibleJobImpl, isVisibleToUser: Boolean) {
}
override fun onHiddenChanged(jobImpl: VisibleJobImpl, hidden: Boolean) {
jobImpl.updateVisible(!hidden)
}
}
open class VFunctionLayoutJob : OnVisibleLifecycleJob {
override fun onCreate(fragment: Fragment, jobImpl: VisibleJobImpl) {
}
override fun onActivityCreated(jobImpl: VisibleJobImpl) {
}
override fun onResume(jobImpl: VisibleJobImpl) {
jobImpl.updateVisible(true)
}
override fun onPause(jobImpl: VisibleJobImpl) {
jobImpl.updateVisible(false)
}
override fun onDestroy(jobImpl: VisibleJobImpl) {
}
override fun setUserVisibleHint(jobImpl: VisibleJobImpl, isVisibleToUser: Boolean) {
}
override fun onHiddenChanged(jobImpl: VisibleJobImpl, hidden: Boolean) {
jobImpl.updateVisible(!hidden)
}
}