How does one accept methods as values, in attributes? Like in the onClick attribute for a View:
<Button android:onClick="onClickMethod"/>
How to define custom attributes that accept methods?
I know we use <declare-styleable> in resources, but how do we make it accept methods?
Android uses reflection to find the name of the method and invoke it. You can see an example in the source starting at line 4209 https://github.com/android/platform_frameworks_base/blob/master/core%2Fjava%2Fandroid%2Fview%2FView.java#L4209
case R.styleable.View_onClick:
if (context.isRestricted()) {
throw new IllegalStateException("The android:onClick attribute cannot "
+ "be used within a restricted context");
}
final String handlerName = a.getString(attr);
if (handlerName != null) {
setOnClickListener(new DeclaredOnClickListener(this, handlerName));
}
break;
If the method name isn't null, it creates a new DeclareOnClickListener() class and initializes it with the method name.
The DeclareOnClickListener() class is defined at line 4435
https://github.com/android/platform_frameworks_base/blob/master/core%2Fjava%2Fandroid%2Fview%2FView.java#L4435
I solved this problem with: BindingAdapter and Lambda
1️⃣ ➖ implement CustomView with lambda getter function onItemClick
class CustomView(
context: Context,
attrs: AttributeSet
) : View(context, attrs) {
fun onItemClick(block: () -> Unit) {
block() |or| block.invoke()
}
2️⃣ ➖ add data binding and kapt in build.gradle(:app)
plugins {
id 'kotlin-kapt'
}
android {
...
buildFeatures {
dataBinding = true
}
...
}
3️⃣ ➖ implement BindingAdapter
@BindingAdapter("onItemClick")
fun CustomView.onItemClick(block: () -> Unit) {
this.onItemClick(block)
}
4️⃣ ➖ use BindingAdapter
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="activityMain"
type="com.veldan.MainActivity" />
</data>
<com.veldan.CustomView
android:id="@+id/recycle"
android:layout_width="match_parent"
android:layout_height="match_parent"
onItemClick="@{() -> activityMain.onItemClick()}" />
</layout>
5️⃣ ➖ activity binding
class MainActivity : AppCompatActivity() {
private val TAG = this::class.simpleName
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater).also {
it.activityMain = this
setContentView(it.root)
}
}
fun onItemClick() {
Log.i(TAG, "onItemClick: ")
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With