Obwohl ich einige nette Antworten gesehen habe, die sich auf Datenbindung stützen, habe ich keine gesehen, die diesen Ansatz voll ausschöpft - im Sinne der Ermöglichung von Fragmentauflösung bei gleichzeitiger Ermöglichung von fragmentfreien Layout-Definitionen in XML's.
Unter der Annahme einer Datenbindung ist aktiviert, hier ist eine allgemeine Lösung, die ich vorschlagen kann; Ein bisschen lang, aber es funktioniert definitiv (mit einigen Vorbehalten):
Schritt 1: Benutzerdefinierte OnClick-Implementierung
Dies führt eine fragmentierte Suche durch Kontexte durch, die mit der angetippten Ansicht (z. B. Schaltfläche) verbunden sind:
// 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
}
Hinweis: basiert lose auf der ursprünglichen Android-Implementierung (siehe https://Android.googlesource.com/platform/frameworks/base/+/a175a5b/core/java/Android/view/View.java#3025 )
Schritt 2: Deklarative Anwendung in Layout-Dateien
Dann, in datenbindungsfähigen XML's:
<layout>
<data>
<import type="com.example.CustomOnClick"/>
</data>
<Button
android:onClick='@{(v) -> CustomOnClick.onClick(v, "myClickMethod")}'
</Button>
</layout>
Vorbehalte
- Geht von einem 'modernen'
FragmentActivity
basierte Implementierung
- Kann nur die Nachschlagemethode "top-most" (d.h. zuletzt ) Fragment im Stapel (obwohl das kann falls erforderlich, repariert werden)
1 Stimmen
Können Sie die Registrierung von Zuhörern nicht innerhalb des onCreate des Fragments vornehmen?
26 Stimmen
@jodes Ja, aber ich möchte nicht die
setOnClickListener
yfindViewById
für jede Schaltfläche, deshalbonClick
wurde hinzugefügt, um die Dinge zu vereinfachen.4 Stimmen
Wenn ich mir die akzeptierte Antwort ansehe, denke ich, dass die Verwendung von setOnClickListener lockerer gekoppelt ist als das Festhalten am XML onClick-Ansatz. Wenn die Aktivität jeden Klick an das richtige Fragment "weiterleiten" muss, bedeutet dies, dass der Code jedes Mal geändert werden muss, wenn ein Fragment hinzugefügt wird. Die Verwendung einer Schnittstelle zur Entkopplung von der Basisklasse des Fragments ist dabei nicht hilfreich. Wenn das Fragment mit der richtigen Schaltfläche selbst registriert, bleibt die Aktivität völlig agnostisch, die besser Stil IMO ist. Siehe auch die Antwort von Adorjan Princz.
0 Stimmen
@smith324 muss Adriaan in diesem Punkt zustimmen. Probieren Sie Adorjans Antwort aus und sehen Sie, ob das Leben danach nicht besser ist.