35 votes

À l'aide Observables dans Android

Je veux mettre en œuvre un Navigation View avec de nombreux fragments qui dépendent totalement d'une valeur définie dans l' MainActivity. Je sais que les variables dans les MainActivity peut être consulté à l'aide de la méthode définie dans MainActivity à partir d'autres Fragments pour obtenir la valeur, mais le hic ici est que la valeur de la variable dans MainActivity peut changer (qui s'exécute sur un AsyncThread). Maintenant, j'ai modifier le code tel que mon Fragments de mise à jour de leur valeur basée sur un événement dans le fragment lui-même ou utiliser SharedPreference. Mais je ne veux pas utiliser les SharedPreferences, ni vérifier, pour le changement de la valeur inutilement à de nombreuses reprises.

Je sais que dans RxJS, nous utilisons des Observables qui s'exécute de façon Asynchrone et fonctionne dans un mode similaire à un convoyeur. Un peu de googling à travers les docs officielles : Observables a confirmé mes soupçons de quelque chose de similaire est disponible dans Android, mais ne pouvais pas trouver un Tutoriel ou une explication sur la façon de la mettre en œuvre. Donc, je suis à la recherche pour un simple extrait de code qui pourrait donner de la clarté à la façon Observable fonctionne sous Android et si elle est Asynchrone et si sa fonction similaire à RxJS mise en œuvre. (Non, je ne veux pas RxJS mise en œuvre)

Cas De Test:

MainActivity : int a, b (need observable for both variables)
Frag1 : int a1 , b1, a1changed(),b1changed()
Frag2 : int a2 , b2, a2Changed(), b2changed()

MainActivity contenant des entiers dont la valeur lorsqu'elles sont modifiées devraient refléter dans correspondant entiers à travers les Fragments et les appels de fonction distincte pour chaque Fragment, le changement d'être remarqué.

37voto

Gaëtan M. Points 9548

Voici un exemple simple avec un Activity et un seul Fragment mais ce sera la même chose avec d'autres fragments.

Vous devez d'abord créer une classe pour la valeur que vous souhaitez observer, dans votre cas, c'est un simple int donc créer une classe contenant ce int et qui s'étend Observable (il implémente Serializable de simplifier l'échange entre l'activité et le fragment):

...
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);
    }
}

Ensuite, utilisez cette observable int dans une activité (activité de mise en page contient un Button et FrameLayout utilisé pour afficher 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);
            }
        });
    }
}

Enfin, créer un Fragment que d'écouter un changement de valeur:

...
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);
    }
}

J'essaie de nom de mes variables le même que le vôtre, j'espère que ça va vous aider.

14voto

Il y a un bon exemple sur l'utilisation des Observables d'Android (java.util.Observables) ici: https://andhradroid.wordpress.com/2012/04/05/object-observer-pattern-in-android/

Et un autre exemple sur l'utilisation de pattern observer en Java: http://www.journaldev.com/1739/observer-design-pattern-in-java.

Généralement, il existe deux façons:

  • L'utilisation de java.util.Observables.
  • La conception de votre propre Observables (plus de souplesse, nous aider à comprendre plus profondément).

J'aime la deuxième manière plus, par exemple: (Désolé, je tiens à vous assurer qu'il fonctionne, donc je fais un exemple complet)

Observables:

public interface MyObservable {
    void addObserver(MyObserver myObserver);
    void removeObserver(MyObserver myObserver);
    void notifyObserversAboutA();
    void notifyObserversAboutB();
}

L'Observateur:

public interface MyObserver {
    void onAChange(int newValue);
    void onBChange(int newValue);
}

Le MainActivity:

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);
        }
    }
}

Le Fragment1:

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));
    }
}

Le Fragment2:

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));
    }
}

L'Adaptateur:

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();
    }
}

La page principale 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>

Le fragment de mise en page 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>

5voto

Benjamin Mesing Points 131

Réactif n'est pas partie d'Android, mais vous êtes probablement à la recherche de cette bibliothèque: https://github.com/JakeWharton/RxBinding

La page de destination n'a pas d'exemple dans l'introduction, de sorte que vous avez à regarder la javadoc. Ce post devrait vous donner un bon départ: Comment créer un Observables de l'Événement OnClick Android? Voici l'exemple de code de Matt pour vous aider à démarrer

RxView.clicks(myButton)
    .subscribe(new Action1<Void>() {
        @Override
        public void call(Void aVoid) {
            /* do something */
        }
    });

2voto

Benjamin Mesing Points 131

Android offre maintenant des ViewModels avec LiveData pour votre but. Un modèle de vue est lié à un objet avec un cycle de vie (Fragment, de l'Activité) et la vie de cet objet. Dans votre cas, vous devez créer un modèle de vue lié à la actvity qui est accessible par tous les fragments.

À partir de l'Android à la documentation:

Il est très fréquent que deux ou plusieurs fragments, dans une activité, ont besoin de communiquer les uns avec les autres. Imaginez un cas commun de maître-détail des fragments, où vous avez un fragment dans lequel l'utilisateur sélectionne un élément dans une liste, et un autre fragment qui affiche le contenu de l'élément sélectionné. Ce cas n'est jamais anodin que les deux fragments de la nécessité de définir une description de l'interface, et le propriétaire de l'activité doit se lier les deux ensemble. En outre, les deux fragments doivent gérer le scénario où l'autre fragment n'est pas encore créé ou visible.

Cette douleur commune point peut être abordé en utilisant ViewModel objets. Ces fragments peuvent partager un ViewModel à l'aide de leur champ d'intervention pour gérer cette communication

Pour utiliser un modèle d'affichage pour le partage des données entre les fragments, vous devez:

  1. créer un modèle de vue qui hérite de ViewModel() et contient MutableLiveData-champs
  2. accéder à la vue du modèle à partir de fragments en appelant ViewModelProviders.de(cette).get(YourViewModel::class.java)
  3. modifier les valeurs du modèle de vue en appelant setValue pour la MutableLiveData-champs
  4. s'inscrire sur la variation de la MutableLiveData-champs en l'appelant .observer()

Ici est le centre de l'extrait de code à partir de l'Android à la documentation sur la façon d'utiliser Viewmodel pour un maître-détail-vue:

class SharedViewModel : ViewModel() {
    val selected = MutableLiveData<Item>()

    fun select(item: Item) {
        selected.value = item
    }
}

class MasterFragment : Fragment() {
    private lateinit var itemSelector: Selector
    private lateinit var model: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        model = activity?.run {
            ViewModelProviders.of(this).get(SharedViewModel::class.java)
        } ?: throw Exception("Invalid Activity")
        itemSelector.setOnClickListener { item ->
        // Update the UI
        }
    }
}

class DetailFragment : Fragment() {
    private lateinit var model: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        model = activity?.run {
            ViewModelProviders.of(this).get(SharedViewModel::class.java)
        } ?: throw Exception("Invalid Activity")
        model.selected.observe(this, Observer<Item> { item ->
            // Update the UI
        })
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X