app:visibleGone cannot resolve on android Databinding - android

When I build the project. I'm getting error on app:visibleGone
I'm also enable true to dataBinding in build.gradle and using android architecture components and mvvm.
project targetSdkVersion is 26 and support lib version is 26.0.1.
Below is the error message
error: package com.****.****.databinding does not exist
error: cannot find symbol class ActivityMainBinding
Cannot find the setter for attribute 'app:visibleGone' with parameter type boolean on android.widget.Button.
here is my activity_main.xml
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="loading"
type="boolean" />
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.aungmyolwin.importdb.MainActivity">
<Button
android:id="#+id/btn_load_sql"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Load from SQL"
app:visibleGone="#{!loading}"/>
<Button
android:id="#+id/btn_load_room"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Load from Room mapper"
app:visibleGone="#{!loading}"/>
<TextView
android:id="#+id/tv_import_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Importing database...."
app:visibleGone="#{loading}"/>
</LinearLayout>
</layout>
ActivityMain.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.btnLoadRoom.setOnClickListener(this);
binding.btnLoadSql.setOnClickListener(this);
viewModels= ViewModelProviders.of(this).get(MainActivityViewModels.class);
}
}

You need to create a custom BindingAdapter for app:visibleGone (because it is not a available method).
Like
public class BindingAdapters {
#BindingAdapter("visibleGone")
public static void showHide(View view, boolean show) {
view.setVisibility(show ? View.VISIBLE : View.GONE);
}
}
Moreover, if you don't want to define a method like this, you can do like
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="loading"
type="boolean" />
<import type="android.view.View"/> <!-- remember to import -->
</data>
<LinearLayout >
<Button
android:visibility="#{loading ? View.GONE : View.VISIBLE}"
</LinearLayout>
</layout>

Related

Pass data to <include> layout using Databinding

I've been searching this a lot today and only found some unclear answers regarding this question.
I'm having multiple TextView and EditText in a form, all of them being customised by my theme and i'd like to reuse the same included layout each time, but having parameters to it like the text inside the TextView or hint inside the EditText.
My form.xml right now:
<LinearLayout android: orientation="horizontal">
<TextView android:text="Username"/>
<EditText android:id="edittext_username"
android:hint="Enter username..."
android:inputType="text"/>
</LinearLayout>
<LinearLayout android: orientation="horizontal">
<TextView android:text="Password"/>
<EditText android:id="edittext_password"
android:hint="Enter password..."
android:inputType="password"/>
</LinearLayout>
... other fields ...
What i'd like in the main form.xml:
<LinearLayout android:orientation="vertical" ...>
<include layout="form_edittext"
app:textview_text="#{`Username`}"
app:edittext_hint="#{`Enter username...`}"
... other parameters ...
/>
<include layout="form_edittext"
app:textview_text="#{`Password`}"
app:edittext_hint="#{`Enter password...`}"
... other parameters ...
/>
</LinearLayout>
also the form_edittext.xml:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="textview_text" type="java.lang.String"/>
<variable name="edittext_id" type="java.lang.String"/>
<variable name="edittext_inputType" type="java.lang.String"/>
<variable name="edittext_hint" type="java.lang.String"/>
</data>
<TextView android:text="#{textview_text}"/>
<EditText android:id="#{edittext_id}"
android:hint="#{edittext_hint}"
android:inputType="#{edittext_inputType}"/>
</layout>
I'm still a beginner and i do not know if this is possible.
On this post, the guy replied he used data binding (as in example shown by me)
How to Re-using Layouts with <include/> with parameters? (you may see the first answer).
However, using this method give me the error of XML not recognizing identifiers like "textview_text" (the variables that i'm accessing via #{...}. If you got other solutions i'd appreciate if you share them. Cheers!
UPDATE on iCantC's answer: (but the textview's text and the hint remain empty).
Main layout:
<?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"
xmlns:tools="http://schemas.android.com/tools">
...
<include layout="#layout/activity_login_layout_input"
android:id="#+id/activity_login_layout_input_emailAddress"
app:textviewt="#{#string/email}" //the string is "Email Address"
app:edittexth="#{#string/emailhint}" /> //the string is "Your email address..."
...
</layout>
Included layout:
<layout>
<data>
<variable
name="textviewt"
type="String"/>
<variable
name="edittexth"
type="String"/>
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:showDividers="middle">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{textviewt}"
android:textColor="#color/c1"
android:textSize="12sp"/>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingEnd="26dp">
<androidx.appcompat.widget.AppCompatEditText
android:id="#+id/edittext"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_gravity="center|start"
android:layout_marginStart="26dp"
android:background="#drawable/activity_login_background"
android:hint="#{edittexth}"
android:padding="6dp"
android:textAlignment="center"
android:textColor="#color/c2"
android:textColorHint="#color/grey"
android:textSize="14sp"/>
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_gravity="start"
android:adjustViewBounds="true"
android:src="#drawable/activity_login_edittext_drawable" />
</FrameLayout>
</LinearLayout>
</layout>
It seems like you want to include a common layout and then pass dynamic parameters from the main layout to included layout, here are the steps,
Step 1: Enable DataBinding in your project
//In the build.gradle file in the app module, add this
android {
...
dataBinding {
enabled = true
}
}
Step 2: Create your common_layout_included.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="username"
type="String"/>
</data>
<LinearLayout
..
>
<TextView
android:id="#+id/tv_username"
android:text="#{username}"/>
</LinearLayout>
</layout>
Step 3: Include common_layout_included.xml in your main_layout.xml
<?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">
<LinearLayout
..
>
<include
android:id="#+id/includedLayout"
layout="#layout/common_layout_included"
app:username="#{#string/username}" // here we pass any String
/>
</LinearLayout>
</layout>
Step 4: Make sure you inflate the layout using DataBinding way
//In the onCreate of your Activity
val binding = DataBindingUtil.setContentView(this,R.layout.main_layout)
That's it, you are good to go. If still some error appears just do File -> Invalidate Caches/Restart
One last thing, I saw that you are assigning the id's to the view's dynamically android:id="#{edittext_id}", in all of my experience, I never really encountered a use case where I would be motivated to do this. I don't even know if it's possible and even if possible I doubt it's a good practice.

