1

I'm trying to use 2-way databinding on a custom view that contains a SeekBar. The layout is rather simple, but I need to reuse it across the project, hence wrapping it into a custom view/component

<androidx.constraintlayout.widget.ConstraintLayout   ... />

<TextView .../>
<TextView .../>

<SeekBar
    android:id="@+id/ds_seekbar"
    android:layout....
    android:max="9"
    android:min="0"
    android:progress="0"
   </androidx.constraintlayout.widget.ConstraintLayout>

The backing code looks like so (reduced)

CustomView @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0

) : ConstraintLayout(context, attrs, defStyleAttr), View.OnClickListener {

    init {
        LayoutInflater.from(context).inflate(R.layout.custom_view, this, true)
        ds_description.setOnClickListener(this)

    }

    override fun onClick(view: View) {
//onClick implementation 
        }
}

I can do the binding in the ViewModel for the layout where this custom view is going to be used, with a BindingAdapter there with custom attribute (ex. app:seekbar), but the custom view would be used multiple times and I'd prefer to have the a lot of the logic that is required into the view and have a "lighter" handling in the ViewModel.

I read Android 2-Way DataBinding With Custom View and Custom Attr and a bunch of other articles which seem to be a little different but oon the same topic, however no matter how I wrote the getter and setters I always run into the kapt exception that it cannot find the getter/setter.

Either I'm not annotating properly the methods or they have wrong signatures.

Ideally I want to have something like:

   CustomView @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0

) : ConstraintLayout(context, attrs, defStyleAttr), View.OnClickListener, SeekBar.OnProgressChangedListener {

... ds_seekbar.setOnProgressChangedListener(this)

And then in the main layout have the app:progress (or even better if someone can show how it's done android:progress) on the custom view for binding when passing my object.

1 Answer 1

3

Okay after more and more headscratching, here's what I've come with, that seems to work. Whether this is the proper way or how performant/reliable is - I'm not sure

@InverseBindingMethods(InverseBindingMethod(type = CustomView::class, attribute = "progress", event = "progressAttrChanged"))
CustomView @JvmOverloads constructor(...

private var progress = 0
private var mInverseBindingListener: InverseBindingListener? = null


cv_seekbar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
        override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) {
            progress = i + 1
            if (mInverseBindingListener != null) {
                mInverseBindingListener!!.onChange()
                cv_indicator.text = progress.toString()
            }
        }...
   })

fun getProgress(): Int {
    return progress
}

fun setProgress(p: Int) {
    if (progress != p) {
        progress = p
    }
}

fun setProgressAttrChanged(inverseBindingListener: InverseBindingListener?) {
    if (inverseBindingListener != null) {
        mInverseBindingListener = inverseBindingListener
    }
}

Then the XML is

 <com.xxx.CustomView
                android:id="@+id/xxx"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:progress="@={viewModel.dataobject.value}"
          ....
                />
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.