Wie verwendet man setArguments() und getArguments() in Fragments?

P

prof.dopenudel

Neues Mitglied
2
Hallo,

bin gerade sehr verzweifelt. Sitze seit 3 Stunden an dem Schrott dran und nichts funktioniert.
Es geht um folgendes.

Habe eine App. Die App startet mit einer SplashScreen Klasse, um mit einem PlaceAutocompleteFragment einen Ort hinzuzufügen.

Mit

Code:
autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
            @Override
            public void onPlaceSelected(Place place) {
                // TODO: Get info about the selected place.
                //Log.i(TAG, "Place: " + place.getName());
                final String placeName = place.getName().toString();
                final String latitude = String.valueOf(place.getLatLng().latitude);
                final String longitude = String.valueOf(place.getLatLng().longitude);

Wird der Name und die lon und lat ermittelt und als String gespeichert. Das funktioniert auch top bis dahin.

Nun möchte ich von dieser Klasse die lon und lat an meine Fragmentklasse schicken.

Das wollte ich so machen:

Code:
Bundle bundle = new Bundle();
                        bundle.putString("latitude", latitude);
                        bundle.putString("longitude", longitude);
                        TabOverview tabOverview = new TabOverview();
                        tabOverview.setArguments(bundle);

                       //TabOverview ist meine Fragmentklasse

und dann in der Fragmentklasse mit:

Code:
Bundle bundle = getArguments();
            lon = getArguments().getString("longitude");
            longi.setText(lon);
            lat = getArguments().getString("latitude");
            lati.setText(lat);

aufrufen.
Mit dieser Methode bekomme ich eine "java.lang.NullPointerException:" Meldung

"java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.os.Bundle.getString(java.lang.String)' on a null object reference
at com.example.user.app.TabOverview.onCreateView(TabOverview.java:56)"


Ok. Also ab ins Internet um dort ein wenig umher zu stöbern.
Als erstes sagen die dort, dass man es mit getExtra(), putExtra() und Intents versuchen soll, aber das geht nicht. Denke weil es eine Fragmentklasse ist?

Dann habe ich diesen Code gefunden:

Code:
Bundle bundle = getArguments();
        if (bundle != null) {
            lon = getArguments().getString("longitude");
            longi.setText(lon);
            lat = getArguments().getString("latitude");
            lati.setText(lat);
        }

Damit kommt immerhin keine Fehlermeldung, aber es wird einfach kein Wert übergeben.

Nur noch so. "longi.setText(lon);" ist nur dazu da, dass man sieht, ob ein Wert übergeben wurde.

Gibt es vielleicht noch andere Möglichkeiten oder bin ich einfach zu dumm?
Bin echt am Ende mit der Lust, weil nichts funktioniert...

Hoffentlich kann mir einer von euch helfen! :)

Danke schon einmal im voraus!

Grüße
 
Wo setzt du denn dein Fragment in die aktuelle Activity? Kannst du dazu mal Code zeigen? Oder hast du das direkt im Layout gemacht, dass das Fragment fix in der Activity drin ist? Dann wird es so nicht gehen.
 
  • Danke
Reaktionen: prof.dopenudel und swa00
Danke für Deine schnelle Antwort!

Weiß jetzt leider nicht genau was Du meinst :confused2:

Also die App wurde mit einem NavigationDrawer erstellt und hat dann noch eine "public class TabPageAdapter extends FragmentStatePagerAdapter" Klasse, welche dann eben Tabs zum Wischen liefert. Deshalb die Fragmente
Das Ding ist, dass die App schon etwas älter ist. Deshalb weiß ich leider nicht mehr genau, was dort alles passiert. Es funktioniert soweit, bis auf das eine eben :D
Der Code ist auch viel zu lang um ihn hier einzufügen, deshalb versuche ich mal alles zusammen zu fassen.

Also:
"Wo setzt du denn dein Fragment in die aktuelle Activity?"
Du meinst damit die SplashScreenActivity?
Dort eigentlich gar nicht

Also der Part in dem Splash sieht so aus:

