148 votes

Android : Comment lier un spinner à une liste d'objets personnalisés ?

Dans l'interface utilisateur, il doit y avoir un spinner qui contient des noms (les noms sont visibles) et chaque nom a son propre ID (les ID ne sont pas égaux à la séquence d'affichage). Lorsque l'utilisateur sélectionne un nom dans la liste, la variable currentID doit être modifiée.

L'application contient la ArrayList

Où l'utilisateur est un objet avec un ID et un nom :

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

Ce que je ne sais pas, c'est comment créer un spinner qui affiche la liste des noms d'utilisateurs et lier les éléments du spinner à des ID de sorte que, lorsque l'élément du spinner est sélectionné/changé, la variable currentID est définie à la valeur appropriée.

J'apprécierais si quelqu'un pouvait montrer la solution du problème décrit ou fournir tout lien utile pour résoudre le problème.

Gracias.

389voto

Joaquin Alberto Points 783

Je sais que le fil est vieux, mais juste au cas où...

Objet de l'utilisateur :

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;
    }
}

Adaptateur de vrille personnalisé (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;
    }
}

Et la mise en œuvre :

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) {  }
        });
    }
}

48 votes

Cela devrait être la réponse acceptée. La création d'un adaptateur personnalisé est certainement la meilleure solution.

12 votes

Ça a bien marché. Très bien. Mais il y a un problème. Le spinner a maintenant changé de style. J'essaie de définir un nouveau xml pour changer les paddings, la taille du texte mais rien ne se passe. Je change le spinner lui-même à partir du xml et toujours rien. La seule chose qui change, c'est si je modifie la taille du texte du TextView depuis le SpinAdapter. Existe-t-il un moyen de conserver le style/thème par défaut du spinner mais de charger ce type de valeurs ?

1 votes

J'ai fait ça, mais j'ai un décalage énorme. Alors que je viens d'ajouter 3 fois. J'ai gonflé une vue pour faire ma mise en page, elle ne contient qu'une icône et du texte. Le logcat me confirme en disant Skipped 317 frames! The application may be doing too much work on its main thread. Des idées ?

122voto

Josh Pinter Points 3814

La solution la plus simple

Après avoir parcouru différentes solutions sur SO, j'ai trouvé ce qui suit comme étant la solution la plus simple et la plus propre pour peupler un fichier Spinner avec une personnalisation Objects . Voici l'implémentation complète :

Utilisateur.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>

Dans votre activité

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) {
    }
});

0 votes

Une petite mise en garde s'impose : cela ne permet pas de définir le paramètre currentID dès que le Spinner les changements de valeur. La plupart du temps, vous n'avez besoin que de la valeur de l'élément Spinner après avoir appuyé sur un bouton, par exemple Soumettre o Sauvez et non pas immédiatement après le Spinner et si cela peut être évité, cela constitue une solution beaucoup plus simple.

0 votes

J'ai trouvé que cela fonctionnait, et en mettant simplement la dernière ligne ailleurs, vous pouvez contourner le "problème" que décrit par @JoshPinter.

0 votes

@x13 C'est exact. Tout ce que vous avez à faire pour obtenir la valeur lors d'un changement est de configurer un écouteur "on change" et ensuite de mettre la balise getSelectedItem() appelle à cela. Merci pour le tuyau.

47voto

Bostone Points 14208

Vous pouvez regardez cette réponse . Vous pouvez également opter pour un adaptateur personnalisé, mais la solution ci-dessous convient pour les cas simples.

Voici un re-poste :

Donc, si vous êtes venu ici parce que vous voulez avoir à la fois des étiquettes et des valeurs dans le Spinner - voici comment j'ai fait :

  1. Il suffit de créer votre Spinner de la manière habituelle
  2. Définissez 2 tableaux de taille égale dans votre array.xml fichier -- un tableau pour les étiquettes, un tableau pour les valeurs
  3. Définissez votre Spinner con android:entries="@array/labels"
  4. Lorsque vous avez besoin d'une valeur, faites quelque chose comme ceci (non, vous n'êtes pas obligé de l'enchaîner) :

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

3 votes

Existe-t-il un moyen élégant d'accéder aux étiquettes définies (pour les comparer à selectedVal), afin d'éviter de coder en dur les étiquettes des chaînes dans le code ?

0 votes

Il s'agit d'un cas de duplication des données qui doit être évité.

27 votes

C'est une erreur du point de vue de l'évolutivité - cela signifie que vos "objets" ne peuvent jamais être dynamiques - une mauvaise pratique.

6voto

Sonja Points 104

Cela fonctionne bien pour moi, le code nécessaire autour de la chose getResource() est le suivant :

spinner = (Spinner) findViewById(R.id.spinner);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

        @Override
        public void onItemSelected(AdapterView<?> spinner, View v,
                int arg2, long arg3) {
            String selectedVal = getResources().getStringArray(R.array.compass_rate_values)[spinner.getSelectedItemPosition()];
            //Do something with the value
        }

        @Override
        public void onNothingSelected(AdapterView<?> arg0) {
            // TODO Auto-generated method stub
        }

    });

Il faut juste s'assurer (par soi-même) que les valeurs des deux tableaux sont correctement alignées !

5voto

Alberto Ricart Points 41

Basé sur l'exemple de Joaquin Alberto (merci), mais il fonctionne pour n'importe quel type (vous devriez implémenter toString() dans le type, afin de pouvoir formater la sortie.

import java.util.List;

import android.content.Context;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class SpinAdapter<T> extends ArrayAdapter<T> {
private Context context;
private List<T> values;

public SpinAdapter(Context context, int textViewResourceId, List<T> values) {
    super(context, textViewResourceId, values);
    this.context = context;
    this.values = values;
}

public int getCount() {
    return values.size();
}

public T getItem(int position) {
    return values.get(position);
}

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

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    TextView label = new TextView(context);
    label.setTextColor(Color.BLACK);
    label.setText(values.toArray(new Object[values.size()])[position]
            .toString());
    return label;
}

@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
    TextView label = new TextView(context);
    label.setTextColor(Color.BLACK);
    label.setText(values.toArray(new Object[values.size()])[position]
            .toString());

    return label;
}
}

Je pense aussi que l'on peut remplacer List par Array pour ne pas avoir à faire toArray dans List, mais j'avais une List ...... :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X