Spinner wird nicht aktualisiert

jerevat

Mitglied
Hallo,

ich habe hier zwei Spinner, die Länder (countries) und deren Bundesländer (states) wiedergeben sollen.
Die Länder und Bundesländer sind als Ressourcen hinterlegt:

XML:
<resources>
<!-- Länder -->
    <string-array name="countries">
        <item>Österreich</item>
        <item>Schweiz</item>
        <item>Deutschland</item>
    </string-array>
<!-- Bundesländer in Österreich -->
    <string-array name="states_at">
        <item>ATDummy1</item>
        <item>ATDummy2</item>
    </string-array>
<!-- Bundesländer in Schweiz -->
    <string-array name="states_ch">
        <item>CHDummy1</item>
        <item>CHDummy2</item>
    </string-array>
<!-- Bundesländer in Deutschland -->
    <string-array name="states_de">
        <item>DEDummy1</item>
        <item>DEDummy2</item>
    </string-array>
</resources>

Hinzu kommt eine Enumeration (CountryCode) die die Länder-Auswahl im ersten Spinner steuert:

Java:
public enum CountryCode {    
    AT, CH, DE;
    public static CountryCode setInteger(int value) {
        switch (value) {
            case 0: return AT;
            case 1: return CH;
            case 2: return DE;
        }
        return null;
    }
}

Das Problem im nachfolgenden Code ist, dass der zweite Spinner mit den Bundesländern die voreingestellte Auswahl zwar lädt aber danach nicht mehr aktualisiert wird, sobald im ersten Spinner ein anderes Land ausgewählt wird.
Mittels Toast.makeText() stelle ich fest, dass das Bundesländer-Array (_states) sehr wohl aktualisiert wird, aber der zweite Spinner unverändert bleibt, auch nachdem sein Adapter mit _statesAdapter.notifyDataSetChanged() aktualisiert wird.
Woran scheitert es? Was meint ihr dazu?

Java:
public class MainActivity extends AppCompatActivity {
    Spinner _spCountries, _spStates;
    CountryCode _countryCode = CountryCode.AT;
    ArrayAdapter<String> _countriesAdapter, _statesAdapter;
    String[] _countries, _states;

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

        _spCountries = (Spinner) findViewById(R.id.spCountries);
        _spStates = (Spinner) findViewById(R.id.spStates);

        _countries = getResources().getStringArray(R.array.countries);
        updateStates(_countryCode);

        _countriesAdapter = new ArrayAdapter<>(this, androidx.constraintlayout.widget.R.layout.support_simple_spinner_dropdown_item, _countries);
        _spCountries.setAdapter(_countriesAdapter);
        _statesAdapter = new ArrayAdapter<>(this, androidx.constraintlayout.widget.R.layout.support_simple_spinner_dropdown_item, _states);
        _spStates.setAdapter(_statesAdapter);

        _spCountries.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                _countryCode = CountryCode.setInteger(i);
                updateStates(_countryCode);
                _statesAdapter.notifyDataSetChanged();

               String s;
               if (_states == null)
                   s = "null";
               else
                   s = _states[0];
               Toast.makeText(getApplicationContext(), s , Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) { }
        });
    }

    private void updateStates(CountryCode nations) {    
        _states = null;
        switch (nations) {
            case AT: _states = getResources().getStringArray(R.array.states_at);
                break;
            case CH: _states = getResources().getStringArray(R.array.states_ch);
                break;
            case DE: _states = getResources().getStringArray(R.array.states_de);
                break;
        }
    }
}
 
Das Problem dürfte sein, dass dein StatesAdapter nix von der Änderung mitbekommt. Den du änderst nicht den Inhalt des _states Array sondern weißt da einfach eine neue Variable zu. Das funktioniert so nicht, der Adapter hat immer noch eine Variable übergeben bekommen, die auf den alten Inhalt zeigt.

Du darfst nicht _states was neues zuweisen, sondern muss den Inhalt austauschen. Da eignet sich vermutlich eine Liste besser als ein Array - da kannst du einfach list.clear(); list.add(...); machen und das sollte gehen.
 
Das Problem dürfte sein, dass dein StatesAdapter nix von der Änderung mitbekommt. Den du änderst nicht den Inhalt des _states Array sondern weißt da einfach eine neue Variable zu. Das funktioniert so nicht, der Adapter hat immer noch eine Variable übergeben bekommen, die auf den alten Inhalt zeigt.

Du darfst nicht _states was neues zuweisen, sondern muss den Inhalt austauschen. Da eignet sich vermutlich eine Liste besser als ein Array - da kannst du einfach list.clear(); list.add(...); machen und das sollte gehen.
Das habe ich nun angepasst:

Java:
List<String> _states;
// List<String> _states = new ArrayList<>();