Code:
autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
            @Override
            public void onPlaceSelected(Place place) {
                // TODO: Get info about the selected place.
                //Log.i(TAG, "Place: " + place.getName());
                final String placeName = place.getName().toString();
                final String latitude = String.valueOf(place.getLatLng().latitude);
                final String longitude = String.valueOf(place.getLatLng().longitude);
                txtLatLngspla.setText(latitude + "/" + longitude);

                add.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Intent intent = new Intent(v.getContext(), MainActivity.class);
                        Toast.makeText(SplashScreenActivity.this, placeName + " wurde hinzugefügt",Toast.LENGTH_SHORT).show();
                        startActivityForResult(intent, 0);

                        Bundle bundle = new Bundle();
                        bundle.putString("latitude", latitude);
                        bundle.putString("longitude", longitude);
                        TabOverview tabOverview = new TabOverview();
                        tabOverview.setArguments(bundle);
                    }
                });
            }

            @Override
            public void onError(Status status) {
                // TODO: Handle the error.
                //Log.i(TAG, "An error occurred: " + status);
            }
        });

Das Fragment wird nur beim Fettgedruckten erwähnt/aufgerufen.

In der MainActvitiy wird sie gar nicht aufgerufen. Dort wird das über den

Code:
TabPageAdapter tabPagerAdapter = new TabPageAdapter(getSupportFragmentManager());
        viewPager.setAdapter(tabPagerAdapter);
        tabLayout.setupWithViewPager(viewPager);

angesprochen.

In der Fragmentklasse standardmäßig so

Code:
@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        overView = inflater.inflate(R.layout.fragment_tab_overview, container, false);

und im Layout dann mit "tools:context=".TabOverview""


Mir stellt sich gerade selbst die Frage, ob das überhaupt funktioniert :D Also irgendwie muss es ja funktionieren...

Sorry bin da noch ein Laie :oops:
 
Hallo
Eine Parameterübergabe mittels Bundle und Arguments geht nur beim Aufruf beim starten des Fragments . genau wie bei einem Intent. Wenn das Fragment gestartet ist werden da keine werte mehr übergeben. Da für kannst du dir eine Callback Methode in die Fragment Activity zb. über ein Interfase implementieren. Mit dem du einen Datenaustausch von der Activity zu Fragment Klasse machen kannst,.

Wenn es reicht die Daten beim Start zu übertragen dann in etwa so.

Code:
Bundle bundle = new Bundle();
Fragment fragTag = (FragTag) Fragment.instantiate(this, FragTag.class.getName(), null);
FragmentTransaction fragtrans = getSupportFragmentManager().beginTransaction();


bundle.putLong("arg_Tag", zeit.getTime());

Main_Activity.fragTag.setArguments(bundle);
Main_Activity.fragtrans.replace(R.id.fragContainer, fragTag);
Main_Activity.fragtrans.commit();
[doublepost=1533649970,1533649118][/doublepost]Hi ich noch mal
So wie ich das sehe willst du das die Daten über einen Listner verschickt werden von selbst an die Fragment Klasse. Wenn dem so ist . Geht es nicht über Bundle usw.

Dazu benutze ein Interface was du in der Main erstellst und im Fragment implementierst.

Oder du erstellst in der Main zwei static Variablen für lon u lat . Im Listner speicherst du die werte in den variablen. Im Fragment kannst du dann auf die Variablen mit dem Klassennamen zugreifen.
[doublepost=1533650700][/doublepost]
Wo setzt du denn dein Fragment in die aktuelle Activity? Kannst du dazu mal Code zeigen? Oder hast du das direkt im Layout gemacht, dass das Fragment fix in der Activity drin ist? Dann wird es so nicht gehen.
Hierzu meinte er ob du dein Fragment dynamisch oder statisch in deine Activty einbindest.
Bei Statischen würde die Parameterübergabe so nicht gehen.
Gut wir wissen jetzt das du es Dynamisch machst.
Aber wie ich schon im vorherigen post sagte wirst du eine andere Möglichkeit brauchen.
 
Zuletzt bearbeitet:
  • Danke
Reaktionen: prof.dopenudel und swa00
Danke für die Antwort.

Habe mich jetzt daran gemacht...ohne Erfolg. Ich check es einfach nicht...

"So wie ich das sehe willst du das die Daten über einen Listner verschickt werden von selbst an die Fragment Klasse. Wenn dem so ist . Geht es nicht über Bundle usw."

Meinst Du damit das in der onPlaceSelected Methode?
Code:
add.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Intent intent = new Intent(v.getContext(), MainActivity.class);
                        Toast.makeText(SplashScreenActivity.this, placeName + " wurde hinzugefügt",
                                Toast.LENGTH_SHORT).show();
                        startActivityForResult(intent, 0);

                    }
                });

Das war eigentlich nur dazu gedacht, dass die MainActivity geöffnet wird.