Android data binding problem: "Cause: couldn't make a guess for"

i'm enabling data binding with adding
dataBinding {
enabled = true
}
and
kapt 'com.android.databinding:compiler:3.1.4'
in app level build.gradle file.
apply plugin: 'kotlin-kapt'
is added top of that. project is based on kotlin .
here is my model:
package ir.app.myapplication;
data class cisclass(val equRevId:String)
main Activity :
val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
val user = cisclass("123")
binding.setVariable(BR.cis, user)
binding.executePendingBindings()
here is my layout:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
>
<data>
<variable
name="cis"
type="ir.app.myapplication.cisclass" />
</data>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:orientation="vertical"
android:scrollbars="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:text="#{cis.equRevId}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</layout>
i will got this error:
java.lang.IllegalArgumentException: couldn't make a guess for ir.meedc.myapplication.cisclass
at com.squareup.javapoet.Util.checkArgument(Util.java:64)
at com.squareup.javapoet.ClassName.bestGuess(ClassName.java:171)
at android.databinding.tool.ext.ExtKt.toTypeName(ext.kt:244)
at android.databinding.tool.ext.ExtKt.toTypeName(ext.kt:192)
at android.databinding.tool.ext.ExtKt.toTypeName(ext.kt:173)
at android.databinding.tool.writer.BaseLayoutBinderWriter.createVariableFields(BaseLayoutBinderWriter.kt:229)
at android.databinding.tool.writer.BaseLayoutBinderWriter.access$createVariableFields(BaseLayoutBinderWriter.kt:39)
at android.databinding.tool.writer.BaseLayoutBinderWriter$createType$1.invoke(BaseLayoutBinderWriter.kt:67)
at android.databinding.tool.writer.BaseLayoutBinderWriter$createType$1.invoke(BaseLayoutBinderWriter.kt:39)
at android.databinding.tool.ext.Javapoet_extKt.classSpec(javapoet_ext.kt:39)
at android.databinding.tool.writer.BaseLayoutBinderWriter.createType(BaseLayoutBinderWriter.kt:63)
at android.databinding.tool.writer.BaseLayoutBinderWriter.write(BaseLayoutBinderWriter.kt:59)
at android.databinding.tool.BaseDataBinder.generateAll(BaseDataBinder.kt:65)
at com.android.build.gradle.internal.tasks.databinding.DataBindingGenBaseClassesTask$CodeGenerator.run(DataBindingGenBaseClassesTask.kt:212)
at com.android.build.gradle.internal.tasks.databinding.DataBindingGenBaseClassesTask$writeBaseClasses$$inlined$recordTaskAction$1.invoke(AndroidVariantTask.kt:52)
at com.android.build.gradle.internal.tasks.databinding.DataBindingGenBaseClassesTask$writeBaseClasses$$inlined$recordTaskAction$1.invoke(AndroidVariantTask.kt:31)
at com.android.build.gradle.internal.tasks.Blocks.recordSpan(Blocks.java:91)
how can i fix that? whats the problem?
as mentioned in https://stackoverflow.com/a/50115802/7407809
your model class must be start with uppercase letter.
Try to use instead binding.setVariable(BR.cis, user) this binding.setCis(user)

