Charlie
Charlie

Reputation: 3104

How do I access a Spinner's selected item when I've dynamically added the Spinner to a ListView?

I am trying to create a simple settings page for an app.

I want to create a list of objects, whose layout I define in an XML file.

Within the layout of these objects is a title TextView, a help TextView, and a Spinner.

As I'm adding these objects dynamically in code, how do I then reference these individual Spinner views so that I can get the selected value?

I currently have a Setting object (what the list will be populated with):

public class Setting {

    private String name;
    private String description;
    private String[] values;

    public Setting(String name, String description, String[] values) {
        this.name = name;
        this.description = description;
        this.values = values;
    }

    public String getName() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    public String[] getValues() {
        return values;
    }
}

A custom ArrayAdapter to translate Setting objects to Views:

(R.layout.spinner_row_text_layout.xml is just a text view to define the layout of the spinner's items)

public class SettingsAdapter extends ArrayAdapter<Setting> {

    public SettingsAdapter(Context context, ArrayList<Setting> settings) {
        super(context, 0, settings);
    }

    @Override
    // Takes the position in the array, the view to convert, and the parent of the view as parameters
    public View getView(int position, View convertView, ViewGroup parent) {

        // Get the item in this position
        Setting setting = getItem(position);

        // Check if a view is being reused, otherwise inflate the view (look at view recycling in lists)
        if (convertView == null) {
            convertView = LayoutInflater.from(getContext()).inflate(R.layout.settings_row_view, parent, false);
        }

        // findViewById in the given view to convert
        TextView title   = (TextView) convertView.findViewById(R.id.titleText);
        TextView help    = (TextView) convertView.findViewById(R.id.helpText);
        Spinner  spinner = (Spinner)  convertView.findViewById(R.id.spinner);

        ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(getContext(), R.layout.spinner_row_text_layout, setting.getValues());
        spinner.setAdapter(spinnerAdapter);

        title.setText(setting.getName());
        help.setText(setting.getDescription());

        return convertView;
    }
}

And the following layout files...

Settings page layout:

<RelativeLayout

    android:id="@+id/relativeLayout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.cgillions.counter.SettingsActivity">

    <ListView
        android:id="@+id/settingsList"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:choiceMode="singleChoice"
        android:divider="@color/black"
        android:dividerHeight="5dp"/>

</RelativeLayout>

Layout for each ListView item:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1">

        <TextView
            android:id="@+id/titleText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"

            android:text="Hello"
            android:textSize="20sp"
            android:textColor="@color/black"

            android:gravity="center_vertical"/>

        <TextView
            android:id="@+id/helpText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"

            android:text="@string/animation_settings_hint"
            android:textSize="12sp"
            />

    </LinearLayout>

    <Spinner
        android:id="@+id/spinner"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1.5"/>

</LinearLayout>

When I start the Activity that corresponds to the Settings page layout, I get the following error:

at junit.framework.Assert.assertNotNull(Assert.java:211)

at com.example.cgillions.counter.SettingsActivity.onCreate(SettingsActivity.java:46)

at android.app.Activity.performCreate(Activity.java:5990)

This is my onCreate() method:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_settings);

    setTitle("Settings");

    // Create a new Setting object in order to populate list
    animSetting = new Setting("Animation", getResources().getString(R.string.animation_settings_hint), getResources().getStringArray(R.array.anim_values));

    settings = new ArrayList<>();
    settings.add(animSetting);

    ListView settingsList = (ListView) findViewById(R.id.settingsList);
    assertNotNull(settingsList);
    settingsList.setAdapter(new SettingsAdapter(this, settings));

    animSpinner = (Spinner) settingsList.findViewById(R.id.spinner);
    assertNotNull(animSpinner);
}

The error occurs on the last line - e.g. the View with ID 'spinner' cannot be found (and therefore is null). I'm accessing the Spinner incorrectly - can someone advise as to how I should do this?

I want to get the Spinner object in order to get the selected item.

Upvotes: 2

Views: 1068

Answers (1)

C. Todd
C. Todd

Reputation: 478

Something you might try. Add an Spinner instance to your Setting class:

public class Setting {

    private String name;
    private String description;
    private String[] values;
    private Spinner spinner;

    public Setting(String name, String description, String[] values) {
        this.name = name;
        this.description = description;
        this.values = values;
    }

    public String getName() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    public String[] getValues() {
        return values;
    }

    public void setSpinner(Spinner s) {
        spinner = s;
    }

    public Spinner getSpinner() {
        return spinner;
    }
}

Then, in your getView() method, provide the Spinner to your setting instance:

ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(getContext(), R.layout.spinner_row_text_layout, setting.getValues());
spinner.setAdapter(spinnerAdapter);
if (setting.getSpinner() == null) {
    setting.setSpinner(spinner);
}

Then, assuming you can access your list of Settings, you can access the spinner associated with each one. If you find a Setting with a null spinner, then it never came into view in the Activity, and that would mean the user never changed it. But I don't know if that's really what you want.

This will allow you to access the spinner for each Setting, but you have no logic for storing the user's selection, or for setting the selected item in the spinner. I.e., if the spinner has options A, B and C, and the user has selected B, you can now read the B but you don't have a place to store it. The Setting class should have a field for holding the chosen value. And your getView() method should call spinner.setSelection() to tell it to show B as the current value. Otherwise, it will always show A regardless of what the user has previously chosen.

Upvotes: 2

Related Questions