Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
7.8k views
in Technique[技术] by (71.8m points)

android - ViewStub element can't find the BindingAdapter

I have an XML layout with a ViewStub. I created a binding adapter setLayout to set which layout the ViewStub will inflate.

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <import type="android.view.View"/>
        <variable
            name="viewModel"
            type="com.smellydogcoding.westvirginiaelectronicfieldguidedistrict.ui.codeDetail.CodeDetailViewModel" />
    </data>

    <ScrollView
        android:id="@+id/codeDetailsScrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <ViewStub
                android:id="@+id/retailfood_viewStub"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:visibility="visible"
                app:setLayout="@{viewModel.searchRuleset}"
                app:viewModel="@{viewModel}"/>
        </LinearLayout>
    </ScrollView>
</layout>

The BindingAdapter

// set the layout for code details ViewStub
@BindingAdapter("setLayout")
fun ViewStub.setLayout(rule: String) {
    layoutResource = when (rule) {
        "retailfood" -> R.layout.stub_code_details_retail_food
        "general" -> R.layout.stub_code_details_general
        "recwater" -> R.layout.stub_code_details_recwater
        "citations" -> R.layout.stub_code_details_citations
        else -> R.layout.stub_code_details_other
    }
}

I keep getting an error in the generated binding file:

cannot find symbol variable setLayout

if (this.retailfoodViewStub.isInflated()) this.retailfoodViewStub.getBinding().setVariable(BR.setLayout, viewModelSearchRulesetGetValue);
                                                                                              ^
  symbol:   variable setLayout
  location: class BR

It seems like the XML layout can't find the Binding Adapter

Edit

Here is the file that the Binding Adapter is in. The Binding Adapter is not in a companion object or other named object.

package com.smellydogcoding.westvirginiaelectronicfieldguidedistrict.ui

import android.app.Application
import android.view.ViewStub
import android.webkit.WebView
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat.getColor
import androidx.databinding.BindingAdapter
import com.smellydogcoding.westvirginiaelectronicfieldguidedistrict.R
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory

// sets the background image for the program grid view recyclerview
@BindingAdapter("backgroundResource")
fun bindImageResource(imageView: ImageView, imagePath: String) {
    imageView.setImageResource(when (imagePath) {
        "code" -> R.drawable.code
        "restaurant" -> R.drawable.restaurant
        "hotelroom" -> R.drawable.hotelroom
        "well" -> R.drawable.well
        "septic" -> R.drawable.septic
        "pool" -> R.drawable.pool
        "tattoo" -> R.drawable.tattoo
        "mobilehomepark" -> R.drawable.mobilehomepark
        "daycare" -> R.drawable.daycare
        "raccoon" -> R.drawable.raccoon
        "tornado" -> R.drawable.tornado
        else -> R.drawable.code
    })
}

// sets the content for the code details WebViews
@BindingAdapter("webViewData")
fun WebView.webViewData(url: String?) {
    url?.let {
        settings.textZoom = 110
        loadDataWithBaseURL(null, url, "text/html", "base64", null)
    }
}

// changes the font color for code details TextViews
@BindingAdapter("changeTextColor")
fun TextView.changeTextColor(text: String?) {
    text?.let {
        when (text) {
            "Priority", "Yes" -> setTextColor(getColor(context, R.color.error)
            )
            "Priority Foundation", "Potential" -> setTextColor(getColor(context, R.color.warning)
            )
        }
    }
}

// set the layout for code details ViewStub
@BindingAdapter("setLayout")
fun ViewStub.setLayout(rule: String) {
    layoutResource = when (rule) {
        "retailfood" -> R.layout.stub_code_details_retail_food
        "general" -> R.layout.stub_code_details_general
        "recwater" -> R.layout.stub_code_details_recwater
        "citations" -> R.layout.stub_code_details_citations
        else -> R.layout.stub_code_details_other
    }
}

// load JSON assets to use with a recyclerview
object JsonAsset {
    lateinit var application: Application
    inline fun <reified T: Any> getJson (name: String): List<T>? {

        val moshi = Moshi.Builder()
            .add(KotlinJsonAdapterFactory())
            .build()
        val json = application.assets.open(name).bufferedReader().use{ it.readText() }
        val listType = Types.newParameterizedType(List::class.java, T::class.java)
        val adapter: JsonAdapter<List<T>> = moshi.adapter(listType)

        return adapter.fromJson(json)
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

So this question is kind of a repeat of This Stack Overflow Question

but there are a couple differences.

tl;dr; You can't.

You can't use a Binding Adapter with a ViewStub because when the compiler creates the binding file it will look for an adapter for the view that the ViewStub is inflating, not for the ViewStub itself. Therefore it will never find an adapter, and throw an error. IMHO this design doesn't make sense. If I wanted to apply Binding Adapters to the view that I'm inflating I would have made them.

The SO question above does mention a round about way to create one. Unfortunately, it only allows you to apply it after the ViewStub layout is inflated via an onInflateListener, and I have to set the layout before the ViewStub is inflated.

I found a solution by removing android:visibility="visible" and app:setLayout="@{viewModel.searchRuleset}" from the ViewStub XML layout and programmatically setting the layout for the stub in the corresponding fragment:

// set the layout for the ViewStub and inflate
val viewStubProxy : ViewStubProxy = binding.codeDetailsViewStub
viewStubProxy.viewStub?.layoutResource = when (viewModel.searchRuleset.value) {
    "retailfood" -> R.layout.stub_code_details_retail_food
    "general" -> R.layout.stub_code_details_general
    "recwater" -> R.layout.stub_code_details_recwater
    "citations" -> R.layout.stub_code_details_citations
    else -> R.layout.stub_code_details_other
}
viewStubProxy.viewStub?.inflate()

If you're using data binding you have to target the ViewStubProxy instead of the ViewStub itself.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...