SharedPreferences und Fragment Update

  • 27 Antworten
  • Letztes Antwortdatum
B

Braesident

Ambitioniertes Mitglied
1
Hallo Android-Hilfe Team,

ich stehe vor dem Problem meine Daten nicht in den Preferences speichern zu können... und ich komm nicht drauf wo das Problem liegt.

Ich habe eine Activity die einen ViewPager und einen FragmentStatePagerAdapter instantiiert.
Das erste Fragment ist eine Übersicht über die folgenden Fragmente. Diese haben dann EditText's in denen Eingaben aus den Preferences schon vorgegeben sind.
Im Fragment 1 (Übersicht) gibt es ein Button um:
  • Daten neu zu laden
  • diese in den Preferences zu hinterlegen
  • und die Fragmente zu aktuallisieren
Ein sharedPreferences.getString("element", "") liefert auch den gerade mit edit().putString("element","neu").apply() (auch .commit()) aktualisierten Wert.
Aber auch nach einem Neustart der App steht wieder der alte Wert im EditText des jeweiligen Fragments.

Java:
public class PagesActivity extends AppCompatActivity {

    private PagesFragmentStatePagerAdapter pagerAdapter;
    private ViewPager viewPager;

    private PagesViewModel pagesViewModel;
    private SharedPreferences sharedPreferences;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pages);

        pagesViewModel = PagesViewModel.getSingeltonInstance(getBaseContext());
        sharedPreferences = pagesViewModel.getSharedPreferences();

        pagerAdapter = new PagesFragmentStatePagerAdapter(getSupportFragmentManager());
        viewPager = findViewById(R.id.activity_pages);

        setupViewPager(viewPager);
    }

    private void setupViewPager(ViewPager viewPager){
        pagerAdapter.addFragment(new CategoryFragment(), "Categories");
        pagerAdapter.addFragment(new CompanyFragment(), "Company");
        // ... weitere Fragmente

        viewPager.setAdapter(pagerAdapter);
    }

    public void setViewPager(int fragmentNumber){
        viewPager.setCurrentItem(fragmentNumber);
    }

    public void updatePages(){
        pagerAdapter.notifyDataSetChanged();
    }

    @Override
    public void onBackPressed() {
        //super.onBackPressed();

        if (viewPager.getCurrentItem() != 0) {
            // goto View before
            //viewPager.setCurrentItem(viewPager.getCurrentItem() - 1,false);
            // goto Overview
            viewPager.setCurrentItem(0,false);
        }else{
            pagesViewModel.save();
            setResult(RESULT_OK);
            finish();
        }
    }
}

public class PagesFragmentStatePagerAdapter extends FragmentStatePagerAdapter {

    final List<Fragment> fragmentList = new ArrayList<>();
    final List<String> fragmentTitleList = new ArrayList<>();

    public PagesFragmentStatePagerAdapter(FragmentManager fm) {
        super(fm);
    }

    public void addFragment(Fragment fragment, String title) {
        fragmentList.add(fragment);
        fragmentTitleList.add(title);
    }

    @Override
    public Fragment getItem(int i) {
        return fragmentList.get(i);
    }

    @Override
    public int getCount() {
        return fragmentList.size();
    }

    @Override
    public int getItemPosition(@NonNull Object object) {
        return POSITION_NONE;
    }
}

public class CategoryFragment extends Fragment implements View.OnClickListener {

    RelativeLayout button_company;
    RelativeLayout button_reset;

    private PagesViewModel pagesViewModel;
    private SharedPreferences sharedPreferences;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_category, container, false);

        button_company = view.findViewById(R.id.btn_company);
        button_reset = view.findViewById(R.id.btn_reset);

        button_company.setOnClickListener(this);
        button_reset.setOnClickListener(this);

        pagesViewModel = PagesViewModel.getSingeltonInstance(getContext());
        //sharedPreferences = pagesViewModel.getSharedPreferences();
        sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());

        return view;
    }

    @Override
    public void onClick(View view) {

        switch (view.getId()){
            case R.id.btn_company:
                ((PagesActivity)getActivity()).setViewPager(1);
                break;

            case R.id.btn_reset:

                //resetSettings();
                sharedPreferences.edit().putString("company_mail", "Neuer Wert").apply();
                ((PagesActivity)getActivity()).updatePages();
                break;
        }
    }
}

public class CompanyFragment extends Fragment {

    EditText editText_company;
    EditText editText_company_mail;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        PagesViewModel pagesViewModel = PagesViewModel.getSingeltonInstance(getContext());

        View inflateView = inflater.inflate(R.layout.fragment_company, container, false);

        editText_company = inflateView.findViewById(R.id.company_data);
        editText_company_mail = inflateView.findViewById(R.id.company_mail);

