Android: How to bind spinner to custom object list?

242,624

Solution 1

You can look at this answer. You can also go with a custom adapter, but the solution below is fine for simple cases.

Here's a re-post:

So if you came here because you want to have both labels and values in the Spinner - here's how I did it:

  1. Just create your Spinner the usual way
  2. Define 2 equal size arrays in your array.xml file -- one array for labels, one array for values
  3. Set your Spinner with android:entries="@array/labels"
  4. When you need a value, do something like this (no, you don't have to chain it):

      String selectedVal = getResources().getStringArray(R.array.values)[spinner.getSelectedItemPosition()];
    

Solution 2

I know the thread is old, but just in case...

User object:

public class User{

    private int _id;
    private String _name;

    public User(){
        this._id = 0;
        this._name = "";
    }

    public void setId(int id){
        this._id = id;
    }

    public int getId(){
        return this._id;
    }

    public void setName(String name){
        this._name = name;
    }

    public String getName(){
        return this._name;
    }
}

Custom Spinner Adapter (ArrayAdapter)

public class SpinAdapter extends ArrayAdapter<User>{

    // Your sent context
    private Context context;
    // Your custom values for the spinner (User)
    private User[] values;

    public SpinAdapter(Context context, int textViewResourceId,
            User[] values) {
        super(context, textViewResourceId, values);
        this.context = context;
        this.values = values;
    }

    @Override
    public int getCount(){
       return values.length;
    }

    @Override
    public User getItem(int position){
       return values[position];
    }

    @Override
    public long getItemId(int position){
       return position;
    }


    // And the "magic" goes here
    // This is for the "passive" state of the spinner
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // I created a dynamic TextView here, but you can reference your own  custom layout for each spinner item
        TextView label = (TextView) super.getView(position, convertView, parent);
        label.setTextColor(Color.BLACK);
        // Then you can get the current item using the values array (Users array) and the current position
        // You can NOW reference each method you has created in your bean object (User class)
        label.setText(values[position].getName());

        // And finally return your dynamic (or custom) view for each spinner item
        return label;
    }

    // And here is when the "chooser" is popped up
    // Normally is the same view, but you can customize it if you want
    @Override
    public View getDropDownView(int position, View convertView,
            ViewGroup parent) {
        TextView label = (TextView) super.getDropDownView(position, convertView, parent);
        label.setTextColor(Color.BLACK);
        label.setText(values[position].getName());

        return label;
    }
}

And the implementarion:

public class Main extends Activity {
    // You spinner view
    private Spinner mySpinner;
    // Custom Spinner adapter (ArrayAdapter<User>)
    // You can define as a private to use it in the all class
    // This is the object that is going to do the "magic"
    private SpinAdapter adapter;

        @Override
        public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Create the Users array
        // You can get this retrieving from an external source
        User[] users = new User[2];

        users[0] = new User();
        users[0].setId(1);
        users[0].setName("Joaquin");

        users[1] = new User();
        users[1].setId(2);
        users[1].setName("Alberto");

        // Initialize the adapter sending the current context
        // Send the simple_spinner_item layout
        // And finally send the Users array (Your data)
        adapter = new SpinAdapter(Main.this,
            android.R.layout.simple_spinner_item,
            users);
        mySpinner = (Spinner) findViewById(R.id.miSpinner);
        mySpinner.setAdapter(adapter); // Set the custom adapter to the spinner
        // You can create an anonymous listener to handle the event when is selected an spinner item
        mySpinner.setOnItemSelectedListener(new OnItemSelectedListener() {

            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view,
                    int position, long id) {
                // Here you get the current item (a User object) that is selected by its position
                User user = adapter.getItem(position);
                // Here you can do the action you want to...
                Toast.makeText(Main.this, "ID: " + user.getId() + "\nName: " + user.getName(),
                    Toast.LENGTH_SHORT).show();
            }
            @Override
            public void onNothingSelected(AdapterView<?> adapter) {  }
        });
    }
}

Solution 3

Simplest Solution

After scouring different solutions on SO, I found the following to be the simplest and cleanest solution for populating a Spinner with custom Objects. Here's the full implementation:

User.java

public class User{
    public int ID;
    public String name;

    @Override
    public String toString() {
        return this.name; // What to display in the Spinner list.
    }
}    

res/layout/spinner.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:padding="10dp"
    android:textSize="14sp"
    android:textColor="#FFFFFF"
    android:spinnerMode="dialog" />

res/layout/your_activity_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <Spinner android:id="@+id/user" />

</LinearLayout>

In Your Activity

List<User> users = User.all(); // This example assumes you're getting all Users but adjust it for your Class and needs.
ArrayAdapter userAdapter = new ArrayAdapter(this, R.layout.spinner, users);