private void updateStates(CountryCode nations) {
    switch (nations) {   
        // _states.clear();  // führt zum Absturz
        case AT: _states = Arrays.asList(getResources().getStringArray(R.array.states_at));
            break;
        case CH: _states = Arrays.asList(getResources().getStringArray(R.array.states_ch));
            break;
        case DE: _states = Arrays.asList(getResources().getStringArray(R.array.states_de));
            break;
    }
}

Das führt zum gleichen Ergebnis: Der zweite Spinner wird nicht aktualisiert.
Gibt es andere Methoden, eine String-Array-Ressource in eine List<> zu übergeben?
 
Du weist da doch schon wieder eine neue List zu und aktualisierst die vorhandene nicht.

Die .clear Methode wurde schon genannt. Statt dem add wirst Du vermutlich addAll nutzen wollen.

Also etwas wie:
Java:
private void updateStates(CountryCode nations) {
    _states.clear();
    switch (nations) {   
        // _states.clear();  // führt zum Absturz
        case AT:
            _states.addAll(Arrays.asList(getResources().getStringArray(R.array.states_at)));
            break;
        case CH:
            _states.addAll(Arrays.asList(getResources().getStringArray(R.array.states_ch)));
            break;
        case DE:
            _states.addAll(Arrays.asList(getResources().getStringArray(R.array.states_de)));
            break;
    }
}

Das ist jetzt im Forum entstanden, also Tippfehler sind möglich.
 
Und bezüglich der Bezeichner: public static CountryCode setInteger(int value) { ist etwas dubios. Du setzt doch keinen Integer. Das wäre also eher etwas wie ein getById oder so.

Und da kannst Du übrigens auch über values() gehen, also etwas wie:
Java:
    public enum CountryCode {
        AT, CH, DE;
        public static CountryCode getById(int index) {
            return values()[index];
        }
    }

Wobei ich da dann sowas lieber hart kodiere und nicht über die Reihenfolge gehe. Dann hat jedes Element ein Attribut Id. Das wäre dann etwas wie:
Java:
    public enum CountryCode {
        AT(0),
        CH(1),
        DE(2);

        private final int id;

        CountryCode(final int id) {
            this.id = id;
        }

        public static CountryCode getById(int index) {
            for (CountryCode countryCode: values()) {
                if (countryCode.id == index) return countryCode;
            }

            return null;
        }
    }

Das nur als kleiner Hinweis, weil es mir so direkt aufgefallen ist.
 
Java:
private void updateStates(CountryCode nations) {
    _states.clear();
    switch (nations) {  
        // _states.clear();  // führt zum Absturz
        case AT:
            _states.addAll(Arrays.asList(getResources().getStringArray(R.array.states_at)));
            break;
        case CH:
            _states.addAll(Arrays.asList(getResources().getStringArray(R.array.states_ch)));
            break;
        case DE:
            _states.addAll(Arrays.asList(getResources().getStringArray(R.array.states_de)));
            break;
    }
}
So funktioniert das. Danke!

Und bezüglich der Bezeichner: public static CountryCode setInteger(int value) { ist etwas dubios. Du setzt doch keinen Integer. Das wäre also eher etwas wie ein getById oder so.

Und da kannst Du übrigens auch über values() gehen, also etwas wie:
Java:
    public enum CountryCode {
        AT, CH, DE;
        public static CountryCode getById(int index) {
            return values()[index];
        }
    }
Ich tue mir immer schwer mit Namen. =D
Die Schreibweise gefällt mir. An Java muss ich mich noch gewöhnen. Damit habe ich erst seit gut zwei Wochen zu tun.

LimDul und du wart eine große Hilfe!​

 
Ach ja - was ich ganz übersehen hatte:
// _states.clear(); // führt zum Absturz

Das Problem dürfte da gewesen sein, dass in der Java Syntax keine Statements innerhalb des switch Blocks sein dürfen. Da dürfen nur die case Elemente kommen. Das Statement hätte also in den Block nach dem case kommen müssen. Es wäre also auch gegangen:
Java:
private void updateStates(CountryCode nations) {
    switch (nations) { 
        case AT:
            _states.clear();
            _states.addAll(Arrays.asList(getResources().getStringArray(R.array.states_at)));
            break;

Da dies aber doch nach jedem case gekommen wäre, hat es Sinn gemacht, es vor das switch zu ziehen.

Ich tue mir immer schwer mit Namen. =D
Ja, das ist am Anfang normal. Das kommt aber von ganz alleine. Bei Code Reviews ist das am Anfang bei jedem oft so, dass da die Rename Funktionalität der IDE zum glühen gebracht wird. Zumindest ist das meine Erfahrung.

Damit habe ich erst seit gut zwei Wochen zu tun.
Dafür ist es doch schon ein recht ordentliches Ergebnis. Bleib einfach weiter dran und weiter viel Spaß beim Entwickeln!
 

Zurück
Oben