webentwicklung-frage-antwort-db.com.de

Verwenden von Observable in Android

Ich möchte einen Navigation View Mit vielen Fragmenten implementieren, die vollständig von einem in MainActivity definierten Wert abhängen. Ich weiß, dass auf Variablen in MainActivity zugegriffen werden kann mit der in MainActivity definierten Methode aus anderen Fragmenten, um den Wert zu erhalten, aber der Haken hier ist, dass der Wert der Variablen in MainActivity change (läuft auf einem AsyncThread). Jetzt ändere ich entweder den Code so, dass meine Fragmente ihren Wert aktualisieren basierend auf einem Ereignis im Fragment selbst oder benutze SharedPreference. Ich möchte SharedPreferences jedoch nicht verwenden und muss auch nicht unnötig oft nach Änderungen im Wert suchen.

Ich weiß, dass wir in RxJS Observable verwenden, das asynchron läuft und ähnlich wie ein Förderband funktioniert. Ein bisschen googeln durch offizielle Dokumente: Observable bestätigte meinen Verdacht, dass etwas Ähnliches in Android verfügbar ist, fand aber kein richtiges Tutorial oder eine Erklärung zur Implementierung. Also, ich suche nach einem einfachen Code-Snippet, das Klarheit darüber gibt, wie Observable in Android und ob es asynchron ist und ob es auf einer ähnlichen RxJS-Implementierung basiert. (Nein, ich weiß nicht.) keine RxJS-Implementierung wollen)

Testfall:

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

MainActivity enthält Ganzzahlen, deren geänderter Wert sich in den entsprechenden Ganzzahlen der Fragmente widerspiegeln soll, und ruft für jedes Fragment eine separate Funktion auf, wenn die Änderung bemerkt wird.

27
Kaushik NP

Hier ist ein einfaches Beispiel mit einem Activity und einem einzelnen Fragment, aber es wird dasselbe mit anderen Fragmenten sein.

Zuerst müssen Sie eine Klasse erstellen, die für den Wert steht, den Sie beobachten möchten. In Ihrem Fall ist es ein einfaches int. Erstellen Sie also eine Klasse, die dieses int enthält und das Observable erweitert. ( Es implementiert Serializable, um den Austausch zwischen Aktivität und Fragment zu vereinfachen.

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

Verwenden Sie dann dieses beobachtbare int in einer Aktivität (das Aktivitätslayout enthält ein Button und ein FrameLayout, mit denen ein Fragment angezeigt wird):

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

Erstellen Sie schließlich ein Fragment, das eine Wertänderung überwacht:

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

Ich versuche, meine Variablen genauso zu benennen wie Ihre. Ich hoffe, es wird Ihnen helfen.

28
Gaëtan M.

Es gibt ein gutes Beispiel für die Verwendung von Observable von Android (Java.util.Observable) hier: https://andhradroid.wordpress.com/2012/04/05/object-observer-pattern-in-Android/

Und noch ein Beispiel zur Verwendung des Observer-Musters in Java: http://www.journaldev.com/1739/observer-design-pattern- in-Java .

Im Allgemeinen gibt es zwei Möglichkeiten:

  • Verwenden Sie Java.util.Observable .
  • Entwerfen Sie Ihre eigene Beobachtbare (flexibler, helfen Sie uns, tiefer zu verstehen).

Ich mag den zweiten Weg mehr, zum Beispiel: ( Entschuldigung, ich möchte sicherstellen, dass es funktioniert, also mache ich ein vollständiges Beispiel)

The Observable :

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

Der Beobachter:

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

Die Hauptaktivität:

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

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

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

Der Adapter:

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

Das Hauptlayout 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>

Das Fragment-Layout 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>
11

Reactive ist nicht Teil von Android aber Sie suchen wahrscheinlich nach dieser Bibliothek: https://github.com/JakeWharton/RxBinding

Auf der Landing Page fehlt ein Einführungsbeispiel. Sie müssen sich also den Javadoc ansehen. Dieser Beitrag sollte Ihnen einen guten Einstieg ermöglichen: Wie erstelle ich eine Observable aus OnClick Event Android? Hier ist das Codebeispiel von Matt, um Ihnen den Einstieg zu erleichtern

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

Android bietet jetzt ViewModels mit LiveData für Ihren Zweck an. Ein Ansichtsmodell ist an ein Objekt mit einem Lebenszyklus (Fragment, Aktivität) gebunden und lebt so lange wie dieses Objekt. In Ihrem Fall würden Sie ein Ansichtsmodell erstellen, das an die Aktivität gebunden ist, auf die alle Fragmente zugreifen können.

Aus der Android Dokumentation :

Es kommt sehr häufig vor, dass zwei oder mehr Fragmente in einer Aktivität miteinander kommunizieren müssen. Stellen Sie sich einen allgemeinen Fall von Master-Detail-Fragmenten vor, in dem Sie ein Fragment haben, in dem der Benutzer ein Element aus einer Liste und ein anderes Fragment auswählt, das den Inhalt des ausgewählten Elements anzeigt. Dieser Fall ist niemals trivial, da beide Fragmente eine Schnittstellenbeschreibung definieren müssen und die Eigentümeraktivität die beiden miteinander verbinden muss. Außerdem müssen beide Fragmente das Szenario handhaben, in dem das andere Fragment noch nicht erstellt oder sichtbar ist.

Dieser häufige Problempunkt kann mithilfe von ViewModel-Objekten behoben werden. Diese Fragmente können ein ViewModel mithilfe ihres Aktivitätsbereichs für die Verarbeitung dieser Kommunikation freigeben

Um ein Ansichtsmodell für die gemeinsame Nutzung von Daten zwischen Fragmenten zu verwenden, müssen Sie folgende Schritte ausführen:

  1. erstellen Sie ein Ansichtsmodell, das von ViewModel () erbt und MutableLiveData-Felder enthält
  2. greifen Sie über die Fragmente auf das Ansichtsmodell zu, indem Sie ViewModelProviders.of (activity) .get (YourViewModel :: class.Java) aufrufen.
  3. ändern Sie die Werte des Ansichtsmodells, indem Sie setValue für die MutableLiveData-Felder aufrufen
  4. registrieren Sie sich bei Änderungen der MutableLiveData-Felder mit .observe ()

Hier ist das zentrale Code-Snippet aus der Android Dokumentation zur Verwendung von ViewModels für eine Master-Detail-Ansicht:

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
        })
    }
}
1
Benjamin Mesing