        pagesViewModel.ObserveUiOnChange(editText_company);
        pagesViewModel.ObserveUiOnChange(editText_company_mail);

        return inflateView;
    }

    public CompanyFragment(){}
}


...
public void ObserveUiOnChange(EditText editText){
    editText.setText(singeltonInstance.sharedPreferences.getString(editText.getResources().getResourceEntryName(editText.getId()), ""));
    // OnChangeListener wird gesetzt usw...
}
...
Lasse ich die Zeile ((PagesActivity)getActivity()).updatePages(); weg, steht "Neuer Wert" zumindest nach einem Neustart der App im EditText.

Wie kann ich in die Preferences schreiben UND die Fragmente aktualisieren ?
 
Zuletzt bearbeitet:
Hallo

Nachdem du die prefs abgefragt hast solltest du auch dein editText damit setzen.
Das solltest du gleich zu Beginn z.B. in der onCreade machen, und auch nachdem du etwas eingegeben , gespeichert , aus den Prefs abgefragt hast.
Der Text auf dem Bildschirm wird nicht von selber erneuert. Das musst du schon machen. Wenn sich der Text ändert.


editText.setText("dein Text oder auch deine Variable");
 
Zuletzt bearbeitet:
Noch ein paar Fragen zu deinem Code.
du sprichst von einen EditText nur wo ist der ? In welcher Klasse?

Du speicherst auch etwas in den Prefs in der Zeile 123.
sharedPreferences.edit().putString("company_mail", "Neuer Wert").apply();


Nur wo liest du das wieder aus und gibst es den EditText?

Ich denke das ist in der Klasse "PagesViewModel" die wir nicht sehen.
 
Zuletzt bearbeitet:
Hallo jogimuc,

ich habe oben in meinen Quellcodes noch das CompanyFragment ergänzt und ein paar Kopierfehler im Code korrigiert. Es gibt natürlich noch mehr Fragmente die aber alle wie CompanyFragment aufgebaut sind. Es gibt auch nicht nur EditText Felder sondern auch Switchs. Die möchte ich natürlich nicht alle per Hand aktualisieren. Die oben abgekürzte ObserveUiOnChange setzt auch zuverlässig die Werte aus den Prefs in die Eingabefelder wenn die Fragmente das erste mal geladen werden.

Angenommen Werte wurden geändert und der Nutzer möchte diese zurücksetzen auf Werte die von einem Server bereitgestellt werden. Dafür gibt es im ersten Fragment (der Übersicht) den RESET Button. In Zeile 123 wird auf diesen Button reagiert... ich habe das zu Testzwecken und als Abkürzung für hier vereinfacht mit nur einem Wert der in die Prefs geschrieben wird.

Schiebe ich ein Log.d("CATEGORY_FRAGMENT", sharedPreferences.getString("company_mail", "") zwischen Zeile 123 und 124 ,ist der neue Wert vorhaden... steht aber nicht im EditText obwohl dieses ja durch das Fragment Update aktualisiert sein müsste. Noch interessanter ist das nach einem App Neustart der ALTE Wert in den Prefs steht.

lasse ich nun die Zeile 124 ((PagesActivity)getActivity()).updatePages() weg... Sind die NEUEN Werte Nach einem App Neustart in den Eingabefeldern vorhanden.
 
editText.setText(singeltonInstance.sharedPreferences.getString(editText.getResources().getResourceEntryName(editText.getId()), ""));

Du willst deiner übergebenen edittext varaiaben mit settext setzen.
Das was du da bekommst ist bestimmt nicht der Inhalt der pref.
Kann es auch nicht denn editText.getResources().getResourceEntryName(editText.getId())
Ist mit Sicherheit nicht der key für die pref.

putString("company_mail", "Neuer Wert").apply();

Der key heißt company_mail wird das wirklich zurückgeben?
Schreibe nicht alles hintereinander sondern prüfe mit variablen was zurück gegeben wird.

Wenn der key nicht stimmt gibt dir getString deinen lehren default String "" zurück.

Getid gibt dir eigendlich den int wert nicht den Namen String deiner id wie du sie im XML vergeben hast. Das kann so nicht gehen.
Beiträge automatisch zusammengeführt:

Mal noch was anderes ist das die gleiche pref Datei auf die du zugreifen und in der du auch speicherst. Da bin ich mir nicht sicher.
Lesen tust du mit
singeltonInstance.sharedPreferences.....

Schreiben

sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());

Ist das die gleiche Datei?
 
Zuletzt bearbeitet:
Aber es funktioniert ja... WENN ich eben das Refreshen der Fragmente nicht anstoße (Zeile 124) ... nur das eben die neuen Werte im EditText erst nach einem Neustart zu sehen sind