Android data binding attribute not found

I am trying to replicate this answer: Setting attribute of child element of included layout
I have a simple custom_edit_text.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="hint123" type="String" />
</data>
<android.support.design.widget.TextInputLayout
android:id="#+id/emailInputLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<android.support.v7.widget.AppCompatEditText
android:id="#+id/emailField"
android:layout_width="275dp"
android:layout_height="wrap_content"
android:paddingBottom="16dp"
android:paddingTop="14dp"
android:hint="#{hint123}"
android:textCursorDrawable="#null"
android:background="#drawable/edit_text_background"
android:fontFamily="#font/eina_regular"
android:textColor="#color/edit_text_color"
android:textColorHint="#color/edit_text_color"
android:textSize="15sp"
/>
</android.support.design.widget.TextInputLayout>
</layout>
And I include it in another file:
<?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">
<include
layout="#layout/custom_edit_text"
app:hint123="Email"/>
</layout>
However the project refuses to compile after a clean & rebuild with the error:
AAPT: error: attribute hint123 (aka inc.company.appname:hint123) not found.
Any ideas?
I also have
dataBinding {
enabled = true
}
enabled in the app level build.gradle
I think I've hit upon the solution. To activate data binding, you need to use a #{} expression, and what's in the braces must be valid Java code. So a literal string must be enclosed in quotes... which must be encoded as " inside an XML attribute value. Put it all together and you get:
<include
layout="#layout/custom_edit_text"
app:hint123="#{"Email"}"/>
Data binding does work for include files, it's just that the syntax for a literal is a bit convoluted. I had the same issue and this form is working in my project.
Bind the variables using #{ }
activity_main.xml
</layout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".simple.MainActivity">
<include layout="#layout/content_main_data_binding"
bind:name="#{ "Hello World" }" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
content_view.xml
</layout>
<data>
<variable
name="name"
type="String" />
</data>
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{ name }"/>
</layout>
GL
Problem is in the included layout. You can not set attribute hint123 in it.
Also, take a note that include is not supported as a direct child of layout tag.
Update your included XML code as below:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include android:id="#+id/custom_edit_text"
layout="#layout/custom_edit_text" />
</android.support.constraint.ConstraintLayout>
</layout>
To set hint using databinding, you have to set it in your java or kotlin file.
Here is java code:
public class YourActivity extends AppCompatActivity {
YourActivityBinding mBinding;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.your_activity);
mBinding.customEditText.setHint123("Email");
mBinding.customEditText.executePendingBindings();
}
}
I am using Android 3.1.1. And the following code is working for me, and if you can use it you will be able to reuse "hint" as you desired. I have a slightly altered layout file (custom_edit_text.xml) as follows.
<?xml version="1.0" encoding="utf-8"?>
<data>
<variable name="cName" type="String" />
<variable name="user" type="your.package.name.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{user.email}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{user.mobile}" />
<android.support.design.widget.TextInputLayout
android:id="#+id/emailInputLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<android.support.v7.widget.AppCompatEditText
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="#{cName.toString()}"
android:paddingBottom="16dp"
android:paddingTop="14dp"
android:textSize="15sp" />
</android.support.design.widget.TextInputLayout>
</LinearLayout>
Above the second "type" is package name + User class name.
I create the "User" class in a separate file as follows.
public class User {
String email;
String mobile;
User(String email, String mobile) {
this.email = email;
this.mobile = mobile;
}
public String getEmail() {
return email;
}
public String getMobile() {
return mobile;
}
}
Inside MainActivity inside onCreate() I create the user object create the string and bind them.
String email = "xyz#yahoo";
String mobile = "9999";
User user = new User(email,mobile);
CustomEditTextBinding binding = DataBindingUtil.setContentView(this,R.layout.custom_edit_text) ;
binding.setCName("Yam May");
binding.setUser(user);
And I enabled binding in the app level build.gradle as you did.
A very detailed description about data binding can be found in https://www.vogella.com/tutorials/AndroidDatabinding/article.html
Just in case anyone misses one detail like me: you must enclose your parent layout content and the included layout content within "" and "". Without that binding is not used in a layout. I had the same issue when I had not enclosed my parent layout with these tags.
There are 2 alternatives to the quote-escaping solution #big_m provides.
You can single-quote the entire expression and use double-quotes around the string:
<include
layout="#layout/custom_edit_text"
app:hint123='#{"Email"}'/>
Or you can use backticks around the string:
<include
layout="#layout/custom_edit_text"
app:hint123="#{`Email`}"/>

