I have simple layout and viewModel. I want to connect them with each other but they dont connect.Problem of above problem is in logs there is no error and my app also doesn't crash.
Here my layout:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="progressView"
type="uz.iutlab.ictnews.viewModel.ProgressViewModel" />
<variable
name="viewModel"
type="uz.iutlab.ictnews.viewModel.DetailFragmentViewModel" />
</data>
<RelativeLayout
android:id="#+id/activity_detail"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
tools:context="uz.iutlab.ictnews.view.fragment.DetailFragment">
<RelativeLayout
android:id="#+id/fragment_detail_post_root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="#dimen/collapsing_image_height">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.makeramen.roundedimageview.RoundedImageView
android:id="#+id/fragment_detail_image_post"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:src="#{viewModel.image}"
app:layout_collapseMode="parallax"
app:setContext="#{viewModel.context}" />
<android.support.v7.widget.Toolbar
android:id="#+id/fragment_detail_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_collapseMode="pin" />
<TextView
android:id="#+id/fragment_detail_title_post"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="#dimen/spacing_view_large"
android:text="#{viewModel.title}"
android:textColor="#android:color/white"
android:textSize="#dimen/font_size_title_huge" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
</RelativeLayout>
</RelativeLayout>
Here is my ViewModel class
public class DetailFragmentViewModel extends BaseObservable {
private Post mPost;
private Context mContext;
public DetailFragmentViewModel(Post mPost,Context mContext) {
this.mPost = mPost;
this.mContext = mContext;
}
public String getTitle() {
return mPost.getTitle().getRendered();
}
public Context getContext () {
return mContext;
}
public String getImage() {
if (mPost.getMedia().getSource_url() != null) {
return mPost.getMedia().getSource_url();
} else {
return null;
}
}
#BindingAdapter({"android:src","setContext"})
public static void downloadImage (RoundedImageView imageView,String url,Context context) {
if (url!=null) {
Picasso.with(context).load(url).into(imageView);
} else {
imageView.setImageResource(R.drawable.placeholder);
}
}
}
There is no error, There is no crashes. App works normally but there is no any title , any image.I tried this one instead overriding its method writing own but doesn't work.
#BindingAdapter({"bind:imageUrl","setContext"})
public static void downloadImage (RoundedImageView imageView,String url,Context context) {
if (url!=null) {
Picasso.with(context).load(url).into(imageView);
} else {
imageView.setImageResource(R.drawable.placeholder);
}
}
In addition to above . I also check it with debug, above methods are not called.
You should better remove app:setContext="#{viewModel.context}" and get it from the view in your adapter. Also you need to use attribute names without a namespace; so instead of bind:imageUrl only use imageUrl.
#BindingAdapter("imageUrl")
public static void downloadImage (RoundedImageView imageView, String url) {
if (url != null) {
Picasso.with(imageView.getContext()).load(url).into(imageView);
} else {
imageView.setImageResource(R.drawable.placeholder);
}
}
But since Picasso works asynchronously, you might end up with an image after you already set it to R.drawable.placeholder again.
Eventually you could also have a look at the generated java sources for the binding and see if your BindingAdapter is called somewhere.
try like this:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/apk/res-auto"
>
.....
<com.makeramen.roundedimageview.RoundedImageView
android:id="#+id/fragment_detail_image_post"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
bind:src="#{viewModel.image}"
app:layout_collapseMode="parallax"
bind:setContext="#{viewModel.context}" />
.....
</layout>
#BindingAdapter({"bind:loadUrl"})
public static void downloadImage(RoundedImageViewimageView, String url) {
if (url != null) {
Picasso.with(imageView.getContext()).load(url).into(imageView);
} else {
imageView.setImageResource(R.mipmap.ic_launcher);
}
}
The problem was not in my ViewModel or in my xml file everything is correct there is no syntax error.Problem is here this is fragment and I have connected them together my fragment with my viewModel.I made mistake in creating binding here you can see it. This is my not working one
FragmentStudentLifeBinding binding = DataBindingUtil.inflate(inflater,R.layout.fragment_student_life,container,false);
Here is correct one
FragmentStudentLifeBinding binding = DataBindingUtil.inflate(inflater,R.layout.fragment_student_life,container,true);
I missed to attach fragment to activity that is why my binding hadn't been working for 21 days.Hope it will help to someone.
Related
i made a simple hello world for 2 way data binding and seams works perfectly (when write on editext, the textview update automatically), but all code found online like official documentation has much more code and complications like https://developer.android.com/topic/libraries/data-binding/two-way
this is my code:
public class MainActivity extends AppCompatActivity {
public String pippo;
public Boolean visible = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DataBindingUtil.setContentView(this, R.layout.activity_main);
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="pippo"
type="String" />
<variable
name="visible"
type="Boolean" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#={pippo}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#{pippo}"
android:visibility="#{visible ? android.view.View.VISIBLE: android.view.View.GONE}" />
<CheckBox
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="#={visible}" />
</LinearLayout>
</layout>
In particular documentation use this things but seams useless:
BaseObservable
#Bindable
code to Avoids infinite loops
notifyPropertyChanged
so, what wrong or missing with my code?
In the two-way data binding you need to create class that extends from BaseObservable, annotate getters with #Bindable and call notifyPropertyChanged in your setters as below:
public class Person extends BaseObservable {
private String name;
Person(String name) {
this.name = name;
}
#Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
}
Then pass this class as a databinding layout variable of type Person.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="person"
type="com.example.android......Person" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#={person.name}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#{person.name}" />
</LinearLayout>
</layout>
Note: change the class path in the type attribute.
and then set this layout variable in your activity with setPerson()
public class ExampleActivity extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DataBindingUtil.setContentView(this, R.layout.activity_example);
ActivityExampleBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_example);
mActivityMainBinding.setPerson(new Person(""));
}
}
The most simple way to me is using #={variable} instead #{variable}
You can see it in:
https://developer.android.com/topic/libraries/data-binding/two-way
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#={viewModel.name}"
android:inputType="text" />
I have a test project where I want to bind the press of a button to trigger a function via the DataBinding Libray and add:command.
Unfortunately, I'm getting the error:
Found data binding errors.
****/ data binding error ****msg:Could not resolve com.example.ckleineidam.testproject.ViewModel.testButton as an accessor or listener on the attribute.
MainActivity:
public class MainActivity extends AppCompatActivity {
ViewModel mModel;
ActivityMainBinding binding;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
mModel = new ViewModel(this);
binding.setViewModel(mModel);
}
}
ViewModel:
public class ViewModel extends BaseObservable {
private static final String TAG = "VIEW_MODEL";
private Context mActivity;
public ViewModel(Context context) {
this.mActivity=context;
}
public void testButton(){
Log.i(TAG, "Button Click");
}
}
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"
tools:context=".MainActivity">
<data>
<variable
name="ViewModel"
type="com.example.ckleineidam.testproject.ViewModel" />
</data>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="title"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/activation_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Test Button"
android:background="?android:attr/selectableItemBackground"
app:command="#{ViewModel.testButton}"
app:layout_constraintTop_toBottomOf="#id/title" />
</android.support.constraint.ConstraintLayout >
</layout>
The code is also as an example project at Github.
You are getting this error because there is no attribute app:command on a button.
If you are trying to achieve an onClick functionality, you can use android:onClick="#{ViewModel.testButton}" and change your function signature to void testButton(View view).
To use custom attributes, you need to define a binding adapter
I use data binding.
Here parent adapter:
public abstract class PreviewSortAdapter extends RealmRecyclerViewAdapter {
protected Context context;
#BindingAdapter("imageUrl")
public static void loadImage(ImageView view, String imageUrl) {
Glide.with(view.getContext()).load(imageUrl)
.apply(RequestOptions.bitmapTransform(
new GlideRoundedCornersTransformation(view.getContext(), (int) AndroidUtil.dpToPx(view.getContext(),
view.getContext().getResources().getInteger(R.integer.image_rounded_corner_radius_dp)),
0, GlideRoundedCornersTransformation.CornerType.TOP)))
.into(view);
}
}
Here my child adapter:
public class MapListSortAdapter extends PreviewSortAdapter {
public MapListSortAdapter(Context context, OrderedRealmCollection<Merchant> data) {
super(context, data, true);
}
#BindingAdapter("imageUrl")
public static void loadImage(ImageView view, String imageUrl) {
Debug.d(TAG, "loadImage: ");
Glide.with(view.getContext()).load(imageUrl)
.into(view);
}
#Override
protected int getLayoutForPosition(int position) {
return R.layout.map_list_item;
}
}
As you can see in my child adapter I override method loadImage(). I want to call method loadImage() from child adapter.
Herer map_list_item.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="item"
type="com.myproject.android.customer.api.model.Merchant" />
</data>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white"
android:minHeight="90dp">
<ImageView
android:id="#+id/imageViewPhoto"
android:layout_width="90dp"
android:layout_height="90dp"
android:scaleType="centerCrop"
app:imageUrl="#{item.preview.formats.reference.url}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
</layout>
As you can see I use custom tag app:imageUrl to call method loadImage().
The problem is that method is call but it call of parent adapter - PreviewSortAdapter.loadImage().
But I need to call this method in child adapter: MapListSortAdapter.loadImage().
Methods anotated with the static modifier doesn't have hieritance. Just remove the static modifier and it should work
I am trying to use MVVM pattern with android databinding.
I have an edittext and a textview that should show the same text when I type in the edittext field by binding to the model via the observablefield with a LocationType model object.
From testing both fields are set to "hello" when I start the app, as expected.
But when I type in the edittextfield the textview does not update, even tough the model object gets updated correctly as can be seen with by debug.
When I use an:
observablefield<String>
drop using the model and just set to some text and update the xml to use this field. It works as intended.
model:
public class LocationType {
private String locationType;
public String getLocationType() {
return locationType;
}
public void setLocationType(String locationType) {
this.locationType = locationType;
}
}
modelView:
public class LocationTypeViewModel extends BaseObservable {
#Bindable
private ObservableField<LocationType> locationTypeObservableField = new ObservableField<>();
private Context context;
LocationType locationType;
public LocationTypeViewModel(Context context) {
this.context = context;
locationType = new LocationType();
locationType.setLocationType("Hello");
locationTypeObservableField.set(locationType);
}
public ObservableField<LocationType> getLocationTypeObservableField() {
Log.d("CALLED GET", locationType.getLocationType());
return locationTypeObservableField;
}
}
XML:
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View"/>
<variable
name="viewModel"
type="fragment.LocationType.viewmodel.LocationTypeViewModel"/>
</data>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="#{viewModel.locationTypeObservableField.locationType}"
android:id="#+id/edittext1"/>
<TextView
android:layout_below="#+id/edittext1"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="#{viewModel.locationTypeObservableField.locationType}"
android:id="#+id/text"
/>
</RelativeLayout>
</layout>
android:text="#={viewModel.locationTypeObservableField.locationType}"
You forgot to add "=" .check and compare above line with your code.
Two way databinding should be like this
android:text="#={ProfileViewModel.mobileNumber}"
You forgot to add "="
The problem is you are observing changes to the field inside LocationTypeViewModel which points to a LocationType object. That will only notify changes when the object reference changes. What you want is an observer on the actual String field.
You are also missing an important two-binding symbol on your EditText. You want to use #={obj.fieldName} instead of #{obj.fieldName}. The #= signals that you want changes to propagate back to the source.
Here is your code edited to work as intended:
public class LocationType {
private String location;
public LocationType(String location) {
this.location = location;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
public class LocationTypeViewModel extends BaseObservable {
private final LocationType locationType;
public LocationTypeViewModel() {
locationType = new LocationType("Hello");
}
#Bindable
public String getLocation() {
return locationType.getLocation();
}
public void setLocation(String location) {
locationType.setLocation(location);
notifyPropertyChanged(BR.location);
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View"/>
<variable
name="viewModel"
type=".LocationTypeViewModel"/>
</data>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="#={viewModel.location}"
android:id="#+id/edittext1"/>
<TextView
android:layout_below="#+id/edittext1"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="#{viewModel.location}"
android:id="#+id/text"
/>
</RelativeLayout>
</layout>
I'm trying to handle clickable TextView on data binding method, but i get this error:
Cannot find the setter for attribute 'android:clickable' with parameter type lambda on android.widget.TextView
my TextView widgets must be clickable and i show simple Toast, how can i set text to that such as android:text="#string/my_text and can be clickable?
ActivityRegister:
public class ActivityRegister extends BaseActivities
implements ActivityRegisterContract.View{
private ActivityRegisterBinding binding;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_register);
ActivityRegisterPresenter mainActivityPresenter = new ActivityRegisterPresenter(this);
ActivityRegisterData viewModel = new ActivityRegisterData();
viewModel.setReadContactPermission(Utils.getString(R.string.get_read_contact_permission, context));
binding.setPresenter(mainActivityPresenter);
}
#Override
public void getReadContactsPermission() {
Utils.toast("CLICKED", context);
}
}
presenter:
public class ActivityRegisterPresenter {
private ActivityRegisterContract.View view;
public ActivityRegisterPresenter(ActivityRegisterContract.View mView) {
view = mView;
}
public void getReadContactsPermission(){
view.getReadContactsPermission();
}
}
ActivityRegisterContract
public interface ActivityRegisterContract {
public interface View {
void getReadContactsPermission();
}
}
and then ActivityRegisterData
public class ActivityRegisterData extends BaseObservable {
private String readContactPermission;
public ActivityRegisterData() {
}
#Bindable
public String getReadContactPermission() {
return readContactPermission;
}
public void setReadContactPermission(String readContactPermission) {
this.readContactPermission = readContactPermission;
notifyPropertyChanged(BR.readContactPermission);
}
}
my layout:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:slidingLayer="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.example.Ui.Register.Model.ActivityRegisterData"/>
<variable
name="presenter"
type="com.example.Ui.Register.Presenter.ActivityRegisterPresenter"/>
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#d1d1d1">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="#+id/permission_for_read_contacts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="#string/permission_for_read_contacts"
android:textColor="#color/white"/>
<TextView
android:layout_width="match_parent"
android:layout_height="#dimen/default_textview_height"
android:background="#drawable/selector_blue_buttons"
android:clickable="#{()->presenter.getReadContactsPermission()}"
android:text="#{viewModel.readContactPermission}"
android:textColor="#color/white"/>
</LinearLayout>
</LinearLayout>
</FrameLayout>
</layout>
problem is for this line on layout:
<TextView
android:layout_width="match_parent"
android:layout_height="#dimen/default_textview_height"
android:background="#drawable/selector_blue_buttons"
android:clickable="#{()->presenter.getReadContactsPermission()}"
android:text="#{viewModel.readContactPermission}"
android:textColor="#color/white"/>
I dont know about your code.but to make a clickable text, you just have to add listener to it or you can define onClick attribute in Xml and define that method in Activity to handle click event.
Use this:
android:onclick="doSomething"
And in activity
public void doSomething(View v){
//Write your code here
}