Validate edittext input using databinding library - android

I am using databinding library. I have a layout which have some edittext for getting basic user input like name, email, password etc. I want to validate these inputs inside the viewmodel on button click. I am a bit confused how to access edittext input on button click inside the view model.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="ViewModel"
type="me.example.model.LoginViewModel"/>
<EditText
android:id="#+id/name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="13sp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Submit"
android:onClick="#{() -> ViewModel.onSubmitClick()}"/>
</layout>
this is the button click method in the view model
fun onSubmitClick(){
Log.e("Clicked ", "True")
}
}

You can reference the EditText within the Button's binding expression:
android:onClick="#{() -> ViewModel.onSubmitClick(name.getText().toString())}"
One caveat is that snake casing becomes camel casing when doing this, so if your View's android:id is name_edit_text, you would use nameEditText.getText() within the binding expression.
Clicking the Button will then call this method in the ViewModel:
fun onSubmitClick(editTextString: String) {
Log.d("LOG", editTextString)
}

Related

Android string link not working with databinding

I am trying to use Databinding in combination with a XML String which has some links and a variable. The problem is that the links do not work anymore, when Databinding is used.
XML text
<data>
<variable
name="vm"
type="com.example.app.framework.ui.viewmodel.EmailViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/app_standard_agb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:linksClickable="true"
android:text="#{#string/app_agb(vm.btnText)}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</TextView>
</androidx.constraintlayout.widget.ConstraintLayout>
When I use android:text="#{#string/app_agb.concat(vm.btnText)}" I get a NullPointer Exception which is impossible because I use the variable somewhere else and it is 1000% not null. Just using android:text="#string/app_agb" works.
String
<string name="app_agb">
By clicking %1$s you accept the
privacy policies and
agbs from example..
</string>
Code in Fragment
app_standard_agb.movementMethod = LinkMovementMethod.getInstance()
EDIT
Does it make any difference that I include my app_standard_agb in my layout?
i think this is because of the LiveData doesn't emit Data until it get Observed, so the value of it is null before it get Observed, you can use #BindingAdapter and check if the value is not null like following code
put code bellow somewhere in your project(I've tried just in Kotlin):
#BindingAdapter("android:bindLink")
fun bindLink(view: TextView, link: String?) {
link?.let{
view.text = view.context.getString(R.string.app_agb, it)
}
}
and the XML should be like this:
<TextView
android:id="#+id/app_standard_agb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:linksClickable="true"
android:bindLink="#{vm.btnText}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">

About creating own attributes for pre-existing Widgets like Edittext, TextView

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

Identifiers must have user defined types from the XML file, databinding with observablefield

I want my view visibility to be dependent on condition behaviour so I am using ObservableField and with databinding trying to change view visibility but getting issue like "Identifiers must have user defined types from the XML file. InputType is missing it"
Code:
Kotlin File
var showView: ObservableField<Boolean>? = ObservableField(false)
//API call response
showView.set(true)
Layout File:
<TextView
android:id="#+id/textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="#{viewModel.showView ? View.VISIBLE : View.GONE}"/>
How to apply databinding with Observablefield of type boolean? I have used it for string text also and it's working but not working with boolean conditional statement.
I am not sure if that's the case here, but this error message is usually displayed when you reference a type in your binding expressions that hasn't been declared in the <data> section of your layout. The same way you declare the View type as an import, you should declare the type InputType.
<data>
<!-- Maybe an import for InputType is missing here? -->
<import type="android.view.View" />
<variable
name="viewModel"
type="com.yourpackage.YourViewModel"/>
</data>

How to pass contents of other layout Views into presenter in onclick with data binding?

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())}"
... />

Android data Binding - ObservableArrayMap how to implement in a POJO

From the observable objects section of the android blog on data binding
The POJO class should extend BaseObservable unless your using ObservableFields. Let's look at my current POJO which is very simple and just stores an observableInt:
public class User {
public ObservableInt visible;
public User(int visible) {
this.visible=new ObservableInt(visible);
}
}
I want to add an Observable Collection to this pojo but im not clear how to layout the xml if i do that. How would i call it. Lets say i want to add the current arraymap to my POJO:
ObservableArrayMap<String, Object> myCollection = new ObservableArrayMap<>();
myCollection.put("firstName", "Google");
myCollection.put("lastName", "Inc.");
myCollection.put("age", 17);
How do i incorporate this into my xml for data binding. My xml file is simple and looks like this:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="user" type="com.example.android.floatingactionbuttonbasic.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="my first textview"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="my second textview"
android:visibility="#{user.visible}"/>
</LinearLayout>
</layout>
I am confused how i would add variables for ObservableArrayMap into this xml file and be able to use it. Can someone help ?I feel i can pass it into the User POJO fine, its just from xml i dont know how i'd reference it? The reason im confused is because the ObservableArrayMap is going to be a attribute of the User class, how do i access it since its going to be a member of the User class ?
You can access the map values like any other public observable from your xml.
For example:
public class Model{
public ObservableArrayMap<Integer,String> MyMap=new ObservableArrayMap<>();
public Model(){
MyMap.put(1,"easy");
}
}
Access the value from your xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="model"
type="de.example.models.Model"/>
</data>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="#dimen/padding"
android:text="#{model.MyMap.get(1)}"/>
</layout>

Categories

Resources