I have a view that uses DataBinding to display data from a ViewModel. The view has a button that is hidden when the view is in landscape mode. At the moment this works by having two layoutfiles, but I would like to only have one as it's a nightmare to refactor. But how to go about this using DataBinding and BindingAdapters?
How do you enable the following bindingexpression for a given view?
android:visibility="#{isLandscapeMode ? View.INVISIBLE : View.VISIBLE}"
EDIT:
My extension property definition (ViewUtils.kt):
val View.isLandscapeMode: Boolean
get() = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
And my 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>
<import type="android.view.View"/>
<import type="com.example.base.views.ViewUtilsKt" />
<variable
name="isLandscapeMode "
type="java.lang.Boolean"/>
<variable
name="viewmodel"
type="com.example.ui.task.TaskViewModel" />
</data>
...
<ImageButton
...
android:visibility="#{isLandscapeMode ? View.INVISIBLE : View.VISIBLE}"
...
/>
This causes a compile error: Cause: not a valid name: isLandscapeMode
You have to check in your binding adapter which phone orientation is currently on. Here is another post where you can find answer to your question how to detect orientation of android device?
EDIT:
You need to detect orientation and save it as boolean value. Later you have to pass that variable to your adapter which in this case will be boolean.
<data>
<import type="android.view.View"/>
<variable
name="isLandscapeMode"
type="boolean"/>
</data>
For others if they need same behavior. There are two solutions.
First solution (create a BindingAdapter and use it on the UI element of your choice):
#BindingAdapter("bind:visibleInLandscapeMode")
fun View.setVisibleInLandscapeMode(visible: Boolean) {
Timber.d("setVisibleInLandscapeMode() returns ${resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE}")
visibility = if (visible && (resources.configuration.orientation != Configuration.ORIENTATION_LANDSCAPE)) VISIBLE else INVISIBLE
}
In your XML 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"
xmlns:bind="http://schemas.android.com/apk/lib/com.example">
...
<ImageButton
...
bind:visibleInLandscapeMode="false"
...
/>
Second solution (create a bindingexpression that changes the visibility of the UI element):
<ImageButton
...
android:visibility="#{context.getResources.getConfiguration.orientation == Configuration.ORIENTATION_LANDSCAPE ? View.INVISIBLE : View.VISIBLE}"
...
/>
Remember the correct imports:
<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>
<import type="android.view.View"/>
<import type="android.content.res.Resources" />
<import type="android.content.res.Configuration" />
...
</data>
...
Btw. be careful when using Databinding with AndroidAnnotation. See issue here and here.
Related
I have included a layout with an EditText into my host-layout. The imeOptions of the EditText in the include-layout should be configurable from my host-layout. Therefore I added a variable and the hook in the include-layout.
My problem is that in the host-layout I want to set the actual flag as value like this
app:customImeOptions="#{actionNext}"
By doing so I get the error "Cannot find identifier 'actionNext'". The only thing that works is setting the integer value.
app:customImeOptions="#{0x00000006}"
The Include-Layout
<data>
...
<variable
name="customImeOptions"
type="Int"/>
</data>
<ConstraintLayout
...
...>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="#{customImeOptions}/>
</ConstraintLayout>
The Host Layout
<ConstraintLayout
...
...>
<include
layout="#layout/include_layout"
app:customImeOptions="#{0x00000006}" />
To set the flag in the host layout one must import the corresponding class in the host-layout. In this case add this import in the data section of the host-layout
<data>
<import type="android.view.inputmethod.EditorInfo" />
</data>
Now that the host-layout knows this class it is possible to set the flag instead of the int-value like this
<include
layout="#layout/include_layout"
app:customImeOptions="#{EditorInfo.IME_ACTION_NEXT}" />
Iv'e got a recyclerView with items using databinding.
The model contains multiple values that together determine the icon/state of the list item.
so i tried to add a variable to the XML that contains a helper function to return a R.drawable that needs to be shown based on different values in the model
ListItem.xml
<data>
<import type="android.view.View"/>
<variable name="model" type="com.example.model"></variable>
<variable name="helper" type="com.example.helper"></variable>
</data>
...
<TextView ...
android:text="#{model.a}" /> <<< THIS UPDATES LIKE EXPECTED
<ImageView ...
app:imageResource="#{helper.getIcon(model)}"/> <<< THIS IS NOT UPDATING AFTER THE VALUES CHANGES
...
helper.kt
fun getIcon(model:Model){
return if(model.a == true){
if(model.b == true){
R.drawable.b
}else{
R.drawable.a
}
}else{
R.drawable.c
}
}
if the model values changes the getIcon is not triggered. (only when i scroll up and down)
vm has a isWin value which can have only 0 or 1.
I'd like to take advantage of Data Binding for this.
I access vm in the <data> and I connected like this:
#{vm.isWin == 1 ? #drawable/win_true : #drawable/win_false}
However, this seems doesn't work.
I get this with red underline:
'!=', '%', '==', '(', ..., '>=', '>>' or '>>>' expected, got ':'
This is reference by Google.
full activity_main.xml is this:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="vm"
type="io.monolabs.asscnfc.vm.ResultViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src = "#{vm.isWin == 1 ? #drawable/win_true : #drawable/win_false}"/>
</LinearLayout>
</layout>
I had also face this issue. resolved by removing 'is' keyword from variable name.javabeans trying generate same name getter and setter. As 'is' comes before method name only.
Have two complex layouts and one is included into another.
Trying to pass list of some custom objects into included layout, but build fails with following error:
Error:Execution failed for task ':core:compileDebugJavaWithJavac'.
> java.lang.RuntimeException: Found data binding errors.
****/ data binding error ****msg:cannot find type element for List
file:D:\myproject-android\core\src\main\res\layout\view_header.xml
loc:101:27 - 101:42
****\ data binding error ****
view_header.xml:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="vm"
type="com.catalogz.app.ui.categories.ItemViewModel" />
</data>
<FrameLayout>
<include layout="#layout/include_title_view" bind:items="#{vm.items}/>
</FrameLayout>
...
</layout>
include_title_view.xml:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="com.catalogz.app.utils.Strings" />
<import type="com.catalogz.app.app.entities.Item" />
<import type="java.util.List"/>
<variable
name="items"
type="List<Item>" /> <!-- generic List<Item> -->
</data>
<TextView android:text="#{Strings.convert(items)}"/>
</layout>
It's possible to move this problem from view_header.xml to include_title_view.xml by removing generic definition <Item> but then it appears in the Strings.convert(items) call as methods expects a list of Item and layout passes List<Object> as I understand:
Error:Execution failed for task ':core:compileDebugJavaWithJavac'.
> java.lang.RuntimeException: Found data binding errors.
****/ data binding error ****msg:cannot find method convert(java.util.List) in class com.catalogz.app.utils.Strings
file:D:\myproject-android\core\src\main\res\layout\include_title_view.xml
loc:51:32 - 51:61
****\ data binding error ****
I also have the same problems, but I found an alternative for setting the value inside the included layout. I know it is not a good practice, at least it is the work around for the setting variable for the include layout with data binding.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="vm"
type="com.catalogz.app.ui.categories.ItemViewModel" />
</data>
<FrameLayout>
<include
android:id="#+id/part1"
layout="#layout/include_title_view" />
</FrameLayout>
...
</layout>
In Activity class
ActivityMainBinding.part1.setItems(myItems);
Hope this can be helpful.
I am using the latest data binding in android using android studio 2.1.
using the visibility tag as describe in the below code getting an error as
java.lang.RuntimeException: Found data binding errors.
/ data binding error ****msg:Identifiers must have user defined types from the XML file. View is missing it
file:D:\HP\HealthPortal_Android\Code\app\src\main\res\layout\cardview_image_twotextview.xml
loc:68:90 - 68:93
\ data binding error
<TextView
android:id="#+id/card_sub_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/card_title"
android:layout_marginLeft="#dimen/carview_margin"
android:layout_toRightOf="#+id/card_image"
android:text="#{toolsAndTrackersCards.subtitle}"
android:textColor="#color/black"
android:textSize="20sp"
android:visibility="#{toolsAndTrackersCards.subtitle.equals(#string/Empty_String) ? View.VISIBLE : View.GONE}"
/>
Done some google not abel to find the solution. The #string/Empty_String is define as empty string "" in string.xml file. where I am doing wrong.
Android data binding, Radio Button not updating
Add this to your cardview_image_twotextview.xml:
<data>
<import type="android.view.View" />
<!--your variables-->
</data>
To hide a view if the string is empty, use the below expression in data binding
<data>
<import type="android.view.View"/>
<variable
name="item"
type="com.test.model.Item" />
</data>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{item.title}"
android:visibility='#{item.title.equals("") ? View.GONE : View.VISIBLE}'/>
NOTE: need to use outer single quote string in order to use double quote to
represent the empty string
If you want to check for null and empty, use the below code :
<data>
<import type="android.view.View"/>
<import type="android.text.TextUtils"/>
<variable
name="item"
type="com.test.model.Item" />
</data>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{item.title}"
android:visibility="#{TextUtils.isEmpty(item.title) ? View.GONE : View.VISIBLE}"/>
Zero or more import elements may be used inside the data element.
These allow easy reference to classes inside your layout file, just
like in Java.
you need to import View class inorder to use its properties.
<data>
<import type="android.view.View"/>
</data>
you can also refer official DataBinding guideline.