I got this error when I try to biding RecyclerView
Error:(15, 22) Cannot find the setter for attribute 'app:items' with parameter type android.databinding.ObservableArrayList<com.toong.databindingdemo.recycler.UserViewModel on android.support.v7.widget.RecyclerView.
Here is my code:
<?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">
<data>
<variable
name="usersViewModel"
type="com.toong.databindingdemo.recycler.UsersViewModel" />
</data>
<android.support.v7.widget.RecyclerView
android:id="#+id/activity_users_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:items="#{usersViewModel.users}"
/>
</layout>
But in UsersViewModel I already have a public users array
package com.toong.databindingdemo.recycler;
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.databinding.ObservableArrayList;
public class UsersViewModel extends BaseObservable{
#Bindable
public ObservableArrayList<UserViewModel> users;
public UsersViewModel()
{
this.users = new ObservableArrayList<>();
}
}
I have clean and rebuild project but it still not working. How can I fix this error?
There is no property like app:items in RecyclerView. You need to create custom Binding method for that
#BindingAdapter("items")
public static void entries(RecyclerView recyclerView, String[] array) {
//write your code to set RecyclerView adapter.
}
change type of array to your required datatype.
Related
We use databinding in out project thus not instantiating controls in code, but doing that within the layout xml. We've done that throughout the app with all UI controls and it works great.
Problem is that I've now added a SwitchCompat control to my layout. It only has onClick (not firing) and it doesn't have onCheckedChange which you would expect from a SwitchCompat, right?
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="music.queue_ui.list.QueueListViewModel" />
</data>
<androidx.appcompat.widget.SwitchCompat
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="#{() -> viewModel.toggleHeader()}" />
</layout>
The toggleHeader never get called :-(
package music.queue_ui.list
import android.widget.Toast
import androidx.lifecycle.ViewModel
import io.reactivex.disposables.CompositeDisposable
class QueueListViewModel(
) : ViewModel() {
private val disposables = CompositeDisposable()
fun toggleHeader() {
// This never gets hit by SwitchCompat `onClick`.
// Why??????
Toast.makeText(
context, "SwitchCompat Clicked",
Toast.LENGTH_LONG
).show()
}
override fun onCleared() {
super.onCleared()
disposables.clear()
}
}
Anyone one knows of a good solution?
Use:
android:onCheckedChanged="#{(v,checked)->viewModel.toggleHeader(checked)}"
I am new to android and trying out the example of data binding as below. The text is not displayed, rather an empty box is displayed.
build.gradle :
databinding {
enabled = true
}
HelloWorld.java :
package com.example.android.databinding;
public class HelloWorld {
public String getText() {
return "My Name is ";
}
}
MainActivity.java :
package com.example.android.databinding;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import com.example.android.databinding.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
HelloWorld mViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
activityMainBinding.setViewModel(mViewModel);
activityMainBinding.executePendingBindings();
}
}
activity_main.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"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.example.android.databinding.HelloWorld" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{viewModel.text}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Output :
You need to instantiate the HelloWorld class before sending it via DataBinding
mViewModel = new HelloWorld();
activityMainBinding.setViewModel(mViewModel);
Also, you don't have a filed text in HelloWorld class, so android:text="#{viewModel.text}" won't get compiled.
So you need to add it, and set its value, I am using constructor in that:
public class HelloWorld {
String text;
public HelloWorld() {
text = getText();
}
public String getText() {
return "My Name is ";
}
}
I am trying to include a layout that contains the only recyclerView in it.
home_recycler_view.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/home_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:paddingStart="#dimen/_8sdp"
android:paddingEnd="#dimen/_8sdp" />
And include this in the main layout like below.
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="#+id/slider_recyclerView"
layout="#layout/home_recycler_view" />
<include
android:id="#+id/boards_recyclerview"
layout="#layout/home_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/_12sdp" />
<include
android:id="#+id/news_recyclerview"
layout="#layout/home_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/_12sdp" />
<include
android:id="#+id/video_recyclerview"
layout="#layout/home_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/_12sdp"
android:layout_marginBottom="#dimen/_2sdp" />
</LinearLayout>
In this case, it gives me an error like,
java.lang.NullPointerException: Missing required view with ID: homeRecyclerView
Generated Class Of home_recycler_view.xml
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewbinding.ViewBinding;
import com.backendme.arduinolearn.R;
import java.lang.NullPointerException;
import java.lang.Override;
import java.lang.String;
public final class HomeRecyclerViewBinding implements ViewBinding {
#NonNull
private final RecyclerView rootView;
#NonNull
public final RecyclerView homeRecyclerView;
private HomeRecyclerViewBinding(#NonNull RecyclerView rootView,
#NonNull RecyclerView homeRecyclerView) {
this.rootView = rootView;
this.homeRecyclerView = homeRecyclerView;
}
#Override
#NonNull
public RecyclerView getRoot() {
return rootView;
}
#NonNull
public static HomeRecyclerViewBinding inflate(#NonNull LayoutInflater inflater) {
return inflate(inflater, null, false);
}
#NonNull
public static HomeRecyclerViewBinding inflate(#NonNull LayoutInflater inflater,
#Nullable ViewGroup parent, boolean attachToParent) {
View root = inflater.inflate(R.layout.home_recycler_view, parent, false);
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
#NonNull
public static HomeRecyclerViewBinding bind(#NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
String missingId;
missingId: {
RecyclerView homeRecyclerView = rootView.findViewById(R.id.home_recycler_view);
if (homeRecyclerView == null) {
missingId = "homeRecyclerView";
break missingId;
}
return new HomeRecyclerViewBinding((RecyclerView) rootView, homeRecyclerView);
}
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
}
if I wrap the recyclerView with any parent layout like frame layout than it works fine but I don't want to wrap my recyclerView in any other view.
please provide me a solution for this how can I include layout with a direct child in this case (RecyclerView) without any root or parent viewGroup in Viewbinding?
When you give ID to <include> means you are setting the id to root view of the layout you're including. In this case, you're setting the id to your recycler view and that's why It can not find the recycler view with its original ID. What you need to do is
use <merge> within the shared layout.
Wrap your RecyclerView in <merge>.
Don't give <include> any ID.
Call the bind() method of the generated class of merge layout and pass the root view of the layout you included your layout in.
Access your view from the object of merge binding
For example, you have home_recycler_view.xml so you'll have HomeRecyclerView.class generated and this is how you will access the view from this layout.
class TestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityTestBinding.inflate(layoutInflater)
val homeRecyclerBinding= HomeRecyclerView.bind(binding.root)
setContentView(binding.root)
homeRecyclerBinding.homeRecyclerView.adapter= SomeAdapter()
}
}
Why is the function setVm(user) error?
This is MainActivity.java:
package com.example.heriprastio.databindingandroid;
import android.databinding.DataBindingUtil;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.example.heriprastio.databindingandroid.databinding.ActivityMainBinding;
import com.example.heriprastio.databindingandroid.model.User;
public class MainActivity extends AppCompatActivity {
private User user;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
user = new User();
user.setName("Heri");
user.setEmail("heriprastio#gmail.com");
binding.setVm(user);
}
}
This is my model class:
package com.example.heriprastio.databindingandroid.model;
public class User {
String name;
String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
And this is my "activity_main" file 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<data>
<variable
name="vm"
type="com.example.heriprastio.databindingandroid.MainActivity"/>
</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="#{vm.text}"
android:textSize="16sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{vm.email}"/>
</LinearLayout>
</layout>
In the function setVm(user) cannot be applied, why is this? Can you explain, about this error?
And I have to solve this error or replace it with what?
You've declared your activity as variable type in the layout, but in the activity code you're passing an User instance as layout variable. To make it working, you have to make next changes:
In your layout file change
type="com.example.heriprastio.databindingandroid.MainActivity"
to
type="com.example.heriprastio.databindingandroid.model.User"
Moreover, I see that you're using "#{vm.text}" variable in the layout, even though you don't have such method or field in the User class. I suppose you wanted to use the user's name instead. So you must change it to "#{vm.name}"
I am working in MVVM design pattern.
I want to use dataBinding or RXJava in order to notify the View when the model has changed.
dataBinding can do it inside xml.
but i want to notify the Activity on a change in the model and do something more complicated.
let's assume i want my TextView to change color when text in not empty.
can you help me to do it via dataBinding or RXJava ?
here is my code:
xml
<?xml version="1.0" encoding="utf-8"?>
<data>
<variable
name="viewModel"
type="edi.com.mydatabindingsimple.MyViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="#+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:text="#={viewModel.txt}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#={viewModel.txt}" />
</LinearLayout>
Activity
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import edi.com.mydatabindingsimple.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
MyViewModel viewModel = new MyViewModel();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setViewModel(viewModel);
}
}
viewModel
import android.databinding.BaseObservable;
import android.databinding.Bindable;
public class MyViewModel extends BaseObservable{
private String txt;
#Bindable
public String getTxt() {
return txt;
}
public void setTxt(String txt) {
this.txt = txt;
notifyPropertyChanged(edi.com.mydatabindingsimple.BR.txt);
}
}
What you can do is create a callback mechanism to trigger a method in your Activity. This will still notify the ViewModel first, but the effect will be the same.
You can create a new interface (or add it inside the ViewModel)
public interface ViewModelCallback{
void ActionCallback();
}
In your Activity:
public class MainActivity extends AppCompatActivity implements ViewModelCallback
In your ViewModel
private ArrayList<ViewModelCallback> callbacks = new ArrayList<>();
public void notifyCallbacks(){
for(ViewModelCallback c : callbacks){
if(c != null){
c.ActionCallback
}
}
}
public void addCallback(ViewModelCallback c){
callbacks.add(c);
}
Call notifyCallback after your NotifyPropertyChanged().
And don't forget to add your activity as a callback in onCreate
viewModel.addCallback(this)
You should also remove it in onDestroy.