0

I need to display the value at a specific position in my spinner but get a

java.lang.IllegalStateException: ArrayAdapter requires the resource ID to be a TextView

error because my spinner uses a simple list set up as follows:

    mSpinner = findViewById(R.id.spinner);
    TextView msgTv = findViewById(R.id.errorMessage);
    if (listTypes.size() > 0) {
        /*
        hide the error message
         */
        msgTv.setVisibility(View.GONE);
        /*
         *  the List has more than zero elements, so print them out in LogCat
         */
        for(int i = 0; i < listTypes.size(); i++) {
            Type type = listTypes.get(i);
            int id = type.getId();
            typeNameString = type.getType();
            Log.d(TAG, "type " + typeNameString + " with an ID of: " + id);
        }
        //create spinner List<> of Type names only for use with the spinner
        spinnerList =  new ArrayList<>();
        for (int i = 0; i < listTypes.size(); i++) {
            typeNameString = listTypes.get(i).getType();
            spinnerList.add(typeNameString);
            Log.d(TAG, "Type " + i + " typeNameString is: " + typeNameString);
        }
        /*
         * set the adapter to use 'this' context, the default Android spinner widget, typeCursor
         * tyepCursor column as the source (from), display in the 'to' destination, no flags
         */
        typeArrayAdapter = new ArrayAdapter<>(this, R.layout.support_simple_spinner_dropdown_item, spinnerList);
        mSpinner.setAdapter(typeArrayAdapter);
    } else{
        /*
         * no types exist in TABLE_TYPE, so add them
         */
        msgTv.setText(R.string.no_types);
        msgTv.setVisibility(View.VISIBLE);
    }

My code to generate the spinner is as follows:

private void setProviderInfo(){
    Log.d(TAG, "Entered setProviderInfo");
    .
    .
    .
    mSpinner.setAdapter(typeArrayAdapter);
    mSpinner.setSelection(spinnerList.indexOf(type), true);
    //TODO: try getView() to display the setSelection() value in spinner
    ViewGroup view = (ViewGroup) findViewById(android.R.id.content);
    typeArrayAdapter.getView(position, mSpinner, view);
    .
    .
    .
}

My spinner's xml is:

<Spinner
    android:id="@+id/spinner"
    style="@style/Spinner"
    android:prompt="@string/select_provider_type"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/tvProviderTitle" />

The style is:

<style name="Spinner">
    <item name="android:layout_width">match_parent</item>
    <item name="android:layout_height">50dp</item>
    <item name="android:layout_marginTop">8dp</item>
    <item name="android:background">@android:color/holo_blue_bright</item>
    <item name="android:layout_marginStart">8dp</item>
    <item name="android:paddingTop">5dp</item>
    <item name="android:paddingBottom">10dp</item>
    <item name="android:layout_marginEnd">8dp</item>
    <item name="android:clickable">true</item>

My spinner works that means that ArrayAdapter.getView() works on the ArrayList that is spinnerList using R.layout.support_simple_spinner_dropdown_item, not a TextView. So, what do I need to do to get my direct call to getView() to display the value at setSelection() as happens when Android does it by default?

I have read the following (there are more), but didn't find anything useful as the initial, default call to ArrayAdapter.getView() works without a TextView widget in my xml:

ArrayAdapter requires the resource ID to be a TextView

“ArrayAdapter requires the resource ID to be a TextView” issue

ArrayAdapter requires the resource ID to be a TextView in DialogFragment

“ArrayAdapter requires the resource ID to be a TextView” xml problems

“ArrayAdapter requires the resource ID to be a TextView” error for AndroidX

Android ExpandableListAdapter: ArrayAdapter requires the resource ID to be a TextView

Jeff
  • 431
  • 4
  • 16
  • It appears from some investigation that yes, ArrayAdapter.getView() is intended to be used to actually display the spinner showing the value designated by setSelection(). This is discussed at [Android Adapter Tutorial: What Are Adapters in Android](https://www.edureka.co/blog/what-are-adapters-in-android/). I will post the answer when I figure this out. – Jeff Apr 04 '20 at 15:03
  • If I understood correctly what you need is to create your own custom adapter. There you will override the getView method and do whatever you want, and also inflate what ever layout you need – sebasira Apr 05 '20 at 02:14
  • @sebasira, I've come to that conclusion because in performing debugging and stepping into ArrayAdapter.java I found this in the comments: "_By default, the array adapter creates a view by calling {@link Object#toString()} on each data object in the collection you provide, and places the result in a TextView._" So, I would need to reference the default TextView, which I don't think that I can do, but I will look into it and post my ultimate answer. – Jeff Apr 05 '20 at 14:53

1 Answers1

0

OK, you cannot set the spinner to a specific value when you use the Android provided resources, such as R.layout.support_simple_spinner_dropdown_item. As I noted in the comments under the original question, the ArrayAdapter created a View that you cannot access. This mandates that you create a custom adapter. There are many examples of doing this on the web and on stackoverflow.

Central to this is to have your spinner declared in the Activity's xml layout file, such as:

<Spinner
    android:id="@+id/spinner"
    style="@style/Spinner"
    android:prompt="@string/select_provider_type"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/tvProviderTitle" />

and the TextView element that the Adapter will write your spinnerList values to in its own xml file:

<?xml version="1.0" encoding="utf-8"?>

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/typeName"
    style="@style/TypeName"/>

The TextView will be filled by your custom adapter using its getView() method, for the spinner itself, and the getDropDownView() method, for the expanded list upon click:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    Log.d(TAG, "Entered: getView");
    convertView = mLayoutInflater.inflate(R.layout.type_name_only, null);
    TextView typeName = convertView.findViewById(R.id.typeName);
    typeName.setText(types.get(position));
    return convertView;
}

@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
    Log.d(TAG, "Entered: getDropDownView");
    convertView = mLayoutInflater.inflate(R.layout.type_name_only, null);
    TextView typeName = convertView.findViewById(R.id.typeName);
    typeName.setText(types.get(position));
    typeName.setBackgroundColor(GREEN);
    typeName.setTextColor(BLUE);
    return convertView;
}

Note that you must also override the getCont() method so that the adapter can determine how many elements to add to your spinner:

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

If it isn't obvious to you, it wasn't at first to me, you do not declare nor set your TextView in your Activity, only your spinner:

    mSpinner = findViewById(R.id.spinner);

In your Activity, set the spinner to a specific element in its list as follows:

    mSelectTypeAdapter = new SelectTypeAdapter(mContext, R.id.spinner, spinnerList);
    mSpinner.setAdapter(mSelectTypeAdapter);
    mSpinner.setOnItemSelectedListener(this);
    mSpinner.setSelection(spinnerList.indexOf(itemToBeSelected), true);
    ViewGroup viewGroup = findViewById(android.R.id.content);
    mSelectTypeAdapter.getView(spinnerList.indexOf(itemToBeSelected), mSpinner, viewGroup);
  • mSpinner.setSelection() selects the specific list index to show, in this case that of the itemToBeSelected spinnerList item
  • The ViewGroup is that of "this" Activity
  • mSelectTypeAdapter.getView() sets the list item that is to be displayed in the spinner in accordance with its index in the spinnerList

Android will display each of your list elements in its own TextView in the Activity's spinner as you set in the Adapter's getView() and getDropDownView() methods. I made mine very colorful and different to verify that it worked as I thought it would.

Jeff
  • 431
  • 4
  • 16