DataBinding causing a Kotlin compiler error

I have following branch in Bitbucket : https://bitbucket.org/ali-rezaei/tmdb/src/dataBinding/
I get following Kotlin compiler error when I build the project :
e: [kapt] An exception occurred: android.databinding.tool.util.LoggedErrorException: Found data binding errors.
Error is related to :
app:visibleGone="#{isLoaded}"
in the following layout :
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="isLoaded"
type="boolean" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.MainActivity">
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:visibleGone="#{isLoaded}" />
</android.support.v4.widget.SwipeRefreshLayout>
<include
layout="#layout/network_state_item"
app:visibleGone="#{!isLoaded}" />
</FrameLayout>
</layout>
I appreciate if you can help me out.
The changes I would do are: Here
<variable
name="isLoaded"
type="boolean" />
Instead of passing boolean I would pass an instance of your VM
<variable
name="vm"
type="com.sample.android.tmdb.ui.MovieViewModel" />
in your fragment, you do
mBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_main, container, false)
mBinding?.setVariable(BR.vm, mViewModel)
mBinding?.setLifecycleOwner(this)
this way, your VM is connected to the lifecycle of your fragment.
Declare a method
#BindingAdapter("visibleGone")
fun View.visibleGone(visible: Boolean) {
setVisibility(if (visible) View.VISIBLE else View.GONE)
}
declare a LiveData<Boolean> variable in you MovieViewModel and connect it in your layout. Eg.
val loading: LiveData<Boolean>
then in your layout you could have
<android.support.v7.widget.RecyclerView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:visibleGone="#{!safeUnbox(vm.loading)}" />
I had a slightly different problem that generated this error.
In my ViewModel, I had the following method:
`fun onSkip() {
_score.value = (_score.value)?.minus(1)
nextWord()
}`
Now, when i was setting the onClick attributes in my xml, I set them like this:
android:onClick="#{() -> gameViewModel.onSkip}"
instead of
android:onClick="#{() -> gameViewModel.onSkip()}"
Notice how I forgot to use the parenthesis in the first method.

How do I turn on data binding?

I have seen this code in one of Google's Android examples. Code example here:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="android.view.View" />
<variable
name="stats"
type="com.example.android.architecture.blueprints.todoapp.statistics.StatisticsViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
<!-- look at this -->
android:text="#{stats.status}"
android:visibility="#{stats.showStatus ? View.VISIBLE : View.GONE}" />
</LinearLayout>
</layout>
I wanted to use something like this for my own project but it did not compile. I suppose that it requires some libraries or something else but I could not find it. Any advice?
enable dataBinding in your build.gradle
android{
...
...
defaultConfig{
...
...
dataBinding{
enabled true
}
}
}

Categories

Resources