Habe es nun mit getter und setter versucht. Also diese in der MainActivity erstellt und dann beim onClick Aufruf die Werte übergeben. In der FragmentKlasse dann mit zb. main.getLon(); den Wert abfragen....das hat leider auch nicht funktioniert.
Wie gesagt. Bin ziemlich schlecht darin...

Verstehe zwar das mit den Interfaces und so, aber wieso muss man das in der Main erstellen und nicht z.b. in dem SplashScreen? Dort wird der Wert ja quasi eingetragen. Die Main ruft ja nur das Fragment auf. Oder habe ich das mit dem Interface doch falsch verstanden? :confused2:

Sorry für die Dummheit :D
 
Erkläre mal etwas genauer wie das abläuft.
Du willst nach dem Start der Mainactivity Daten an ein Fragment übergeben was nun innerhalb der Activity läuft richtig?
Wo fragst du denn die GPS Pos ab im Fragment oder in der Activity?
Wo willst du die Daten verarbeiten Im Fragment oder der Main.?
Müssen denn überhaupt die Daten in der Main geholt und in das Fragment übertragen werden oder Kannst du nicht alles im Fragment machen?
 
  • Danke
Reaktionen: prof.dopenudel und swa00
Die App startet.
Also es soll eine WetterApp werden. Habe dazu von OpenWeather die API und die Methode asyncTask.execute(lat, lon); funktioniert auch.
Diese Methode läuft in dem TabOverview Fragment ab (möchte gerne mehrere Fragmente erstellen. Erstes Fragment = Aktuelles Wetter, zweites = Vorhersage, ...) Bin noch recht am Anfang, deshalb möchte ich erst einmal das aktuelle Wetter darstellen. Wie gesagt. Das funktioniert auch, aber nur, wenn die Strings lon und lat bei der Deklaration mit Werten gefüllt werden. Also sieht das für Berlin eben so aus.
String lon = "13.4061";
String lat = "52.5192"

(Also die Deklaration geschieht in dem Fragment selbst. Die Daten sollen aber nun von der SplashScreenActivity in die asyncTask.execute Methode des Fragmentes kommen)

Also es zeigt perfekt das Wetter, die Stadt, Temperatur, alles an.
Nun eben der Gedanke, dass wenn jemand die App startet, erst einmal die Möglichkeit hat, seine eigene Stadt einzugeben.
Deshalb der SplashScreen mit der autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() Methode.

Dann werden über
final String latitude = String.valueOf(place.getLatLng().latitude);
final String longitude = String.valueOf(place.getLatLng().longitude);

die lon und lat geholt und dann hört es auf.


Die Daten werden also in der SplashScreenActivity abgefragt.
Das Fragment zeigt eben nur die Wetterdaten und da dort die asyncTask.execute Methode ist müssen die lon und lat Daten dort hin. Habe dort leider keine andere Möglichkeit gefunden.

Das sollte doch irgendwie möglich sein? Zumindest für einen Laien wie mich? :D
 
Dein Ansatz ist etwas falsch. Der SplashScreen ist eine eigene Activity so wie es aussieht .
Wenn du die main aufrufst wird die Splash Activity zerstört somit auch deine Variablen.
Soweit ist es richtig die per Intent und Bundle an die MainActivity zu übergeben, wie gesagt an die Main und nicht an das Fragment.
So nun musst du die Daten auch an das Fragment übergeben.
Denn ich denke das die komplette Anzeige in den Fragment abläuft und das auch die wetterdaten aus dem Netz holt.

Wie gesagt könntest du auch in der Splash Activity die Daten in den globalen static Variablen speichern . Und im Fragment kannste darauf zugreifen. Ist zwar nicht ganz nach OOP . sollte aber gehen und ich denke das ist die einfachste lösung für dich.

Natürlich musst du den Klassennahmen der Splach mit punkt vor der variablen im Fragment benutzen .
[doublepost=1533662360,1533662289][/doublepost]wo stehen diese zwei zeilen
final String latitude = String.valueOf(place.getLatLng().latitude);
final String longitude = String.valueOf(place.getLatLng().longitude);

Wie und wo werden den die Pos daten an die API übergeben?
Dann hole sie vorher aus den globalen static variablen und gib sie der API.

Hier ein Beispiel wie du auf eine public static Variable von einer anderen Klasse zugreifen kannst.

