java - rxkotlin - Usando Observable en Android
rxjava2 android español (3)
Aquí hay un ejemplo simple con una Activity
y un solo Fragment
pero será igual con otros fragmentos.
Primero debe crear una clase que represente el valor que desea observar, en su caso, es un int
simple, así que cree una clase que contenga este int
y que se extienda a Observable
(implementa Serializable
para simplificar el intercambio entre actividad y fragmento):
...
import java.util.Observable;
public class ObservableInteger extends Observable implements Serializable {
private int value;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
this.setChanged();
this.notifyObservers(value);
}
}
Luego use este int observable en una actividad (el diseño de la actividad contiene un Button
y un FrameLayout
usado para mostrar un Fragment
):
public class MainActivity extends Activity {
private ObservableInteger a;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create observable int
a = new ObservableInteger();
// Set a default value to it
a.setValue(0);
// Create frag1
Frag1 frag1 = new Frag1();
Bundle args = new Bundle();
// Put observable int (That why ObservableInteger implements Serializable)
args.putSerializable(Frag1.PARAM, a);
frag1.setArguments(args);
// Add frag1 on screen
getFragmentManager().beginTransaction().add(R.id.container, frag1).commit();
// Add a button to change value of a dynamically
button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Set a new value in a
a.setValue(a.getValue() + 1);
}
});
}
}
Finalmente, crea un Fragment
que escuche un cambio de valor:
...
import java.util.Observer;
public class Frag1 extends Fragment {
public static final String PARAM = "param";
private ObservableInteger a1;
private Observer a1Changed = new Observer() {
@Override
public void update(Observable o, Object newValue) {
// a1 changed! (aka a changed)
// newValue is the observable int value (it''s the same as a1.getValue())
Log.d(Frag1.class.getSimpleName(), "a1 has changed, new value:"+ (int) newValue);
}
};
public Frag1() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
// Get ObservableInteger created in activity
a1 = (ObservableInteger) getArguments().getSerializable(PARAM);
// Add listener for value change
a1.addObserver(a1Changed);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_blank, container, false);
}
}
Intento nombrar mis variables igual que las tuyas, espero que te ayuden.
Quiero implementar una Navigation View
con muchos fragmentos que dependen totalmente de un valor definido en MainActivity
. Sé que se puede acceder a las variables en MainActivity utilizando el método definido en MainActivity desde otros Fragmentos para obtener el valor , pero el problema aquí es que el valor de la variable en MainActivity puede cambiar (que se ejecuta en un AsyncThread ). Ahora, o cambio el código para que mis Fragmentos actualicen su valor en función de algún evento en el propio fragmento o utilicen SharedPreference
. Pero no quiero usar SharedPreferences, ni tengo que verificar el cambio en el valor innecesariamente muchas veces.
Sé que en RxJS, utilizamos Observable que funciona de forma asíncrona y funciona de una manera similar a una cinta transportadora. Un poco de googlear en los documentos oficiales: Observable confirmó mi sospecha de que algo similar estaba disponible en Android, pero no pudo encontrar ningún tutorial o explicación adecuada sobre cómo implementarlo. Por lo tanto, estoy buscando un fragmento de código simple que pueda aclarar cómo funciona Observable en Android y si es Async y si está basado en la implementación de RxJS. (No, no quiero la implementación de RxJS)
Caso de prueba:
MainActivity : int a, b (need observable for both variables)
Frag1 : int a1 , b1, a1changed(),b1changed()
Frag2 : int a2 , b2, a2Changed(), b2changed()
MainActivity contiene enteros cuyo valor cuando se modifica debe reflejarse en los enteros correspondientes en los Fragmentos y llama a una función separada para cada Fragmento en el cambio que se está notando.
Reactivo no es parte de Android pero probablemente esté buscando esta biblioteca: https://github.com/JakeWharton/RxBinding
A la página de destino le falta un ejemplo introductorio, así que tienes que mirar el javadoc. Esta publicación debería darle un buen comienzo: ¿Cómo crear un Observable desde OnClick Event Android? Aquí está el ejemplo de código de Matt para comenzar
RxView.clicks(myButton)
.subscribe(new Action1<Void>() {
@Override
public void call(Void aVoid) {
/* do something */
}
});
Hay un buen ejemplo sobre el uso de Observable de Android (java.util.Observable) aquí: https://andhradroid.wordpress.com/2012/04/05/object-observer-pattern-in-android/
Y otro ejemplo sobre el uso del patrón Observer en Java: http://www.journaldev.com/1739/observer-design-pattern-in-java .
En general, hay dos formas:
- Utilice java.util.Observable .
- Diseña tu propio Observable (más flexible, ayúdanos a comprender más profundamente).
Me gusta la segunda forma más, por ejemplo: ( Lo siento, quiero asegurarme de que funcione, así que hago un ejemplo completo )
El observable :
public interface MyObservable {
void addObserver(MyObserver myObserver);
void removeObserver(MyObserver myObserver);
void notifyObserversAboutA();
void notifyObserversAboutB();
}
El observador:
public interface MyObserver {
void onAChange(int newValue);
void onBChange(int newValue);
}
La actividad principal:
public class MainActivity extends AppCompatActivity implements MyObservable {
private List<MyObserver> myObservers;
private int a, b;
private EditText etA;
private EditText etB;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myObservers = new ArrayList<>();
ViewPager vpContent = (ViewPager) findViewById(R.id.activity_main_vp_content);
etA = (EditText) findViewById(R.id.et_a);
etB = (EditText) findViewById(R.id.et_b);
Button btnOk = (Button) findViewById(R.id.btn_ok);
//add fragments to viewpager
List<Fragment> fragments = new ArrayList<>();
Fragment1 fragment1 = new Fragment1();
addObserver(fragment1);
Fragment2 fragment2 = new Fragment2();
addObserver(fragment2);
fragments.add(fragment1);
fragments.add(fragment2);
MyFragmentPagerAdapter fragmentPagerAdapter
= new MyFragmentPagerAdapter(getSupportFragmentManager(), fragments);
vpContent.setAdapter(fragmentPagerAdapter);
btnOk.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String a = etA.getText().toString().trim();
String b = etB.getText().toString().trim();
if (!a.equals("") && !b.equals("")) {
setA(Integer.parseInt(a));
setB(Integer.parseInt(b));
}
}
});
}
private void setA(int value) {
a = value;
notifyObserversAboutA();
}
private void setB(int value) {
b = value;
notifyObserversAboutB();
}
@Override
public void addObserver(MyObserver myObserver) {
myObservers.add(myObserver);
}
@Override
public void removeObserver(MyObserver myObserver) {
myObservers.remove(myObserver);
}
@Override
public void notifyObserversAboutA() {
for (MyObserver observer : myObservers) {
observer.onAChange(this.a);
}
}
@Override
public void notifyObserversAboutB() {
for (MyObserver observer : myObservers) {
observer.onBChange(this.b);
}
}
}
El Fragmento 1:
public class Fragment1 extends Fragment implements MyObserver {
private TextView tvA;
private TextView tvB;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View contentView = inflater.inflate(R.layout.fragment_basic, container, false);
tvA = (TextView) contentView.findViewById(R.id.tv_a);
tvB = (TextView) contentView.findViewById(R.id.tv_b);
return contentView;
}
@Override
public void onAChange(int newValue) {
tvA.setText(String.valueOf("New value of a: " + newValue));
}
@Override
public void onBChange(int newValue) {
tvB.setText(String.valueOf("New value of b: " + newValue));
}
}
El Fragmento 2:
public class Fragment2 extends Fragment implements MyObserver {
private TextView tvA;
private TextView tvB;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View contentView = inflater.inflate(R.layout.fragment_basic, container, false);
tvA = (TextView) contentView.findViewById(R.id.tv_a);
tvB = (TextView) contentView.findViewById(R.id.tv_b);
return contentView;
}
@Override
public void onAChange(int newValue) {
tvA.setText(String.valueOf("New value of a: " + newValue));
}
@Override
public void onBChange(int newValue) {
tvB.setText(String.valueOf("New value of b: " + newValue));
}
}
El adaptador:
public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public MyFragmentPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
}
El diseño principal activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
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="codeonce.thinktwice.testobserverpattern.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/linearLayout">
<EditText
android:id="@+id/et_a"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:inputType="number"
android:hint="Type value for a"/>
<EditText
android:id="@+id/et_b"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:inputType="number"
android:hint="Type value for b"/>
<Button
android:id="@+id/btn_ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:layout_gravity="center_horizontal"
android:text="OK"/>
</LinearLayout>
<android.support.v4.view.ViewPager
android:id="@+id/activity_main_vp_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/linearLayout"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
</android.support.v4.view.ViewPager>
</RelativeLayout>
El diseño del fragmento fragment_basic.xml:
<?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="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<TextView
android:layout_marginTop="20dp"
android:id="@+id/tv_a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Value of a will appear here"
android:textSize="20sp"/>
<TextView
android:id="@+id/tv_b"
android:layout_marginTop="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Value of b will appear here"
android:textSize="20sp"/>
</LinearLayout>