Der int von GetId wird ja an getResourceEntryName(int) übergeben... damit bekomme ich den Id Name aus der XML und diesen nutze ich als Key in den Prefs

Wegen des abrufen der Prefs.... das hab ich jetzt nochmal überprüft und vorsichtshalber angepasst... Ich habe dafür eine Application Klasse erstellt die immer den gleichen Context zur verfügung stellt. Und laden tu ich nun immer über PreferenceManager.getDefaultSharedPreferences(MyApplication.getContext());
(Diese habe ich natürlich auch im Manifest namentlich bekannt gegeben)

Ergebnis bleibt gleich....

Nochmal zu Zeile 123: ändere ich diese zu
Code:
Log.d("CATEGORY_FRAGMENT", sharedPreferences.getString("company_mail", ""));
sharedPreferences.edit().putString("company_mail", "Neuer Wert").apply();
Log.d("CATEGORY_FRAGMENT", sharedPreferences.getString("company_mail", ""));
ist die Ausgabe IMMER:
Alter Wert
Neuer Wert

Und ohne Zeile 124 ((PagesActivity)getActivity()).updatePages(); wird der neue Wert auch nach App Neustart angezeigt.
Nur mit Zeile 124 ist nach dem Neustart der alte Wert wieder da

Um das gleich vorweg zu nehmen... in den Fragmenten wird ledigllich aus den Prefs geladen und in die EditTexts eingefügt...

und was mir gerade noch einfällt die onChangeListener gesetzt.
Das schau ich mir nochmal an ob da vielleicht Werte zurück gesetzt werden.
Beiträge automatisch zusammengeführt:

Ok der onChangeListener scheint die Daten selbst nicht zu ändern zeigt aber das die Inhalte NACH dem Refresh der Fragmente manipuliert werden.
Java:
editText.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
        Log.d("PREF_VIEW_MODEL", "BeforeTextChanged "+editText.getResources().getResourceName(editText.getId()) + " " + editText.getText().toString());
    }

    @Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}

    @Override
    public void afterTextChanged(Editable editable) {
        Log.d("PREF_VIEW_MODEL", "TextChanged "+editText.getResources().getResourceName(editText.getId()) + " " + editText.getText().toString());
        saveEditText(editText);
    }
});
Ausgabe:
Neuer Wert
Alter Wert

Scheinbar wird der neue Wert gespeichert... beim neu Laden der Fragmente dieser auch gesetzt... dann aber wieder geändert (woher auch immer der Alte Wert kommt?)
 
Zuletzt bearbeitet:
So nun nochmal wo wie mit was stösst du denn deinen refrech an?
 
😊 ja also

im CategoryFragment mit dem Button button_reset
dieser ruft in Zeile 124 ((PagesActivity)getActivity()).updatePages() auf
die Methode updatePages ruft pagerAdapter.notifyDataSetChanged() auf

Für diesen Refresh muss laut Internet im pagerAdapter noch folgendes stehen
Java:
@Override
    public int getItemPosition(@NonNull Object object) {
        return POSITION_NONE;
    }
 
Wenn du so das reflash anstößt. Solltest du natürlich auch vorher die Daten in der Pref Datei speichern. Damit es auch beim Neuaufbau des Fragmente in der onCreadeView zur Verfügung steht.

Nur die Methode aufrufen reicht nicht am Sinnvollsten macht man das nach der Eingabe. Das sagte ich schon am Anfang.
 
Ich glaube wir reden aneinander vorbei.

Es sind zwar manuelle Eingaben möglich und diese werden auch korrekt abgespeichert, doch in meinem geschilderten Fall geht es darum das manuelle Eingaben die bereits schon gespeichert wurden über den Reset Button zurück gesetzt werden. Dafür halte ich aber keine Daten in der App vor sondern lade diese von einem Server.

Da es nun aber Probleme bei diesem Vorgang gibt... hab ich das zum testen gekürzt. Also ohne Request und so.
Dafür hab ich in der Zeile 123 (unmittelbar vor dem Refresh Anstoß) neue Daten simuliert und schreibe in die Preferences.
 
Zeige mal deine Zeile 123 in deinem Code sind keine Zeilen Nummern. Keine Ahnung was du meinst.
Wenn du wieder anfangs werde haben willst. Wirst du wohl mehrere key wert Paare setzen müssen und nicht nur einen.
Beiträge automatisch zusammengeführt:
 
Java:
123     sharedPreferences.edit().putString("company_mail", "Neuer Wert").apply();
124     ((PagesActivity)getActivity()).updatePages();
 
Ok und das soll ein Rücksetzen auf anfangs Werte sein.
Das ist nur ein key. Ich denke du hast viele fragments. Dann müsstest du für alle key Paare eine Zeile schreiben. Solte klar sein.


