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.
Related
I was using binding object. My button id in the main activity xml file was 'button' but in the java class its showing red color on button in binding.button
package com.example.admybrand_git_api;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.material.progressindicator.BaseProgressIndicator;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
#Override
protected void onCreate(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
binding = ActivityMainBinding.inflate(inflater, container, false);
binding.button.setOnClickListener(view->{
Intent intent = new Intent(this,Git_Id_searchpage.class)
});
return binding.getroot();
}}
You didn't add necessary information so let's guess.
Check if:
you added this code to build.gradle
buildFeatures {
viewBinding true
dataBinding true
}
added necessary import in your Activity
it has the same name as your XML layout (not Activity class)
You don't need
setContentView(R.layout.activity_main);
if you use ViewBinding
if you are using data binding then be ensure you have implemented it correctly.
your layout should start with <layout> and also end with </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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
tools:context=".activities.MainActivity">
<!--your activity code design -->
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
and you don't need to setContentView and infalteView both for single layout bind your layout with DataBindinngUtils.
MainActivity.class
public class MainActivity extends BaseActivity {
ActivityMainBinding activityMainBinding;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
activityMainBinding.button.setOnClickListener(view-> {
Intent intent = new Intent(this,Git_Id_searchpage.class)
});
}
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 ";
}
}
Im rendering a android layout.xml file on react native.while trying to implement events on views inside layout.xml on android.I'm not able to acheive it
my customview.xml layout.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height='40dp'
android:orientation="horizontal"
>
<TextView android:id="#+id/multipleCameraText"
android:layout_marginTop="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, Text View from Native"
/>
<Button android:id="#+id/button1"
android:layout_width="wrap_content"
android:height="40dp"
android:layout_height="wrap_content"
android:text="Button 1"
/>
<Button android:id="#+id/button2"
android:layout_marginLeft="20dp"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:height="40dp"
android:text="Button 2"
/>
</LinearLayout>
my customview.java
package com.typesproject;
import android.content.Context;
import android.widget.LinearLayout;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.events.RCTEventEmitter;
public class CustomView2 extends LinearLayout {
private Context context;
public CustomView2(Context context) {
super(context);
this.context=context;
this.init();
}
public void init() {
//modified here.
inflate(context, R.layout.customview2, this);
}
public void onReceiveNativeEvent() {
WritableMap event = Arguments.createMap();
event.putString("message", "MyMessage");
ReactContext reactContext = (ReactContext)getContext();
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
getId(),
"topChange",
event);
}
}
I want to implement button clicks on two buttons(button1 and button2) and change text inside text view(multipleCameraText).
my customViewManager.java
package com.typesproject;
import android.view.View;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.image.ReactImageView;
import javax.annotation.Nonnull;
public class CustomViewManager extends SimpleViewManager<CustomView> {
public static final String Custom_View="CustomView";
#Nonnull
#Override
public String getName() {
return Custom_View;
}
#Nonnull
#Override
protected CustomView createViewInstance(#Nonnull ThemedReactContext reactContext) {
return new CustomView(reactContext);
}
}
Please let me know how we can achieve separate events for buttons and textview.
the layout view can not use in this situation. you have to define a class for the view, then use it in the ViewManager.
As for your situation, you firstly could define a class which extends Button,
public class CustomButton extends Button {}
then in the CustomViewManager expose the props and event
#ReactProp(name = "text")
public void setContenxt(CustomButton button, #Nullable String text) {
view.setSource(sources);
}
#ReactProp(name = "text")
public void setContenxt(CustomButton button, #Nullable String text) {
button.setText(sources);
}
as for the event, you can read official API.
If you want to define a view that shows like the layout XML. the view have to extend Linearlayou or ViewGroup. that effect realizes is complex. the related
knowledge is the android custom view. you may have to override onDraw, onMeasure, onLayout.
you can look at the ReactViewGroup class, it will give you some clues
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}"
Note: I realize that there are no onClick in the xml because every tim i try to link it to AlexRevolver.shoot() or .reload() the program crashes.
Any ideas on how to fix this, also the TextView's are not getting updated
Display.Java
package com.example.firedatgun_v2;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.Button;
import android.widget.TextView;
public class Display extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display);
}
#SuppressWarnings("unused")
public void main(){
gun AlexRevolver = new gun(0);//create a new gun Object
Button reload_V = (Button) findViewById(R.id.reload_B);
Button fire_V = (Button) findViewById(R.id.fire_B);
TextView bulletCount_V = (TextView)findViewById(R.id.bulletCount_TV);
TextView infoDisplay_V = (TextView)findViewById(R.id.info_TV);
infoDisplay_V.setText("Magazine Size: " + AlexRevolver.magSize);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.display, menu);
return true;
}
}
gun.Java
package com.example.firedatgun_v2;
import android.app.Activity;
import android.widget.TextView;
public class gun extends Activity{
public TextView bulletCount_Var = (TextView)findViewById(R.id.bulletCount_TV);
public TextView infoDisplay_Var = (TextView)findViewById(R.id.info_TV);
public int magSize = 6; //create the variable called "magSize" //set the size of the Magazine to 6
public int bulletCount; //create a variable called "bulletCount"
public gun(int startingBullets) {
bulletCount = startingBullets;
}
public void reload(){
bulletCount = magSize;
}
public void shoot(){
if (bulletCount < 0){
infoDisplay_Var.setText("Bang!");
bulletCount --;
bulletCount_Var.setText(bulletCount);
}
else{
infoDisplay_Var.setText("Reload now");
}
}
}
activity_display.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".Display" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="15dp"
android:text="#string/bullets_in_mag"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="35sp" />
<TextView
android:id="#+id/bulletCount_TV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/textView1"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:text="#string/empty"
android:textSize="50sp" />
<TextView
android:id="#+id/info_TV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="86dp"
android:text="#string/clear" />
<Button
android:id="#+id/fire_B"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/info_TV"
android:layout_marginTop="31dp"
android:layout_toLeftOf="#+id/info_TV"
android:text="#string/Fire" />
<Button
android:id="#+id/reload_B"
android:layout_width="150sp"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/fire_B"
android:layout_alignBottom="#+id/fire_B"
android:layout_toRightOf="#+id/info_TV"
android:text="#string/reload" />
</RelativeLayout>
if you need more data, just post , any help would be greatly appreciated.
Only when a activity A calls setContentView with a layout a, then that activity A can directly access the elements in the layout a using the findViewById() - means layout elements can be used directly by that activity A - or in other words*layout a* elements will not be directly accessible to activity B through findViewById().
Also Which means that for the events directly set in layout a, when fired will always look for a matching function in the activity A in which that layout was inflated through setContentView.
In your case Activity gun is not the activity that inflated the *layout activity_display* instead Activity Display inflated that layout. so for any events directly specified in *layout activity_display*,when triggered will look for matching function in the Activity Display - if a match is absent error will be raised
Modified Code
Display.java
....
...
public class Display extends Activity {
gun AlexRevolver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display);
AlexRevolver = new gun(0)
AlexRevolver.reload_V = (Button) findViewById(R.id.reload_B);
AlexRevolver.fire_V = (Button) findViewById(R.id.fire_B);
AlexRevolver.bulletCount_V = (TextView)findViewById(R.id.bulletCount_TV);
AlexRevolver.infoDisplay_V = (TextView)findViewById(R.id.info_TV);
AlexRevolver.infoDisplay_V.setText("Magazine Size: " + AlexRevolver.magSize);
AlexRevolver.reload_V.setOnClickListener(AlexRevolver);
AlexRevolver.fire_V.setOnClickListener(AlexRevolver);
}
gun.java
gun can be an simple class instead of extending Activity
.....
.....
public class gun implements OnClickListener {
public TextView bulletCount_V;
public TextView infoDisplay_V;
public Button fire_V;
public Button reload_V;
public gun(int startingBullets) {
bulletCount = startingBullets;
}
.....
.....
...
public void onClick(View v)
{
switch(v.getId())
{
case R.id.reload_B:
this.reload();
break;
case R.id.fire_B:
this.shoot();
break;
}
}
}
Note: The above code does not follow proper oop, Java Interfaces can be used to achieve the same functionality
You have two activities in your posted code as both extends Activity: gun and Display. And you can't instantiate the activity like any other java object. There are certain protocols you have to follow for communication between activities. But from you posted code you don't need multiple activities.
Moreover, each activity on creation, instantiate the layout and that instance of the layout (and subelements as well) belongs to instance of that activity.
Android has good documentation on layout
You have
public class gun extends Activity{
public TextView bulletCount_Var = (TextView)findViewById(R.id.bulletCount_TV);
You will get NullPointerException. findViewById looks fora view with the id mentioned in the current inflated layout.
Also in Display Activity you do not call main().
Change your activity code to
public class Display extends Activity implements OnClickListener {
TextView bulletCount_Var,infoDisplay_var;
Buttom reload_V,fire_V;
public int bulletCount=0; // initially count is 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display);
reload_V = (Button) findViewById(R.id.reload_B);
fire_V = (Button) findViewById(R.id.fire_B);
bulletCount_Var = (TextView)findViewById(R.id.bulletCount_TV);
infoDisplay_Var = (TextView)findViewById(R.id.info_TV);
infoDisplay_V.setText("Magazine Size: " + AlexRevolver.magSize);
realod_V.setOnClickListener(this);
fire_V.setOnClickListener(this);
}
public void onClick(View v)
{
switch(v.getId())
{
case R.id.reload_B :
// do reload calculation
// update bulletCount_Var
break;
case R.id.fire_B :
// do fire calculation
// update infoDisplay_Var
break;
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.display, menu);
return true;
}
}
Write button listeners. Do calculation and update text view in activtiy. I do not knwo why you use gun class which extends Activity.