Cannot find the setter for attribute with parameter - android

I am working on DataBinding with BindingAdapter. Here is my custom method.
#BindingAdapter("{bind:fadevisible}")
public static void setFadeVisible(LinearLayout view, int visible) {
Log.e("Bindings", "setFadeVisible: ");
}
And in xml file i am calling it like
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:fadevisible="#{1}"/>
But it is showing error
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
java.lang.RuntimeException: Found data binding errors.
****/ data binding error ****msg:Cannot find the setter for attribute 'app:fadevisible' with parameter type int on android.widget.LinearLayout.
file:\app\src\main\res-main\layout\activity_detail.xml
loc:236:31 - 236:54
****\ data binding error ****
I have checked this and this thread but somehow it is not helping me, as you can see i am passing int from xml and in BindingAdapter also i have mentioned LinearLayout with int value.
Even i have another method, where just parameters are different and its working fine
#BindingAdapter({"bind:image_round"})
public static void loadRoundImage(ImageView imageView, String url)

Make sure in app level gradle, you have apply plugin: 'kotlin-kapt'

Your #BindingAdapter definition looks a little bit odd to me
#BindingAdapter("{bind:fadevisible}")
This is not the same like
#BindingAdapter({"bind:fadevisible"})
or
#BindingAdapter("bind:fadevisible")
which should work perfectly fine.

I had this problem with binding to ImageView and unlike your case, the definition of my binding adapter was correct but still, the IDE kept giving me this error message. After spending many hours on searching for the cause, I figured that the namespace that I use in xml layout file needs to be exactly what I declared in #BindingAdapter.
So, if my xml is like below:
<ImageView
android:id="#+id/logo"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignParentRight="true"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
app:image_url="#{item.logoUrl}"
/>
Then my binding method should be as below:
#BindingAdapter({"app:image_url"})
public static void loadImage(ImageView view, String logoUrl) {
if (logoUrl == null) {
view.setImageResource(R.drawable.ic_place_holder);
} else {
Glide.with(getContext()).load(logoUrl).crossFade().into(view);
}
}
Note that binding method annotation indicates the namespace in it , i.e. #BindingAdapter({"app:image_url"}) exactly as it is used in layout file app:image_url="#{item.logoUrl}"
So unlike what is said in most tutorials, don't use #BindingAdapter({"bind:image_url"}) in your binding method and app:image_url="#{item.logoUrl}" in your xml file.

You try
#BindingAdapter("bind:fadevisible")

I had initially set defined my customBindidingAdapter as private:
#BindingAdapter("setPriorityColor")
private static void getPriorityColor(TextView textView, int priority) {
}

In my particular case, my BindingAdapter had two parameters, with requireAll, and I had neglected to put one of them on the element in my layout XML. So, like this: (Kotlin, I know)
#BindingAdapter("app:arg1", "app:arg2", requireAll = true)
fun MyAdapter(view: ImageView, x: String, y: Int) {
// ...
}
<Element app:arg1="#{"foo"}"/>
The error was roughly Cannot find the setter for attribute "app:arg1" with parameter String which is perfectly true, there is no such adapter; there's only one for two args.
One hint that this was happening was that Android Studio indicated that MyAdapter was an unused function by coloring it grey.
Obviously a more eloquent error message like "there is no adapter for app:arg1 of type String but there is one for..." (when one of the attribute names matches) would be appreciated, but I won't hold my breath.

Add on to the answers if you are working on multiple modules then where you have
#BindingAdapter("fadevisible")
That module should have the following code in the module -> build.gradle.
dataBinding {
enabled = true
}
Enjoy Happy coding. :)

Apart from #BindingAdapter improvements
(mine were working fine in one build and not in another),
upgrading the Build gradle version to the latest one worked for me.

Related

Binding adapter - cannot find setter for LiveData variable in recycler view

