Developing user interfaces in a mobile environment requires careful consideration of interaction with controls. One of the most common components that allows the user to select one option from a limited list is Spinner. This widget is similar to a drop-down list in web development, but has its own implementation specifics in the ecosystem Android SDK. Incorrect selection processing may result in the application not being able to read data entered by the user.

In this article we will examine in detail the mechanisms for retrieving data from a drop-down list. You will understand the difference between the getSelectedItem and getSelectedItemPositionmethods, and also learn how to correctly configure event listeners. This is critical for creating responsive registration forms, catalog filters, and application settings.

The main difficulty for novice developers is often not in displaying the list, but in reacting to changes in the selected item. We'll look at both the classic front-end approach and modern alternatives that can simplify your code. Understanding the life cycle of a widget will help you avoid common logical mistakes. AdapterView.OnItemSelectedListener, as well as modern alternatives that can simplify your code. Understanding the widget lifecycle will help you avoid common logical mistakes.

The basics of the Spinner component

Spinner is a widget that displays the currently selected element, and when clicked, shows a drop-down menu with all the available options. Internally, it works based on an adapter that binds an array of data or a resource to a list of items to display. To get the value, you need to first initialize that component in your activity or fragment.

The initialization process starts by finding the widget in the layout using the findViewByIdmethod. After this, you need to create an adapter, for example ArrayAdapter, which will be responsible for displaying strings. Only after binding the adapter to the spinner does it become functional and ready to interact with the user. Without this step, attempting to get the value will return null or throw an exception.

โš ๏ธ Warning: Never try to get a value from Spinner before you have installed an adapter for it. This will produce an empty result because the internal data array has not yet been formed.

It is important to distinguish between the type of data you are passing to the adapter. If you use an array of strings String, then getting the value will return a string. If you are using an array of objects or custom classes, the getter method will return an object of the appropriate type, which will need to be cast to the desired class. Typing plays a key role in the security of your code.

Using OnItemSelectedListener to track selection

The most reliable way to get the value immediately when the user selects a new item is to implement an interface AdapterView.OnItemSelectedListener. This approach is reactive: your code is executed exactly when the state of the interface changes. This allows you to instantly update other parts of the screen depending on the selection in the spinner.

The interface contains two methods: onItemSelected and onNothingSelected. The first one is called every time the user selects an item from the list. The second fires if the list becomes empty, which is rare, but handling this case is considered good programming practice. Inside the method onItemSelected you have direct access to the parent (the spinner itself), the selected view and position.

๐Ÿ’ก

Use the parent parameter in the onItemSelected method to cast to the Spinner type if you need to access additional properties of the widget, such as prompt or item counter.

To attach a listener, use method setOnItemSelectedListener. Inside an anonymous class or lambda expression, you can retrieve data. The position of the element is passed as an argument position, which allows you to quickly access the original data array if you have it stored in a class variable. This is often faster than querying the widget itself.

  • ๐Ÿ“ฑ The onItemSelected method also fires on first initialization if no additional flags are used.
  • ๐Ÿ”„ You can programmatically change the selection via setSelection, which will also call this listener.
  • โšก Data is accessed asynchronously relative to the main UI thread, but processing should be fast.

Implementation via a listener is especially useful in dependent list scenarios. For example, when selecting a country in the first spinner, it should automatically update the list of cities in the second. In this case, receiving a value from the first component is a trigger for loading new data into the second.

Receiving data through getSelectedItem and getSelectedItemPosition

Sometimes you do not need to react to every change, but just need to read the current state of the spinner at a specific point in time. For example, when you click the "Submit" button on a form. Methods getSelectedItem and getSelectedItemPositionare ideal for these purposes. They return the current active value without the need to set up complex listeners.

The method getSelectedItem returns the object that was selected by the user. If your adapter works with strings, you will get a string. However, if the adapter returns complex objects, you will need to cast it to the correct class. Type cast error is a common cause of crashes ClassCastException at runtime, so always check the data type in the adapter.

๐Ÿ“Š Which data retrieval method do you use most often?
getSelectedItem
getSelectedItemPosition
OnItemSelectedListener
Other

Method getSelectedItemPosition returns the integer index of the selected element. This is useful if you need to store not the value itself, but its ordinal number in the database or pass the index to another activity. Indexing starts from zero, so the first element has position 0. This is important to consider when validating data.

Consider an example of getting a row when a button is pressed:

String selectedValue = spinner.getSelectedItem.toString;

int selectedIndex = spinner.getSelectedItemPosition;

Pay attention to the call toString. Although often the object is already a string, an explicit cast ensures that you are working with a text representation. If the adapter stores objects, the method toString will return the result of their string representation, which may not be what you expect if the method is overridden incorrectly.