Spinner userSpinner = (Spinner) findViewById(R.id.user);
userSpinner.setAdapter(userAdapter);
userSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        // Get the value selected by the user
        // e.g. to store it as a field or immediately call a method
        User user = (User) parent.getSelectedItem();
    }

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

Solution 4

For simple solutions you can just Overwrite the "toString" in your object

public class User{
    public int ID;
    public String name;

    @Override
    public String toString() {
        return name;
    }
}

and then you can use:

ArrayAdapter<User> dataAdapter = new ArrayAdapter<User>(mContext, android.R.layout.simple_spinner_item, listOfUsers);

This way your spinner will show only the user names.

Solution 5

By far the simplest way that I've found:

@Override
public String toString() {
    return this.label;           
}

Now you can stick any object in your spinner, and it will display the specified label.

Share:
242,624

Related videos on Youtube

Niko Gamulin
Author by

Niko Gamulin

Engineer/Researcher, working in the field of Machine Learning.

Updated on June 29, 2021

Comments

  • Niko Gamulin
    Niko Gamulin almost 3 years

    In the user interface there has to be a spinner which contains some names (the names are visible) and each name has its own ID (the IDs are not equal to display sequence). When the user selects the name from the list the variable currentID has to be changed.

    The application contains the ArrayList

    Where User is an object with ID and name:

    public class User{
            public int ID;
            public String name;
        }
    

    What I don't know is how to create a spinner which displays the list of user's names and bind spinner items to IDs so when the spinner item is selected/changed the variable currentID is set to appropriate value.

    I would appreciate if anyone could show the solution of the described problem or provide any link useful to solve the problem.

    Thanks!

  • jamesc
    jamesc over 11 years
    This should be the accepted answer. Creating a custom adapter is definitely the way to go.
  • Joshua Pinter
    Joshua Pinter over 10 years
    One small caveat is that this doesn't set the currentID as soon as the Spinner value changes. Most of the time you only need the value of the Spinner after subsequently hitting a button, such as Submit or Save, not immediately after the Spinner changes and if it can be avoid, this provides a much simpler solution.
  • lantonis
    lantonis about 10 years
    This worked fine. Very nice. But one problem. The spinner now changed its style. I try to set a new xml to change the paddings, text size but nothing happens. I change the spinner it self from the xml and stil nothing. The only thing that changes is if i change the text size of the TextView from within the SpinAdapter. Is there a wa to keep the default spinner style/theme but load these kind of values?
  • Anti Earth
    Anti Earth about 10 years
    Is there an elegant way to access the defined labels (for comparison to selectedVal), so one can avoid hard-coding the string labels in code?
  • Binoy Babu
    Binoy Babu over 9 years
    This is a case of duplication of data and should be avoided.
  • CularBytes
    CularBytes about 9 years
    I've did this, but I'm getting tremendous amount of lag. While I just add 3 times. I did inflate an view to make my layout, it only contains an Icon and text. The logcat confirms me by saying Skipped 317 frames! The application may be doing too much work on its main thread. Any ideas?
  • Sasa
    Sasa almost 9 years
    @Joaquin Alberto I Done this exactly the way you said, now I want to set a User to this spinner , can you please tell me how can i do it?
  • Sasa
    Sasa almost 9 years
    @Joaquin i did exactly like what you have posted, how can I set a user to show in spinner. each time I want to set different user, cant you please tell me how can i do this?
  • Ahmad Alkhatib
    Ahmad Alkhatib over 8 years
    +1 for this line :) User user = adapter.getItem(position);
  • Srneczek
    Srneczek over 8 years
    So wrong from the scalability point of view - it means your "objects" can never be dynamic - bad practice
  • Srneczek
    Srneczek over 8 years
    Why does getItemId() method returns position isntead of user id? Otherwise it should be accepted answer - the one currently accepted is bad practice event tho it can be usefull to someone
  • Srneczek
    Srneczek over 8 years
    @Bostone I didnt check the time but I think it is irrelevant in this case. Adapters are there for some reason and I bet this is not about SDK change in time. It is one of reasons why they created adapters at first place. So you can serve list of complex objects, so this was in my opinion always been bad practice usable only in the very simple cases but that doesnt make it good practice.
  • Admin
    Admin over 8 years
    @Srneczek maybe you should check the date of the answers and posts this was a good relavent answer for the user.
  • Srneczek
    Srneczek over 8 years
    @Bob'sBurgers read what I answered to Bostone - are you saying there were no Adapters back in 09? If there were none I agree with you but as far as I can see in docs developer.android.com/reference/android/widget/Adapter.html it says "Added in API level 1" so I don't think time has to do anything with my comment.
  • Admin
    Admin over 8 years
    @Srneczek lol of course there where but again obviously the answer worked for the developer asking the question. Still comes back to you as a commenter looking at the date. I've had the temptation to have a comment on old posts but I move on.
  • Srneczek
    Srneczek over 8 years
    @Bob'sBurgers you are missing the point. I never said it is not working I said it is bad practice and I am right about that. global variables or code in 1 very, very long file are working too you know... Btw you should comment on old threads because they still appear in todays searches and ppl will use those (todays) wrong answers.
  • Admin
    Admin over 8 years
    @Smeczek lol I didn't miss the point. It's already been accepted and the answer worked.
  • Srneczek
    Srneczek over 8 years
    @Bob'sBurgers man seriously? Look at 133 up votes at second answer. This answer is no longer good answer and it never was a good one. You'r obviously missing point of more then just my comments. My last attempt: like it or not WORKING code doesn't have to be (and usually is not) GOOD code. I recommend you to search the term "good practice" first.
  • Admin
    Admin over 8 years
    @Srneczek yes seriously. It's already been done and finished and worked for the person who asked the question as I'm sure they've moved on. This is not meant as a personal attack on anyone. The person who asked the question has the right to accept whatever answer they choose. I personally use BaseAdapter's but this isn't mine or yours question just saying.
  • Srneczek
    Srneczek over 8 years
    @Bob'sBurgers what does this have to do with what I said? I said it is "bad practice" - if it works or not is irrelevant, which makes all your comments useless (thats why I am saying you are missing the point). Someone is going to edit the code the person who asked this wrote and it is usually nightmare when programmers don't follow best practices.
  • Srneczek
    Srneczek over 8 years
    @user5530425 exactly, thank you. Thats why I am leaving comment, to direct people to the right answer which is the one by Joaquin Alberto
  • x13
    x13 almost 8 years
    I found this to work, and by simply putting the last line somewhere else, you can get around the "problem" @JoshPinter described.
  • Sruit A.Suk
    Sruit A.Suk over 7 years
    this answer does not actually answer the question, but it point some important things relate to this answer
  • Selvin
    Selvin over 7 years
    and where is the view reusing? ... very bad implementation ... also extending ArrayAdapter is not a good practice when you are overriding almost all method from BaseAdapter ...
  • jackcar
    jackcar about 7 years
    Just a modification to reuse the view, instead of creating a new TextView, it should do like this: TextView label = (TextView) super.getView(position, convertView, parent)
  • Joshua Pinter
    Joshua Pinter about 7 years
    @x13 That's correct. All you need to do to get the value on change is setup a "on change" listener and then put the getSelectedItem() call in that. Thanks for the tip.
  • Joshua Pinter
    Joshua Pinter about 7 years
    @theapache64 Thumbs up... you learn something new every day. :)
  • Juan De la Cruz
    Juan De la Cruz over 6 years
    3 years have passed and works amazing! Can't believe that people overcomplicate this simple thing.
  • Joshua Pinter
    Joshua Pinter over 6 years
    @JuanDelaCruz Android and java make it easy to overcomplicate things. Simplification for the win!
  • Juan De la Cruz
    Juan De la Cruz over 6 years
    @JoshuaPinter I would add that just new ArrayAdapter(...) triggers a warning and new ArrayAdapter<>(...)is enough to avoid it...
  • Babken Vardanyan
    Babken Vardanyan about 6 years
    @lantonis Check this answer which I just updated, now the style is consistent with the default style of the android spinner.
  • Arnold Brown
    Arnold Brown about 6 years
    How to set the spinner on EDIT to selected item which returns on response?
  • FutureShocked
    FutureShocked over 5 years
    You're still extending a class here. You're just doing it with an anonymous object.
  • Laurent
    Laurent over 3 years
    Thanks a lot for showing the Kotlin version
  • AaA
    AaA about 3 years
    @Srneczek, I would love to see your right answer, but seems I'm having trouble finding it.
  • Srneczek
    Srneczek about 3 years
    @AaA as me and user5530425 stated above, its the answer from Joaquin Alberto.
  • AaA
    AaA about 3 years
    @Srneczek Did you notice that answer loosing UI theme because of using simple_spinner_item? application users don't care how you do your programming and probably nobody will ever touch that screen ever again in application lifetime. Also same users will most probably complain about the UI and its loading speed. As Answer also explains for a spinner with 2~3 fixed choices, I wouldn't go for extending classes and will use a simple and fast way.
  • Srneczek
    Srneczek about 3 years
    @AaA No I have not. My comments are 5 years old. Only thing I can tell you is "best practices wherever u go". Users and clients dont care, you are the one who should. It will cost u a lot of $ if u dont. But thats general rule and ofc u gotta do tradeoffs sometimes.