3

I am using data binding in my current application, and so far so good. However, when I tried to use it with a custom data binding adapter I wrote for my custom view, I got an error from auto generated file as the title says, missing return statement. This error does not occur when I used this data binding on only one view, but more than one gives the error. Below are my custom view and adapters, and usage in xml file. I already checked the answer of kinda duplicated question but it doesn't worked in my case, and does not have enough explanation.

class NeonTextView(context: Context, attrs: AttributeSet) : TextView(context, attrs) {

private val drawableClear: Drawable?
    get() = ContextCompat.getDrawable(context, R.drawable.ic_clear)

lateinit var actionMethod: () -> Unit
lateinit var clearMethod: () -> Unit
var hasActionMethod = false
var hasClearMethod = false

init {
    setupAttributes(attrs)
}

private fun setupAttributes(attrs: AttributeSet) {
    val typedArray =
        context.theme.obtainStyledAttributes(attrs, R.styleable.NeonTextView, 0, 0)

    hasActionMethod = typedArray.getBoolean(
        R.styleable.NeonTextView_hasActionMethod,
        false
    )
    hasClearMethod = typedArray.getBoolean(
        R.styleable.NeonTextView_hasClearMethod,
        false
    )
    typedArray.recycle()
}

override fun onTextChanged(
    text: CharSequence?,
    start: Int,
    lengthBefore: Int,
    lengthAfter: Int
) {
    text?.let { text ->
        drawableClear?.let {
            it.setBounds(0, 0, it.intrinsicWidth, it.intrinsicHeight)
        }
        setCompoundDrawablesWithIntrinsicBounds(
            null,
            null,
            if (text.isNotEmpty()) drawableClear!! else null,
            null
        )
    }
}

override fun onTouchEvent(event: MotionEvent?): Boolean {
    event?.let {
        return when (it.action) {
            ACTION_DOWN -> return true
            ACTION_UP -> {
                if (compoundDrawables[2] == null && hasActionMethod) {
                    actionMethod()
                } else {
                    if (it.x > (width - paddingRight - compoundDrawables[2]!!.intrinsicWidth)) {
                        if (hasClearMethod) clearMethod()
                        text = ""
                    } else {
                        if (hasActionMethod) actionMethod()
                    }
                }
                performClick()
                true
            }
            else -> false
        }
    }.run {
        return false
    }
}

override fun performClick(): Boolean {
    super.performClick()
    return true
}
}

And here is my binding adapters for binding methods that are used inside this custom text view:

@BindingAdapter("actionMethod")
fun NeonTextView.setActionMethod(actionMethod: () -> Unit) {
    this.actionMethod = actionMethod
    this.hasActionMethod = true
}

@BindingAdapter("clearMethod")
fun NeonTextView.setClearMethod(clearMethod: () -> Unit) {
    this.clearMethod = clearMethod
    this.hasClearMethod = true
}

And here is how I applied inside xml file:

<com.android.platform.NeonTextView
                        android:id="@+id/textViewSectionField"
                        style="@style/HeaderTextView.SubHeader"
                        app:hasActionMethod="true"
                        app:actionMethod="@{() -> viewModel.getDepartmentList()}"/>

Any ideas why I am getting error from generated file when I used this binding in more than one view inside xml?

Thanks in advance.

denizt
  • 713
  • 5
  • 21

1 Answers1

2

The problem is with Java<->Kotlin compatibility. In Kotlin if you declare function

interface Func {
    fun test(): Unit {
    }
}

And use it from java

class FuncImpl implements Func {
    @Override
    public Unit test() {
        return Unit.INSTANCE;
    }
}

Note, please, that in that case in java code you need return statement. The same is for lambdas. So when you set up lambda from xml using databinding, it's treated as java's lambda, so generated code wasn't correctly processed in that case.

ConstOrVar
  • 2,025
  • 1
  • 11
  • 14
  • So is there a way to handle the approach currently I am trying? Or is it just a dead end, which needs to be handled by code? – denizt Feb 19 '19 at 08:36
  • Can you show source code of `viewModel.getDepartmentList()` function? – ConstOrVar Feb 19 '19 at 10:40
  • it calls following piece of code: private fun executeQuery(token: String, query: String) { sqlQueryUseCase(SqlQueryUseCase.Params(token, query)) { it.either(::handleErrorState, ::onDataListFetched) } } which one is a network call to a REST api, and retrieve the list from response and set to a MutableLiveData on view model itself. – denizt Feb 19 '19 at 11:30
  • So, as I understand, signature of method is `fun getDepartmentList(): Unit`. Is that right? – ConstOrVar Feb 19 '19 at 13:20
  • Can't reproduce your issue. I've copied all stuff from question, but have no errors. Am I missing something? – ConstOrVar Feb 19 '19 at 13:35
  • yep, its signature is as you stated, Unit. For some reason tough, on my side, when I tried it, it gave me the error. Probably something related with my project setup, I will try it with blank project to see what is going on. – denizt Feb 19 '19 at 13:53