如何让自定义视图观察包含片段的生命周期事件而不是活动?

Posted

技术标签:

【中文标题】如何让自定义视图观察包含片段的生命周期事件而不是活动?【英文标题】:How to make custom view observe containing fragment's lifecycle events instead of activity? 【发布时间】:2019-04-05 05:04:06 【问题描述】:

我在框架布局中替换了一个活动和几个片段。每个片段都包含一个从 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。只有在整个 activity 暂停时,视图才会收到 onPause,但它们不知道 fragments 中的生命周期变化。我将此追踪到布局充气器,用于充气片段的 xml 布局,它将活动作为 context 参数传递给视图。这是布局充气器在支持库的 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 生命周期。

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

【问题讨论】:

您可以将构造函数中的片段生命周期传递给您的视图或通过 setter。 new MyView(getLifecycle()) 其中getLifecycle() 是片段生命周期。 感谢您的建议,阿列克谢。是的,这可行,但需要我手动实例化视图,并且我将无法将 layoutInflater 用于 XML 布局。我希望有一种更优雅的方式,比如将活动包装到片段中的上下文包装器中,并重写某种方法来传递这个包装器? 我认为您可以尝试在您的 init 块中执行此操作 (context as AppCompatActivity).supportFragmentManager.findFragmentByTag("your tag").getLifecycle() 【参考方案1】:

如果您只使用onPause()onResume(),在您的视图类中覆盖onDetachedFromWindow()onAttachedToWindow() 就足够了:

override fun onAttachedToWindow() 
    //onResume code


override fun onDetachedFromWindow() 
    //onPause code

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

fun onResume() 
fun onPause() 

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

//`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()onResume(),调用你的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() 

【讨论】:

嘿。是的,我知道我可以使用 onAttached/onDetached 来监视视图的可见性状态,实际上我目前正在覆盖视图中的 onWindowVisibilityChanged(),因为它提供了我需要的行为。但是当有专用的生命周期机制时,这似乎是一种黑客行为,我宁愿使用它。只有它在观察错误的生命周期:( 所以使用第二种方法。 然后我失去了使用 Lifecycle 类的所有灵活性。而且我需要记住,每次更改片段布局时,我都会执行此手动步骤,这会每隔几个月发生一次。不要误会我的意思,我知道这里有很多选择。我可以让我的所有视图都实现一个 MyLifecycle 接口,并在片段的 onPause/onResume 中循环遍历整个视图层次结构,检查子项是否是 MyLifecycle 的实例并在它们上调用 onPause/onResume。但是,如果 Lifecycle 有办法,我宁愿避免所有的跑腿工作。 更改片段的布局?嗯?只需保持onResume()onPause() 方法的实现即可。如果您不想要全局参考,您甚至可以只做view.my_view.onResume()view.my_view.onPause()。或者您可以创建一个基础 Fragment 类来为您完成它。 创建一个基类,然后使用您所说的递归策略。我可以编辑一个方法以递归地调度生命周期更改。

以上是关于如何让自定义视图观察包含片段的生命周期事件而不是活动?的主要内容,如果未能解决你的问题,请参考以下文章

如何响应 SwiftUI 中的视图生命周期事件?

Maven2生命周期帮助

我想要一个自定义的、可重复使用的子视图,它可以了解模型和生命周期事件

把 LiveData 用于事件传递那些坑

Rx 生命周期管理

Android开发——UI_片段