Как заставить пользовательский просмотр наблюдать, содержащий события жизненного цикла фрагмента вместо активности? - PullRequest
0 голосов
/ 01 ноября 2018

У меня есть действие и несколько фрагментов, которые оно заменяет внутри структуры кадра. Каждый фрагмент содержит макет, накачанный из XML, с несколькими настраиваемыми представлениями. Внутри этих представлений я хочу подписаться на события жизненного цикла в этих представлениях с помощью LifecycleObserver. Мой взгляд на Котлин:

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-фрагмента, она передает активность как параметр 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, которое содержит этот фрагмент. Следовательно, LayoutInflater, который передается в onCreateView () фрагмента, содержит ссылку на FragmentActivity в качестве контекста. Таким образом, мнения эффективно соблюдают жизненный цикл деятельности.

Как мне сделать, чтобы мои пользовательские представления наблюдали за событиями жизненного цикла содержащего их фрагмента?

1 Ответ

0 голосов
/ 01 ноября 2018

Если вы используете только onPause() и onResume(), переопределения onDetachedFromWindow() и onAttachedToWindow() в вашем классе View должно быть достаточно:

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) }

А затем из onPause() и onResume() вашего фрагмента вызовите соответствующие методы вашего представления:

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

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

EDIT:

Для расширяемости создайте свой собственный min-SDK. Создать базовый класс фрагмента:

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() {}
...