Bien que j'aie repéré de jolies réponses en s'appuyant sur la liaison de données, je n'en ai vu aucune allant dans toute la mesure avec cette approche - dans le sens d'activer la résolution des fragments tout en permettant des définitions de disposition sans fragment dans XML.
Donc, en supposant que la liaison de données est activée, voici une solution générique que je peux proposer; Un peu long mais ça marche définitivement (avec quelques mises en garde):
Étape 1: implémentation OnClick personnalisée
Cela lancera une recherche sensible aux fragments à travers les contextes associés à la vue sur laquelle vous avez tapé (par exemple le bouton):
// CustomOnClick.kt
@file:JvmName("CustomOnClick")
package com.example
import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import java.lang.reflect.Method
fun onClick(view: View, methodName: String) {
resolveOnClickInvocation(view, methodName)?.invoke(view)
}
private data class OnClickInvocation(val obj: Any, val method: Method) {
fun invoke(view: View) {
method.invoke(obj, view)
}
}
private fun resolveOnClickInvocation(view: View, methodName: String): OnClickInvocation? =
searchContexts(view) { context ->
var invocation: OnClickInvocation? = null
if (context is Activity) {
val activity = context as? FragmentActivity
?: throw IllegalStateException("A non-FragmentActivity is not supported (looking up an onClick handler of $view)")
invocation = getTopFragment(activity)?.let { fragment ->
resolveInvocation(fragment, methodName)
}?: resolveInvocation(context, methodName)
}
invocation
}
private fun getTopFragment(activity: FragmentActivity): Fragment? {
val fragments = activity.supportFragmentManager.fragments
return if (fragments.isEmpty()) null else fragments.last()
}
private fun resolveInvocation(target: Any, methodName: String): OnClickInvocation? =
try {
val method = target.javaClass.getMethod(methodName, View::class.java)
OnClickInvocation(target, method)
} catch (e: NoSuchMethodException) {
null
}
private fun <T: Any> searchContexts(view: View, matcher: (context: Context) -> T?): T? {
var context = view.context
while (context != null && context is ContextWrapper) {
val result = matcher(context)
if (result == null) {
context = context.baseContext
} else {
return result
}
}
return null
}
Remarque: librement basé sur l'implémentation Android d'origine (voir https://android.googlesource.com/platform/frameworks/base/+/a175a5b/core/java/android/view/View.java#3025 )
Étape 2: application déclarative dans les fichiers de mise en page
Ensuite, dans les XML sensibles à la liaison de données:
<layout>
<data>
<import type="com.example.CustomOnClick"/>
</data>
<Button
android:onClick='@{(v) -> CustomOnClick.onClick(v, "myClickMethod")}'
</Button>
</layout>
Avertissements
- Suppose une
FragmentActivity
implémentation «moderne»
- Peut uniquement rechercher la méthode du fragment "le plus haut" (c'est-à-dire le dernier ) dans la pile (bien que cela puisse être corrigé, si nécessaire)