I'm trying to implement RecyclerView with GridLayoutManager and I'm stuck.
I'm receiving error:
If a binding adapter provides the setter, check that the adapter is annotated correctly and that the parameter type matches.
Open File"
When I'm trying to compile. "Open file" provides me to xml recycler view declaration.
This is my BindingAdapter:
#BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView,
data: List<MarsPropertyData>?) {
val adapter = recyclerView.adapter as PhotoGridAdapter
adapter.submitList(data)
}
How I'm calling the above:
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/photos_grid"
android:clipToPadding="false"
app:layoutManager=
"androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
tools:itemCount="16"
tools:listitem="#layout/grid_view_item"
app:listData="#{viewModel.properties}"
/>
And this is my properties variable:
val properties: LiveData<List<MarsPropertyData>>
I'm understand that IDE complains about data types, my properties var it is LiveData<List<MarsPropertyData>> and inside BindingAdapter there's just normal List, but I saw example from google in which logic was made in same way and it worked fine.
Yes, I have added 'kotlin-kapt' plugin, I have another BindingAdapter which works fine.
I experienced a similar problem while working on a google codelab whose setup is very similar to yours.
In my case, I inadvertently defined the "bindRecyclerView" bindingAdapter function within the curly braces of a previous bindingAdapter function. This resulted in the "bindRecyclerView" function being eclipsed by the previous function and hence its setter cannot be seen from other files. When I removed it from within the previous function, everything worked fine.

Exposed Dropdown Menus - with no filter from xml resource

Context
When you use the following object: MaterialAutoCompleteTextView or AutoCompleteTextView you can specify if the text filter the result or not. You can do this using the method setText(CharSequence text, boolean filter) and i quote:
boolean: If false, no filtering will be performed as a result of this
call.
.. and the method works perfectly!
The question
Is it possible to do the same thing directly from xml? Let me explain, using the data binding I am able to set a value via the android:text xml attribute. Like this:
<MaterialAutoCompleteTextView
android:text="#{viewmodel.data}" />
Since I don't have the option to specify whether I actually want to filter the results or not, it uses the default setting which is true.. unfortunately for me I would like to use false.
More info
I did some research using the official documentation but found nothing in the pages that reference the MaterialAutoCompleteTextView and AutoCompleteTextView.
You should be able to create an BindingAdapter and set those values using Databinding without no issues at all.
For example, you could create something like:
#BindingAdapter("app:text", "app:filter")
fun MaterialAutoCompleteTextView.updateFilter(text: String, filter: Boolean) {
this.setText(text, filter)
}
And then, use databinding in your xml as:
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="#+id/autocomplete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:filter="#{ here comes filter value }"
app:text="#{ here comes text value }" />
Keep in mind that I didn't run this code so there may be some typo issues, but I guess you get the idea.
Another thing to consider is in Kotlin code I used an extension function, you can easily change that and pass MaterialAutoCompleteTextView as first parameter.
How I've implemented:
Following hardartcore answer, I've look into binding adapter and their best practice. Inside my model class I've added a method that has been referenced for actualy binding the data using the function I've defined.
public class Model {
...
#BindingAdapter(value = {"text", "filter"})
public static void setText(MaterialAutoCompleteTextView view, String text, Boolean filter) {
view.setText(text, filter);
}
}
value specify the arguments that are needed to use your function, you can also use requiredAll = false to tell that maybe there will be some null values. In my case i always need both so inside the xml:
<com.google.android.material.textfield.MaterialAutoCompleteTextView
...
app:filter="#{false}"
app:text="#{mode.variable}" />

Binding implementation for XML layout is not generated if 2-way Data Binding through Converters is used

