How can I get '#id/view_name' dynamically for databinding? - android

I have a TextView that I want to change the position by other view.
For example,
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv"
...
app:layout_constraintBottom_toTopOf="#id/btn_to_follow"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
I have a button btn_to_follow, btn_to_follow2, btn_to_follow3.
So, I'd like to change app:layout_constraintBottom_toTopOf attribute dynamically with those three values.
I have a data class for state.
data class ButtonState(type: String){
val followPosition = R.id.btn_to_follow
}
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv"
...
app:layout_constraintBottom_toTopOf="#{vm.followPosition}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
I tried to change followPosition with 'R.id.xxx' values which are int values. It didn't work.
So, How can I approach what I expect??

I think what you are missing is "Live" portion of it.
app:layout_constraintBottom_toTopOf="#{vm.followPosition}"
It works, it does get the value from the followPosition in the ViewModel where followPosition is an Int.
But there isn't any implicit way how it gets updated. View was inflated and it no longer cares what is in followPosition.
You need followPosition to be LiveData<Int> or the parent data class to be stored in LiveData.

Related

View won't update in dialog after creation

Surely a simple question to ask but I've tried for hours and I can't seem to get the problem !
I have a DialogFragment which contains a
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/interval_input_layout"
style="#style/Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:boxStrokeWidth="0dp">
<AutoCompleteTextView
android:id="#+id/time_interval_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
tools:ignore="LabelFor" />
</com.google.android.material.textfield.TextInputLayout>
This is the listener set on this AutoCompleteView
binding.untilInput.onItemClickListener =
AdapterView.OnItemClickListener { parent, view, position, id ->
when (position) {
0 -> {
binding.numberLayout.visibility = View.GONE
}
1 -> {
binding.numberLayout.visibility = View.GONE
}
2 -> {
binding.numberLayout.visibility = View.VISIBLE
}
}
}
While number layout is just a linear layout like this
<androidx.appcompat.widget.LinearLayoutCompat
android:id="#+id/number_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="16dp">
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="For number of events"
android:textColor="?android:textColorSecondary" />
</androidx.appcompat.widget.LinearLayoutCompat>
At first the listener wasn't being called so I set it like this instead of directly calling the function setOnItemClickListener (don't know why that wasn't working) , Now the listener is being called , I even put a breakpoint and debugged it and its setting visibility but its not taking any effect and it does not cause any error so I can't seem to get the problem here
Using a DialogFragment I inflated the view layout with the use of databinding in the method OnCreateView & then in OnViewCreated I worked with my view and caused changes to UI and set listeners to it and again cause changes to UI
in OnCreateDialog I called both of these methods onCreateView (to get the view and set it to dialog) and onViewCreated to setup the view
I tried to find the view using findViewById just to ensure if the databinding was working fine and it was working fine but visibility still won't change and the view won't update !
I still have't gotten to the root of the problem but it seems you have to store the view inside a variable so you don't lose a reference to it when onClickListener is called and in my opinion just when the onClickListener is called getting the view from the binding becomes invalid and it should but the small problem is that I am not calling onDestroy to make the binding null because the binding was invalid at that time !
Now I am just storing a refrence to binding like this
val myvar = binding.monthLayout
and I am capturing the value of myvar inside the onClickListener rather than using the binding

Android BindAdapter with custom attribute with custom values (similar to visible attribute)