So und wo machst du eine Eingabe und speicherst diese?

In deinem Textwatcher speicherst du nichts.
Dort musst du es natürlich in den prefs speichern.
Damit es nach dem Update auch in den fragments wieder vorhanden ist. Also aus den prefs ausgelesen werden kann.
 
Zuletzt bearbeitet:
Ja wie gesagt habe ich es gekürzt... eigentlich wird in 123 ein JSON String vom Server geholt und dessen Daten an die sharedPreferences übergeben.

Für manuelle Eingabe registriert jedes Fragment seine Controls wie oben gezeigt über mein ViewModel.
z.B.: pagesViewModel.ObserveUiOnChange(editText_company);

Hier wird im ersten Schritt über die xml Id des editText_company der Wert aus den Prefs geholt und mit editText.setText gesetzt.
Im 2. Schritt wird ein OnChangeListener gesetzt (siehe Post #6)
das saveEditText(editText); macht auch wieder nur sharedPreferences.edit().putString(KEY, VALUE).apply();

Würde dir eine ZIP von einem AndroidStudio Projekt was nützen?
 
Wenn du immer alles änderst und nur die Hälfte verrätst macht das hier keinen Sinn mehr.

Wie ich schon sagte sehe ich in deinem TextWatcher kein speichern in den prefs.
Beiträge automatisch zusammengeführt:

Der actuelle eingegebene string kommt in der onTextChanged an. Da soltest du auf ein enter prüfen und dann speichern.

Frage was ist saveEditText(editText);
Beiträge automatisch zusammengeführt:

Der Textwatcher wird bei jeden Tastendruck aufgerufen. Du musst schon selber prüfen wenm die Eingabe gültig ist zum Speichern.
Wenn du zb Hallo eingibst wird er schon beim ersten Zeichen also H aufgerufen und beim loslassen der Taste auch.
 
Zuletzt bearbeitet:
Sorry ich habe oben in Quellcodes ein Kommentar von OnClickListener in OnChangeListener geändert und im letzten Post die Frage zu einer ZIP ergänzt.

und in meinem letzten Post hatte ich geschrieben:
das saveEditText(editText); macht auch wieder nur sharedPreferences.edit().putString(KEY, VALUE).apply();
 
Also ich komme nicht mehr mit wo nun noch dein Problem ist.
Beiträge automatisch zusammengeführt:

Zip kannst du machen bin aber nechste woche nicht an einen rechner mit as.
 
Ja schade... ich mach sie trotzdem mal fertig... nur mit den Funktionen wie hier beschrieben.

Ich würd noch ein letzten Versuch starten:

1. App startet das erste mal und lädt Daten vom Server in die Prefs
2. Geht der Nutzer in die PagesActivity werden auch die Fragmente geladen... dabei werden die Prefs ausgelesen und die Werte richtig in EditTexts usw geladen
3. Im laufe der Nutzung der App ändert ein Nutzer Daten in einem EditText in einem der Fragmente. (funktioniert)
4. Plötzlich geht etwas nicht oder auf dem Server wurden bessere Werte bereit gestellt - also soll der Nutzer über den Reset Button aktuelle Werte vom Server laden... Das passiert im ersten Fragment der PagesActivity

Und da hab ich jetzt 2 Szenarien:
in beiden werden die Daten natürlich abgerufen (hier für Vereinfachung nur ein Wert in der oben gezeigten Zeile 123)
1. Ich stoße das refreshen der Fragmente nicht an (oben gezeigte Zeile 124)
Ergebnis: Die Daten (vom Server oder sonst wo) sind zwar in den Prefs gespeichert aber natürlich erst Sichtbar wenn die App neu gestartet wurde... oder zumindest die PagesActivity

2. Ich stoße das refreshen der Fragmente, nach dem Speichern in den Prefs, an
Ergebnis: Es ist keine Veränderung im Fragment zu sehen... auch nicht nach einem Neustart
Obwohl zwischenzeitlich die neuen Werte in den Prefs standen... Das folgere ich aus den Logs aus dem TextWatcher (Post #6)
Denn BeforeTextChanged liefert den neueren Wert (der jetzt eigentlich da sein sollte)
und AfterTextChanged liefert wieder den alten Wert (welcher eigentlich gar nicht mehr existent sein dürfte)
 
Hallo koje71,

vielen Dank... aber leider funktioniert es auch mit commit nicht

Obwohl der Ansatz zu gar nicht verkehrt sein könnte... das würde erklären warum BeforeTextChanged und AfterTextChanged (ich sag mal) verdrehte Werte liefert.

Der neue Wert ist gespeichert doch beim laden wird noch der alte Wert ausgeliefert und somit findet ein TextChange statt

Hmmm... Haare rauf
 
Zurück
Oben Unten