Code:
public class Kalasse1{

public static String lon;

public Klasse1(){
lon = " Hallo";
}

}
 
 public class Kalasse2{
String test;
public Klasse2(){
 test = Klasse1.lon;

}

}
[doublepost=1533664729][/doublepost]
Verstehe zwar das mit den Interfaces und so, aber wieso muss man das in der Main erstellen und nicht z.b. in dem SplashScreen? Dort wird der Wert ja quasi eingetragen. Die Main ruft ja nur das Fragment auf. Oder habe ich das mit dem Interface doch falsch verstanden? :confused2:

zu dem zeitpunkt als ich das schrieb wuste ich noch nichts von einer Splash Activity. Und zweitens ist die zum zeitpunkt des Fragment schon zerstört.
 
  • Danke
Reaktionen: prof.dopenudel
Fang bitte nicht an State in globale Variablen zu verschieben, das ist schlechter Stil.
Entweder machst du es so wie Jogimuc gesagt hat über Listener Pattern, wo genau was hin muss für dich ist aber aus den Codeauszügen nicht zu erkennen.
Alternativ schreibst du die Werte in SharedPreferences oder eine Datenbank und liest sie aus. Das würde sowieso Sinn machen, weil dann dein Benutzer nicht bei jedem Start die Koordinaten auswählen muss, sondern sie gespeichert bleiben.

Save key-value data  |  Android Developers
Esperandro
 
  • Danke
Reaktionen: prof.dopenudel
So jetzt. Erst einmal vielen lieben Dank für Eure Antworten! Das hilft mir wirklich sehr :)
Und sorry für die späte Antwort

Dein Ansatz ist etwas falsch. Der SplashScreen ist eine eigene Activity so wie es aussieht .
Wenn du die main aufrufst wird die Splash Activity zerstört somit auch deine Variablen.
Soweit ist es richtig die per Intent und Bundle an die MainActivity zu übergeben, wie gesagt an die Main und nicht an das Fragment.
So nun musst du die Daten auch an das Fragment übergeben.
Denn ich denke das die komplette Anzeige in den Fragment abläuft und das auch die wetterdaten aus dem Netz holt.

Der SplashScreen ist eine eigene Activity, genau.
Ok dann wird das mal an die Main übergeben. Bei dem Fragment hat das ja sowieso nicht wirklich funktioniert und Android Studio wollte dann auch, das

final String latitude = String.valueOf(place.getLatLng().latitude);
final String longitude = String.valueOf(place.getLatLng().longitude);

final werden. Anders hätte er das Programm nicht gestartet.

Also es läuft eigentlich nur die Anzeige über das Fragment. Die Wetterdaten werden in einer eigenen WetterActivity geholt.

wo stehen diese zwei zeilen
final String latitude = String.valueOf(place.getLatLng().latitude);
final String longitude = String.valueOf(place.getLatLng().longitude);

Wie und wo werden den die Pos daten an die API übergeben?
Dann hole sie vorher aus den globalen static variablen und gib sie der API.

Die zwei Zeilen mit latitude und longitude stehen in meiner SplashActivity, da dort der setOnPlaceSelectedListener ist, der den Namen und die Koordinaten der Stadt bezieht.

Die Pos Daten sollten dann eben an das Fragment weitergegeben werden und in die asyncTask.execute(lon, lat) eingefügt werden. Die Klasse Weather holt sich die Daten.
Habe jetzt einfach mal den kompletten Code eingefügt.

Code:
public class Weather {

    private static final String OPEN_WEATHER_MAP_URL =
            "http://api.openweathermap.org/data/2.5/weather?lat=%s&lon=%s&units=metric";

    private static final String OPEN_WEATHER_MAP_API = "afwer234t34tf34tf34t34t34t34t34t34t34t34t";
    // Insert your Weather API Here

    public interface AsyncResponse {
        void processFinish(String output1, String output2, String output3, String output4);
    }


    public static class placeIdTask extends AsyncTask<String, Void, JSONObject> {

        public AsyncResponse delegate = null;
        //Call back interface

        public placeIdTask(AsyncResponse asyncResponse) {
            delegate = asyncResponse;
            //Assigning call back interface through constructor
        }

        @Override
        protected JSONObject doInBackground(String... params) {

            JSONObject jsonWeather = null;
            try {
                jsonWeather = getWeatherJSON(params[0], params[1]);
            } catch (Exception e) {
                Log.d("Error", "Cannot process JSON results", e);
            }
            return jsonWeather;
        }