I've successfully made my first bind adapter and I wish to know a bit more about it.
I want to know how to make an attribute that can get only specific strings for a different state for my view.
For example every view has the visibility attribute that it can be"gone", "visible", "invisible"
<TextView
android:id="#+id/loading_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="#id/inventory_items_recycler"
app:layout_constraintEnd_toEndOf="#+id/inventory_items_recycler"
app:layout_constraintStart_toStartOf="#id/inventory_items_recycler"
app:layout_constraintBottom_toBottomOf="#id/inventory_items_recycler"
android:textSize="18sp"
android:visibility="gone"
app:item_id="#{ItemID.BLACK_GLOVES.ordinal()}"
/>
I've made a custom attribute called item_id that get a number that represent enum value. And in my binding utils I have this code:
#BindingAdapter("item_id")
public static void setItemName(TextView tv, int itemId) {
tv.setText(ItemData.get(ItemID.values()[itemId]).getName());
}
I prefer to have something similar to the visibility attribute that it value can be either "visible", "invisible" or "gone"
Bonus::
I wish android studio can auto-complete me for the possibilities that I can use.
You could pass directly the enum to your binding adapter, instead of converting it first to an int and than back to enum.
#BindingAdapter("item_id")
public static void setItemName(TextView tv, ItemID itemId) {
..
}
Then you could pass directly the enum in your xml:
app:item_id="#{ItemID.BLACK_GLOVES}"
This way you'll have a limited number of possibilities to enter and will be less likely to accidentally enter a meaningless integer.
However, binding adapters and custom attibutes are different. With a binding adapter, you still need to use the syntax of binding expression, ie: "#{ }".
android:visibility , on the other hand, is an attribute. You can also define custom attributes for your custom views and get something similar (have a limited number of input options and IDE shows you your options etc). But you shouldn't confuse that with binding adapters. These are two different concepts.
Try this:
#BindingAdapter("isGone")
#JvmStatic
fun View.setVisibility(isGone: Boolean) {
if (isGone) this.visibility = View.GONE else View.VISIBLE
}
Inside your xml:
<com.google.android.material.checkbox.MaterialCheckBox
android:id="#+id/cb_class"
style="#style/TextStyleNormal.White"
android:layout_marginStart="#dimen/margin_large"
isGone="#{isSharedDailyActivity}"// it take boolean value
app:buttonTint="#color/white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:useMaterialThemeColors="true" />

MutableLiveData doesn't work, ObservableField does - why?

I have a very simple code in XML:
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:text="#={viewModel.password}"
android:enabled="#{viewModel.inputEnabled}">
Now when viewModel.inputEnabled is a MutableLiveData<Boolean> underneath, it simply doesn't work, edit is always enabled, regardless of value of inputEnabled. However, it takes only to change inputEnabled to an ObservableField<Boolean> (and switch setValue to set) such that it immediately starts working.
Why is that so? How can I make MutableLiveData work correctly?
Note, that this field is used in three places (to enable/disable form during processing).
I ran in to same issue....make sure to call following in your activity/fragment:
binding.setLifecycleOwner(this)

Continues updating view with data binding

I am using data binding for setting data to recyclerview item and I am using #BindingAdapter for custom attrinbutes.
Now i want to update the text of a textview in each second. The textview is displaying a text(time ago) with related to a timestamp, i have created a #BindingAdapter function by passing the timestamp, which works perfectly for setting the text single time, but i want to update it in each second.
<TextView
android:id="#+id/tvTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12sp"
android:textStyle="italic"
app:timeago="#{order.DateStamp}"
app:layout_constraintBottom_toTopOf="#id/tvOrderId"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Just now" />
Following is my binding adapter
#BindingAdapter("timeago")
public static void setTimeAgo(TextView view, long time) {
view.setText(getTimeAgo(time));
}
this is my layout file
You can use EchoBind class, BindingAdapter for this.
The binding should be in two way from XML to model/adapter or model/adapter to XML.
To get detailed documentation refer these link1 & link2.

EditText LiveData Two-way binding

Okay, so I have a ViewModel with a getter getTitle() that returns MutableLiveData<String>.
<EditText
style="#style/Widget.EditText.FullWidth"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/label_title"
android:inputType="text"
android:text="#={ viewModel.title }" />
This works fine at first: the EditText contains the value of the MutableLiveData when it first appears. However, if the value of this data is updated using MutableLiveData.setValue() (such as by another EditText, or from my code), then the value inside of the text box does not change. How do I fix this?
This works properly in the new version of Android Studio, which supports binding to LiveData objects properly.

Categories

Resources