Handling Spinner in Fragment and life cycle

Working with spinners internally Fragment has its own peculiarities related to with the component life cycle. Widgets are available only after calling the method onViewCreated or after inflating in onCreateView. An attempt to initialize in onCreate will result in a NullPointerException because the view hierarchy has not yet been created.

A particular problem is the method onItemSelectedthat is called automatically when creating a fragment and binding an adapter. This may cause unwanted logic to occur (such as loading data) immediately upon opening the screen. To avoid this, developers often use a Boolean flag variable isFirstTime.

โš ๏ธ Warning: When the screen is rotated, the activity is recreated and the listener may fire again. Save the selection state through onSaveInstanceStateto restore the position of the spinner.

The logic with the flag is as follows: when the listener is first called, we check the flag, ignore the event and switch the flag to false. All subsequent calls will be processed normally. This is a standard pattern for separating app initialization from user input.

Code with the flag to ignore the first call

private boolean isFirstLoad = true;

spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener {

@Override

public void onItemSelected(...) {

if (isFirstLoad) {

isFirstLoad = false;

return;

}

// Your processing logic

}

});

It is also worth considering that when navigating between fragments, the spinner state may be reset if the fragment is destroyed. Using ViewModel from the Components architecture allows you to store selected data regardless of the re-creation of the interface, which makes the application more resistant to configuration changes.

Working with custom adapters and objects

Often the spinner displays not just text, but complex entities, for example, class objects Product or User. In this case, the adapter must return these objects, and in the method getView or getDropDownView you determine which field of the object to show to the user (for example, the name of the product). Retrieving the value in this case requires care.

When you call getSelectedItem for such a spinner, you get the entire object. You need to cast it to your class type in order to access the ID or other hidden fields. This is a powerful mechanism that allows you to connect visual display with business logic without using additional maps or database lookups.

Get method Return type Necessity of casting Usage
getSelectedItem Object Yes (if not String) Getting the full entity
getSelectedItemPosition int No Getting an index in the list
getSelectedItemId long No Getting a stable ID
onItemSelected View and position Depends on the adapter Reaction to change

When working with custom objects, make sure that your class overrides methods equals and hashCodeif you plan to compare selected elements programmatically. This is especially true when filtering lists or checking for duplicates. Incorrect implementation of these methods may result in the system not being able to correctly determine the selected element.

โ˜‘๏ธ Checking the custom adapter

Done: 0 / 1

Common mistakes and how to fix them

One of the most common mistakes is trying to get a value before the user has made a choice, relying on the default value. If the adapter is empty or the selection was not made explicitly, the methods may return an unexpected value. Always check the adapter's count through spinner.getAdapter.getCount before accessing data.

Another problem is related to memory leaks. If you create anonymous listener classes inside an activity and store references to them in static fields or long-lived objects, this may prevent the garbage collector from disposing of the activity. Use weak references or clear listeners in the onDestroymethod by setting them to null.

โš ๏ธ Attention: Do not perform heavy operations (network queries, database work) directly inside the onItemSelected method. This will block the UI thread. Use coroutines or separate threads.

Also, developers often forget that when the device orientation changes, the activity is recreated. If you haven't saved the spinner state, the user will lose their selection. Use a mechanism onSaveInstanceState to save the position or ID of the selected element, and restore it to onCreate or onRestoreInstanceState.

๐Ÿ’ก

Always check that the adapter is set and not null before calling methods to get data from the Spinner. This will save you from 90% of runtime errors.

FAQ: Frequently Asked Questions

How to get a value from Spinner without using an event listener?

You can call the method spinner.getSelectedItem.toString anywhere in the code after the widget is initialized, for example, inside a handler button presses. This will return the currently displayed value.

Why does onItemSelected fire when the app starts?

This is standard Android behavior. The system assumes that the item is selected by default (the first one in the list) when the adapter is initialized. To avoid unnecessary logic, use a boolean flag to ignore the first call.

How to programmatically select an element in Spinner?

Use the method spinner.setSelection(position), where position is the index of the desired element. If you need to call an event listener at the same time, use the overloaded version setSelection(position, true).

Is it possible to get the ID of an element, and not its position?

Yes, the method getSelectedItemId returns long ID if your adapter supports stable IDs (method hasStableIds returns true). This is more reliable than using position, since the order of elements can change.

How to create a hint for the Spinner?

In XML, use the attribute android:prompt for the title of the selection dialog. To display placeholder text like "Select an option" before making a selection, you need to create a special first element in the adapter with a gray text color or use a custom View.