I'm using Data Binding Library. I have this in the XML:
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="#{!myAdapter.isEmpty}"/>
I want that EditText to be enabled only when Spinner adapter is not empty. When app starts, EditText is disabled. So far, so good.
Then, in my activity, items are inserted in the adapter. But after:
myAdapter.notifyDataSetChanged();
EditText is not enabled. Do I have to do anything more?
To have an ability to notify data binding about something you could use ObservableBoolean like this:
...
<variable name="isAdapterEmpty"
type="android.databinding.ObservableBoolean" />
...
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="#{!isAdapterEmpty}"/>
And then notify databinding about changes like this:
myAdapter.notifyDataSetChanged();
isAdapterEmpty.set(myAdapter.isEmpty());
Or you can make your own method inside your adapter that will return ObservableBoolean instead of simple boolean and provide ability to manage this value to adapter.
Related
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
Help! I want to know whether we can create own custom Attributes for pre-existing android components like EditText, TextView, AutoCompleteTextView, MultiAutoCompleteTextView etc.
Using XML I need to implement a custom attribute/property to an autocompletetextview so that it do not show autocomplete suggestions if property is set false, and vice versa.
Yes you can. There is this thing that is called binding adapters, and you could use those as new xml attributes. Well, ofcourse, you have to enable data binding on your project to make the binding adapters to work.
Read more here: https://developer.android.com/topic/libraries/data-binding/binding-adapters
Bonus: If you are using kotlin, you could instead make these binding adapters into extension functions such that you could use them as an extension function for your objects.
Update
To xml attributes for pre-existing widgets, you first need to define a custom binding adapter. Here is an example of a custom binding adapter:
// This will change the text views background color and text when it is tapped
#BindingAdapter("changeBackgroundAndTextOnTap")
public static void changeBackgroundAndTextOnTap(final TextView view, boolean shouldChange) {
// The first parameter is the type of view this xml attribute will be available to
// The second is the value you will receive from the xml attribute
if (shouldChange) {
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
view.setBackgroundColor(Color.HSVToColor(new float[]{(int)Math.round(Math.random() * 360), 0.8f, 0.4f}));
view.setText("" + (Math.random() * 10000000000L));
}
});
}
}
But before we could use this, we should first tell android that we are using data binding, so in your app level build.gradle file, add this line:
android {
...
dataBinding {
enabled true
}
...
}
Next, to make data binding work on your xml files, you first have to wrap your layouts inside tags, like so:
<?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">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Don't Click Me!"
android:gravity="center"
android:textSize="32sp"
android:padding="24dp"
android:textColor="#dedede"
android:background="#000000"
tools:context=".MainActivity" />
</layout>
Then, on your activity, or fragment, you should set content view using databinding util:
private ActivityMainBinding mBinding; //Optional
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
}
Now that you are all set, you could now use your custom xml attribute a.k.a. data binding adapter on your layouts like this:
<?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">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Don't Click Me!"
android:gravity="center"
android:textSize="32sp"
android:padding="24dp"
android:textColor="#dedede"
android:background="#000000"
changeBackgroundAndTextOnTap="#{true}" // Note: the #{} is necessary
tools:context=".MainActivity" />
</layout>
For an example project, here is a github repo:
https://github.com/jianastrero/Android-Data-Binding-Example-In-Java
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)
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.
Basically, how do I do this:
android:onClick="#{()->presenter.login(username.getText(), password.getText())}"
where username and password are EditText views in the layout whose contents I want to pass to the presenter. Is it necessary to set up two-way data binding to do this, or is there a way to refer to the contents of those other views within the layout?
I wonder if one way to do it is to enable two-way data binding and use a view model, e.g. LoginViewModel with fields for the username and password, set this as a variable on the, pass the whole thing to the login presenter when the form is submitted, and read it out of there.
Fortunately, you can access the text values from EditText because it supports two-way. You can do this:
android:onClick="#{()->presenter.login(username.text, password.text)}"
Unfortunately there is no way to access the values of the views within the layout itself using Databinding. The only way to do this is set these values inside variables in your layout file and access them using your presenter. eg:
Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.your_layout);
String username = mBinding.editText.getText().toString();
String password = mBinding.editText.getText().toString();
mBinding.setUserName(username);
mBinding.setpassword(password);
}
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="name"
type="java.lang.String" />
<variable
name="password"
type="java.lang.String" />
<Button
android:onClick="#{()->presenter.login(username, password)}"/>
Not exactly you are looking for but yes there is a similar way.
You can pass whole EditText in presenter and get a text from it.
android:onClick="#{()->presenter.login(edtUsername, edtPassword)}"
and inside your presenter
public void login(EditText edtUsername, EditText edtPassword)
{
}
You can access views in the same layout by their id:
<EditText android:id="#+id/username"
... />
<EditText android:id="#+id/password"
... />
<Button android:onClick="#{v->presenter.login(username.getText(), password.getText())}"
... />