I'm not able to get a onClick
method to work with a custom FrameLayout view.
When I refactor a default Button
view into a custom ProgressButton
view containing a Button
, the onClick
method doesn't get called.
Custom ProgressButton
view class:
class ProgressButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0,
defStyleRes: Int = 0
) : FrameLayout(context, attrs, defStyle, defStyleRes){
private var button: Button
init {
val view = LayoutInflater.from(context).inflate(R.layout.view_progress_button, this, true)
button = view.findViewById(R.id.button)
context.theme.obtainStyledAttributes(
attrs,
R.styleable.ProgressButton,
0, 0
).apply {
try {
if (hasValue(R.styleable.ProgressButton_buttonText)) {
button.text = getString(R.styleable.ProgressButton_buttonText)
}
if (hasValue(R.styleable.ProgressButton_buttonBackground)) {
button.background = getDrawable(R.styleable.ProgressButton_buttonBackground)
}
} finally {
recycle()
}
}
}
}
ProgressButton
XML Layout file: res/layout/view_progress_button.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</FrameLayout>
Usage of ProgressButton
view with android:onClick
:
<com.example.ui.common.ProgressButton
android:id="@+id/progress_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.onProgressButtonClick()}"
app:buttonText="@string/test"
app:buttonBackground="@drawable/selectable_button_blue"
/>
What's the best way to pass a click listener to a custom view using Android databinding?
Assuming that you have dataBinding.enabled true
defined in your build.gradle. If not, check this out.
It doesn't look like your layout is currently a databinding layout. First you'd want to convert view_progress_button.xml
to a databinding layout like the following:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.your.package.YourViewModelClass" />
</data>
<FrameLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.ui.common.ProgressButton
android:id="@+id/progress_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.onProgressButtonClick()}"
app:buttonText="@string/test"
app:buttonBackground="@drawable/selectable_button_blue"
/>
</FrameLayout>
There should be an option in Android Studio that lets you auto-convert your layout. Do this by hitting alt + enter (Windows) or option + enter (OSX) on the root ViewGroup in your layout. In this case you'd want to hit that combination on your FrameLayout.
In your ProgressButton
class, you'd want to do something like this:
val binding: ViewProgressButtonBinding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.view_progress_button, null, false);
instead of:
val view = LayoutInflater.from(context).inflate(R.layout.view_progress_button, this, true)
Using the binding
object, you can do:
binding.setViewModel(yourViewModelInstance)
You can instantiate your ViewModel however, but inside your ViewModel class you should have onProgressButtonClick()
defined.
If I remember correctly, you might need to do something like this in your layout for the onClick
:
android:onClick="@{(v) -> viewModel.onProgressButtonClick()}"
because the lambda expects the view as a parameter.
Another note is that you no longer need to do findViewById
since you have access to that view like so:
binding.button