Android: <declare-styleable> 方法属性
Android: <declare-styleable> method attributes
如何在属性中接受方法作为值?
就像在视图的 onClick 属性中一样:
<Button android:onClick="onClickMethod"/>
如何定义接受方法的自定义属性?
我知道我们在资源中使用 <declare-styleable>
,但是我们如何让它接受方法呢?
Android 使用反射找到方法的名称并调用它。您可以在第 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;
如果方法名称不为空,它会创建一个新的 DeclareOnClickListener()
class 并使用方法名称对其进行初始化。
DeclareOnClickListener()
class 在第 4435 行定义
https://github.com/android/platform_frameworks_base/blob/master/core%2Fjava%2Fandroid%2Fview%2FView.java#L4435
我用以下方法解决了这个问题:BindingAdapter 和 Lambda
1️⃣ ➖ 实现 CustomView with lambda getter 函数 onItemClick
class CustomView(
context: Context,
attrs: AttributeSet
) : View(context, attrs) {
fun onItemClick(block: () -> Unit) {
block() |or| block.invoke()
}
2️⃣ ➖ 在build.gradle(:app)
中添加数据绑定和kapt
plugins {
id 'kotlin-kapt'
}
android {
...
buildFeatures {
dataBinding = true
}
...
}
3️⃣ ➖ 实现 BindingAdapter
@BindingAdapter("onItemClick")
fun CustomView.onItemClick(block: () -> Unit) {
this.onItemClick(block)
}
4️⃣ ➖ 使用 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绑定
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: ")
}
如何在属性中接受方法作为值? 就像在视图的 onClick 属性中一样:
<Button android:onClick="onClickMethod"/>
如何定义接受方法的自定义属性?
我知道我们在资源中使用 <declare-styleable>
,但是我们如何让它接受方法呢?
Android 使用反射找到方法的名称并调用它。您可以在第 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;
如果方法名称不为空,它会创建一个新的 DeclareOnClickListener()
class 并使用方法名称对其进行初始化。
DeclareOnClickListener()
class 在第 4435 行定义
https://github.com/android/platform_frameworks_base/blob/master/core%2Fjava%2Fandroid%2Fview%2FView.java#L4435
我用以下方法解决了这个问题:BindingAdapter 和 Lambda
1️⃣ ➖ 实现 CustomView with lambda getter 函数 onItemClick
class CustomView(
context: Context,
attrs: AttributeSet
) : View(context, attrs) {
fun onItemClick(block: () -> Unit) {
block() |or| block.invoke()
}
2️⃣ ➖ 在build.gradle(:app)
中添加数据绑定和kaptplugins {
id 'kotlin-kapt'
}
android {
...
buildFeatures {
dataBinding = true
}
...
}
3️⃣ ➖ 实现 BindingAdapter
@BindingAdapter("onItemClick")
fun CustomView.onItemClick(block: () -> Unit) {
this.onItemClick(block)
}
4️⃣ ➖ 使用 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绑定
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: ")
}