        @Override
        protected void onPostExecute(JSONObject json) {
            try {
                if(json != null){
                    JSONObject details = json.getJSONArray("weather").getJSONObject(0);
                    JSONObject main = json.getJSONObject("main");
                    DateFormat df = DateFormat.getDateTimeInstance();

                    String city = json.getString("name").toUpperCase(Locale.US) + ", " +
                            json.getJSONObject("sys").getString("country");
                    String description = details.getString("description").toUpperCase(Locale.US);
                    String temperature = String.format("%.1f", main.getDouble("temp"))+ "°";
                    String updatedOn = df.format(new Date(json.getLong("dt")*1000));

                    delegate.processFinish(city, description, temperature, updatedOn);
                }
            } catch (JSONException e) {

            }
        }
    }

    public static JSONObject getWeatherJSON(String lat, String lon){
        try {
            URL url = new URL(String.format(OPEN_WEATHER_MAP_URL, lat, lon));
            HttpURLConnection connection =
                    (HttpURLConnection)url.openConnection();

            connection.addRequestProperty("x-api-key", OPEN_WEATHER_MAP_API);

            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(connection.getInputStream()));

            StringBuffer json = new StringBuffer(1024);
            String tmp="";
            while((tmp=reader.readLine())!=null)
                json.append(tmp).append("\n");
            reader.close();

            JSONObject data = new JSONObject(json.toString());

            // This value will be 404 if the request was not successful
            if(data.getInt("cod") != 200){
                return null;
            }

            return data;
        }catch(Exception e){
            return null;
        }
    }
}

Wie gesagt. Das funktioniert soweit auch gut wenn man die Daten bereits von Anfang in dem Fragment deklariert hat.


Fang bitte nicht an State in globale Variablen zu verschieben, das ist schlechter Stil.
Entweder machst du es so wie Jogimuc gesagt hat über Listener Pattern, wo genau was hin muss für dich ist aber aus den Codeauszügen nicht zu erkennen.
Alternativ schreibst du die Werte in SharedPreferences oder eine Datenbank und liest sie aus. Das würde sowieso Sinn machen, weil dann dein Benutzer nicht bei jedem Start die Koordinaten auswählen muss, sondern sie gespeichert bleiben.

Da muss sowieso noch eine Datenbank rein, ja. Werde mir mal Deine Links anschauen. Danke :)
 
Hi das mit SharedPreferences oder db zu lösen ist auf jeden Fall die sauberre Lösung.
Verstehe ich das richtig das das Wetter wieder in einer eigenen activity abläuft.
Dachte das wäre im Fragment. Somit müsstest du ja wieder die Daten zu Activity übergeben.
Da ist in jedem Fall DB oder SharedPreferences den vorzug zu geben.
wenn du dich mit einer DB sqlite noch nicht auskennst versuche erst mal SharedPreferences in der splash setzt du sie und im Fragment oder der activity fragst du sie wieder ab.
 
  • Danke
Reaktionen: prof.dopenudel
Ja Super! Die saubere Lösung ist doch immer die Beste :D
Aber da ja sowieso kein Weg an der DB vorbei führt (auch für später und eventuell andere Apps) wäre es ja sinnvoll sich erst einmal das anzuschauen?
Werde es aber trotzdem einmal für den Anfang mit den SharedPreferences probieren.

Das hat mir sehr weiter geholfen, Eure Hilfe. Ihr seid die Besten! Auf so etwas plausibles wäre ich nie gekommen :blushing:
Danke!
 
  • Danke
Reaktionen: jogimuc
Hier ein einfaches Beispiel

Code:
// Speichen in der splash
SharedPreferences  pref = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor ed = pref.edit();

 
   ed.putString("lon"," 123456");      
   ed.putString("lat", "123456");    
   ed.commit();
Hier ist 12356 der String den du speichern möchtest


// auslesen in der activity
SharedPreferences  pref = PreferenceManager.getDefaultSharedPreferences(this);

String lon = pref.getString("lon",123456);
String lat = pref.getString("lat",123456);

Hier ist 123456 ein denfault wert der eingesetzt wird wenn es die pref. nicht gibt.
Kannst da ja die werte für Berlin einsetzen.
 
Zuletzt bearbeitet:

Ähnliche Themen

M
  • MikelKatzengreis
Antworten
10
Aufrufe
223
swa00
swa00
S
Antworten
7
Aufrufe
1.160
swa00
swa00
OnkelLon
Antworten
13
Aufrufe
1.983
OnkelLon
OnkelLon
Zurück
Oben Unten