Aunque he visto algunas respuestas agradables basadas en el enlace de datos, no vi ninguna en su totalidad con ese enfoque, en el sentido de permitir la resolución de fragmentos mientras permitía definiciones de diseño sin fragmentos en XML.
Entonces, suponiendo que el enlace de datos esté habilitado, aquí hay una solución genérica que puedo proponer; Un poco largo pero definitivamente funciona (con algunas advertencias):
Paso 1: implementación personalizada de OnClick
Esto ejecutará una búsqueda con reconocimiento de fragmentos a través de contextos asociados con la vista pulsada (por ejemplo, botón):
// 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
}
Nota: basado libremente en la implementación original de Android (consulte https://android.googlesource.com/platform/frameworks/base/+/a175a5b/core/java/android/view/View.java#3025 )
Paso 2: aplicación declarativa en archivos de diseño
Luego, en XML de enlace de datos conscientes:
<layout>
<data>
<import type="com.example.CustomOnClick"/>
</data>
<Button
android:onClick='@{(v) -> CustomOnClick.onClick(v, "myClickMethod")}'
</Button>
</layout>
Advertencias
- Asume una
FragmentActivity
implementación 'moderna' basada
- Solo se puede buscar el método del fragmento "top-most" (es decir, el último ) en la pila (aunque eso se puede arreglar, si es necesario)