I have an AppCompatAutoEditTextView in my layout. This view is populated from an enum class called FlavorType using ArrayAdapter.
Now, I want to implement 2-way DataBinding, with the text attribute being bound to a MutableLiveData. For this to work, I implemented the following Converter:
object Converter {
#InverseMethod(value = "flavor_str_to_enum")
fun flavor_enum_to_str(view: AppCompatAutoCompleteTextView, value: FlavorType): String {
return when (value) {
FlavorType.SWEET -> view.context.getString(R.string.label_flavor_sweet)
FlavorType.SAVORY -> view.context.getString(R.string.label_flavor_savory)
FlavorType.NONE -> view.context.getString(R.string.label_none)
else -> view.context.getString(R.string.label_none)
}
}
fun flavor_str_to_enum(view: AppCompatAutoCompleteTextView, value: String): FlavorType {
return when (value) {
view.context.getString(R.string.label_flavor_sweet) -> FlavorType.SWEET
view.context.getString(R.string.label_flavor_savory) -> FlavorType.SAVORY
view.context.getString(R.string.label_none) -> FlavorType.NONE
else -> FlavorType.NONE
}
}
}
After this, in my XML for my view, I added the following line:
android:text="#={Converter.INSTANCE.flavor_enum_to_str(viewmodel.flavor)}
where, flavor is of type MutableLiveData. Also, I added import tags for my Converter class at the top of the XML.
Now, for some reason unknown to me, just by adding the above line in my xml, my build starts failing with a cannot find symbol error that the BindingImpl associated with my XML was not found. I looked in the generated code folders and yes, the Fragment...BindingImpl for my xml is not there while for other layouts they are present. Just by removing the above line of code, everything starts working again. I have tried everything, invalidating, restarting, etc. But, this issue still seems to persist.
Can anyone provide insights into, what could be happening or what I might be doing wrong?
EDIT:
Running gradle build with --stacktrace, I get the following error:
[databinding] {"msg":"cannot find method flavor_enum_to_str(dev.example.myapp.model.FlavorType) in class dev.example.myapp.utils.Converter","file":"app\\src\\main\\res\\layout\\fragment_addrecipe.xml","pos":[{"line0":119,"col0":41,"line1":119,"col1":86}]}
This leads me to believe that it's trying to look up the above mentioned function signature, but instead it finds:
flavor_enum_to_str(AppCompatAutoCompleteTextView, FlavorType)
I followed the documentation and implemented things the way it described.

If there is no attribute namespace in the data binding, intellisense will not work

There is a very simple bindingadapter function.
#JvmStatic
#BindingAdapter("app:test")
fun testBind(v: View, test: Int) {
//test...
}
If you apply this code in xml, it will usually look like this:
autocomplete function works fine, and xml does not print any warnings.
However, this bindingadapter function outputs a warning at compile time.
warning: Application namespace for attribute app:test will be ignored.
Many other posts say remove namepsace for this warning.
I removed the namespace from the bindingadapter function along with it.
#JvmStatic
#BindingAdapter("test")
fun testBind(v: View, test: Int) {
//test...
}
Doing so will not print out the warning at compile time.
But this time, xml prints a warning.
Also, when the namespace exists, the autocomplete function that worked normally does not work at all.
Of all the methods I've tried, the only way to resolve all two warnings is to specify the namespace as android.
is there any other way? android namespace seems to be a misunderstanding as this is a basic binding feature in Android, not a custom binding function.
If you are using single argument in binding adapter method, remove namespace in #BindingAdapter(...) string, after that add bind: namespace before calling string in xml.
#JvmStatic
#BindingAdapter("icon")
fun setImage(view: ImageView, imageID: Int) {
}
and bind:icon="#{vm.iconID}"
If I'm using several attributes it's not worked.

Android Studio Warn about application namespace

When I compile a project in which I use BindingAdapter, the Android Studio always show lots of warning and jump into the source file which I write code of BindingAdapter.How can I solve it, I never want to show it when I compile and also I don't want to remove the namespace app or other, can anyone help me. Thank you!!! The Warning is below:
The example source code:
As #Michael Spitsin pointed out in the comment, just remove the "app:" namespace from the annotation. You can keep the "app:" namespace in your layout XML. The namespace will be removed internally unless it is android:; all other namespaces are treated the same.
The warning serves to inform you that the namespace does not have any effect on the annotation. So, for example, you cannot have different methods for #BindingAdapter("app:src") and #BindingAdapter("foo:src") -- the namespace is removed before it's used as a key in the implementation of that annotation. The only exception is the android namespace; you can have #BindingAdapter("android:src") and also #BindingAdapter("app:src") as well.
If you look through the source code that implements the #BindingAdapter annotation, you'll see that the namespace is removed, which is the reason for the warning. For example, in this excerpt from android.databinding.tool.store.SetterStore.addBindingAdapter:
public void addBindingAdapter(ProcessingEnvironment processingEnv, String attribute,
ExecutableElement bindingMethod, boolean takesComponent) {
attribute = stripNamespace(attribute);
...you can see that the namespace is stripped, like so:
private static String stripNamespace(String attribute) {
if (!attribute.startsWith("android:")) {
int colon = attribute.indexOf(':');
if (colon >= 0) {
attribute = attribute.substring(colon + 1);
}
}
return attribute;
}
This is done because that the binding adapter needs to work across any and all source files in the project, regardless of what string was used for the namespace in a particular XML file.

Categories

Resources