首页 > 解决方案 > 如何让自定义视图观察包含片段的生命周期事件而不是活动?

问题描述

我有一个活动和几个片段,它在框架布局中替换。每个片段都包含一个从 XML 扩展而来的布局,其中包含许多自定义视图。在这些视图中,我想使用 LifecycleObserver 订阅这些视图中的生命周期事件。我对 Kotlin 的看法:

class MyView(context: Context) : View(context, null, 0): LifecycleObserver {

    init {
        (getContext() as LifecycleOwner).lifecycle.addObserver(this)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun onResume() {
        // code
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun onPause() {
        // code
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy() {
        lifecycle.removeObserver(this)
    }

}

问题是,当一个片段消失并被另一个片段替换时,第一个片段中的视图不会收到onPause事件。当我从第二个片段返回到它时,他们也没有得到onResume 。只有在整个活动暂停时,视图才会收到onPause,但它们不知道片段中的生命周期变化。我将此追踪到布局充气器,用于充气片段的 xml 布局,它将活动作为上下文参数传递给视图。这是布局充气器在支持库的 Fragment 类中实例化的方式:

/** @deprecated */
@Deprecated
@NonNull
@RestrictTo({Scope.LIBRARY_GROUP})
public LayoutInflater getLayoutInflater(@Nullable Bundle savedFragmentState) {
    if (this.mHost == null) {
        throw new IllegalStateException("onGetLayoutInflater() cannot be executed until the Fragment is attached to the FragmentManager.");
    } else {
        LayoutInflater result = this.mHost.onGetLayoutInflater();
        this.getChildFragmentManager();
        LayoutInflaterCompat.setFactory2(result, this.mChildFragmentManager.getLayoutInflaterFactory());
        return result;
    }
}

mHost一个 FragmentActivity 包含这个片段。因此,传递到片段的 onCreateView() 的 LayoutInflater 包含对 FragmentActivity 的引用作为上下文。因此视图有效地观察了 Activity 生命周期。

如何让我的自定义视图观察其包含片段的生命周期事件?

标签: androidandroid-fragmentsandroid-lifecycle

解决方案


如果您只使用onPause()and onResume(),在您的 View 类中覆盖onDetachedFromWindow()andonAttachedToWindow()就足够了:

override fun onAttachedToWindow() {
    //onResume code
}

override fun onDetachedFromWindow() {
    //onPause code
}

您还可以创建自己的生命周期方法:

fun onResume() {}
fun onPause() {}

从您的片段中,您可以保留对您的视图的全局引用:

//`by lazy` only initializes the variable when it's fist called, 
//and then that instance is used for subsequent calls. 
//Make sure you only reference `myView` after `onCreateView()` returns
private val myView by lazy { view.findViewById<MyView>(R.id.my_view) }

然后从您的 Fragment 的onPause()andonResume()中,调用您的 View 的相应方法:

override fun onPause() {
    myView.onPause()
}

override fun onResume() {
    myView.onResume()
}

编辑:

为了可扩展性,制作您自己的 min-SDK。创建一个基本的 Fragment 类:

open class LifecycleFragment : Fragment() {
    internal fun dispatchOnResume(parent: View) {
        if (parent is CustomLifecycleObserver) parent.onResume()
        if (parent is ViewGroup) {
            for (i in 0 until parent.childCount) {
                dispatchOnResume(parent.getChildAt(i))
            }
        }
    }

    internal fun dispatchOnPause(parent: View) {
        if (parent is CustomLifecycleObserver) parent.onPause()
        if (parent is ViewGroup) {
            for (i in 0 until parent.childCount) {
                dispatchOnResume(parent.getChildAt(i))
            }
        }
    }

    override fun onResume() {
        dispatchOnResume(view)
    }

    override fun onPause() {
        dispatchOnPause(view)
    }
}

CustomLifecycleListener将是您的视图实现、包含onResume()onPause()方法的接口。)

然后只需在您的其他片段中扩展该类:

class SomeFragment : LifecycleFragment() {}

推荐阅读