69 votes

Le Spinner n'enroule pas le texte - est-ce un bug d'Android ?

Si le texte d'un Spinner est trop long pour tenir sur une seule ligne, le texte n'est pas enveloppé mais coupé. Il s'agit uniquement le cas pour Niveau API >= 11 . Voici des captures d'écran d'Android 4.2.2 (à gauche) qui montre le mauvais comportement et Android 2.3.3 (à droite) où il apparaît comme prévu.

android:singleLine="false" est simplement ignoré ici. Donc, comme tous les autres essais comme android:lines , android:minLines etc. Le site TextView semble en quelque sorte être beaucoup plus large que la largeur de la fenêtre.

J'ai vu d'autres personnes ayant le même problème, mais personne n'a pu trouver de solution. Alors, est-ce un bug du système ? Je ne pense pas que cette incohérence entre les versions de l'OS puisse être voulue.


Veuillez noter :

Certaines réponses suggéraient des solutions relativement simples.

  • Rédiger un texte personnalisé Adapter et de remplacer getView() ainsi que getDropDownView() . Ce n'est pas la solution, car à ce stade, le problème initial subsiste : à quoi doit ressembler la mise en page pour gérer correctement le retour à la ligne ?

  • Emballage de la TextView de la vue déroulante dans un parent ViewGroup . Ne fonctionne pas avec android:layout_width="match_parent" car la largeur du parent semble étrangement être illimitée.

  • Donner une largeur fixe à la liste déroulante. Cela n'est pas adapté aux différentes largeurs de l'affichage de la liste déroulante. Spinner peut avoir.

  • Et bien sûr, aucune solution ne consiste à insérer manuellement \n n'importe où dans le texte.


Reproduisez avec le code suivant :

UPDATE : J'ai également téléchargé ce projet en tant qu'exemple de projet sur GitHub : Télécharger

/res/values/arrays.xml :

<string-array name="items">
    <item>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.</item>
    <item>At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est.</item>
</string-array>

/res/layout/spinner_item.xml :

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    style="?android:attr/spinnerDropDownItemStyle"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ellipsize="none"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:singleLine="false" />

Set Adapter :

spinner.setAdapter(ArrayAdapter.createFromResource(this,
            R.array.items,
            R.layout.spinner_item));

1 votes

Vous devez créer une classe d'adaptateur personnalisée qui étend l'adaptateur de base, utiliser la méthode getDropDownView() et définir cet adaptateur sur le spinner, c'est tout.

2 votes

En regardant le code source d'Android, la version 2.3.3 de Spinner ( lien ) affiche un simple AlertDialog comme liste déroulante, alors que la version 4.1.1 ( lien ) affiche sa propre classe interne DropdownPopup (une sous-classe de ListPopupWindow). Une étude de cette différence est probablement un bon point de départ pour quiconque envisage de répondre à cette question.

0 votes

Essayez d'utiliser layout_weight=1, cela pourrait fonctionner

36voto

Kolchuga Points 171

Dans le thème holo, le spinner utilise par défaut le mode déroulant. Et toutes les actions visant à remplacer les styles par défaut consistent simplement à passer du mode spinner au mode dialogue, ce qui permet d'envelopper le texte multiligne comme dans l'API 11. Au lieu de cela, vous pouvez créer un spinner avec new Spinner(context, Spinner.MODE_DIALOG) ou en xml : android:spinnerMode="dialog" . Mais cela ne résout pas le problème, car il s'agit d'une boîte de dialogue et non d'une liste déroulante.

J'ai trouvé une autre solution à ce problème : Remplacer getDropDownView méthode dans ArrayAdapter et mettre setSingleLine(false) dans la méthode post de la vue. Ainsi, lorsque la vue est complètement créée, elle enveloppe le texte dans les lignes appropriées.

@Override
public View getDropDownView(final int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        convertView = new TextView(_context);
    }

    TextView item = (TextView) convertView;
    item.setText("asddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd");
    final TextView finalItem = item;
    item.post(new Runnable() {
        @Override
        public void run() {
            finalItem.setSingleLine(false);
        }
    });
    return item;
}

UPDATE :

Et voici une autre réponse.

Enveloppez manuellement le listview dans la PopupWindow et affichez-le sous le TextView au clic et cachez-le au clic sur le listItem.

Mise en œuvre simple juste pour montrer l'idée :

public class MySpinner extends TextView {
    private PopupWindow _p;
    private ListView _lv;
    public MySpinner(Context context) {
        super(context);
        init();
    }
    public MySpinner(Context context, AttributeSet attributeSet){
        super(context, attributeSet);
        init();
    }

    private void init(){
        setBackgroundResource(R.drawable.spinner_background);
        final List<String> list = new ArrayList<String>();
        list.add("Very long text AAAAAAAAAAAAAAAA");
        list.add("1 Very long text AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
        list.add("2 Very long text A");
        list.add("3 Very long text AAAAAAAAA");

        setMinimumWidth(100);
        setMaxWidth(200);

        _lv = new ListView(getContext());
        _lv.setAdapter(new ArrayAdapter<String>(getContext(), R.layout.simple_list_item_1, list));
        _lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                _p.dismiss();
                setText(list.get(i));
            }
        });

        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {

                _p = new PopupWindow(getContext());
                _p.setContentView(_lv);
                _p.setWidth(getWidth());
                _p.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
                _p.setTouchable(true);
                _p.setFocusable(true);
                _p.setOutsideTouchable(true);
                _p.showAsDropDown(view);
            }
        });
    }
}

0 votes

Bonne solution ! Le bug n'a toujours pas été corrigé dans Android 4.4 donc cette réponse est très utile.

0 votes

Il semble que l'invocation de la méthode setSingleLine oblige à recalculer la mise en page du dropDown du spinner après l'affichage des TextViews à l'écran. J'ai rejeté cette décision parce que lorsque vous faites défiler les éléments - la taille de la barre de défilement change dynamiquement, et l'élément de la liste déroulante clignote, en particulier sur l'émulateur. Il est assez simple de créer votre propre spinner avec PopupWindow et ListView - voir ma prochaine réponse.

0 votes

Wow, quelle façon minable de résoudre ce problème. Mais bon, ça marche très bien :)

12voto

cVoronin Points 71

J'ai résolu ce problème en adoptant un spinner de type dialogue :

<Spinner
  ...
android:spinnerMode="dialog" />

10 votes

Cela ne résout le problème de problème, ce évite problème

9voto

Mike Schall Points 2921

L'ajout d'un LinearLayout autour du TextView permet au texte de s'enrouler correctement.

Mise en page (common_domainreferencemodel_spinner_item.xml) :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:padding="4dp">

       <TextView
            android:id="@+id/nameTextView"
            android:singleLine="false"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

</LinearLayout>

Adaptateur :

public class DomainReferenceModelAdapter extends ArrayAdapter<DomainReferenceModel> {

    private List<DomainReferenceModel> objects;
    private LayoutInflater inflater;
    private int oddRowColor = Color.parseColor("#E7E3D1");
    private int evenRowColor = Color.parseColor("#F8F6E9");

    public DomainReferenceModelAdapter(Context context, int resource, List<DomainReferenceModel> objects) {
        super(context, resource, objects);
        this.objects = objects;
        this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    static class ViewHolder {
        public TextView nameTextView;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        return getViewInternal(position, convertView, parent, false);
    }

    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        return getViewInternal(position, convertView, parent, true);
    }

    private View getViewInternal(int position, View convertView, ViewGroup parent, boolean isDropdownView) {
        View view = convertView;
        if (view == null) {
            view = inflater.inflate(R.layout.common_domainreferencemodel_spinner_item, null);
            ViewHolder viewHolder = new ViewHolder();
            viewHolder.nameTextView = (TextView) view.findViewById(R.id.nameTextView);
            view.setTag(viewHolder);
        }
        if (isDropdownView) {
            view.setBackgroundColor(position % 2 == 0 ? evenRowColor : oddRowColor);
        }
        ViewHolder holder = (ViewHolder) view.getTag();
        DomainReferenceModel model = objects.get(position);
        holder.nameTextView.setText(model.getName());
        return view;
    }

}

8voto

Gunnar Karlsson Points 15071

Réaliser des éléments de liste déroulante à plusieurs lignes sur un site Web Spinner tout en utilisant un Holo n'est pas possible, d'après ce que j'ai essayé.

Une solution de contournement consiste à :

  • créer un style pour le Spinner qui n'hérite pas de Holo . Cela permettra d'activer les éléments déroulants à plusieurs lignes.
  • Donnez un style " manuel " au Spinner pour qu'il ait l'air d'être Holo -à thème.

Ceci produit (montrant les états fermés et ouverts) :

enter image description here

Détails de la mise en œuvre :

Il n'y a aucun moyen d'hériter d'un Holo sur le Spinner et afficher plusieurs lignes dans le Spinner pour autant que je puisse dire, même si nous définissons le paramètre de l'élément déroulant TextView singleLine à l'attribut false et fournir une mise en page personnalisée. J'ai également essayé de conserver le Holo mais en modifiant le

android:spinnerStyle
android:spinnerItemStyle 
android:spinnerDropDownItemStyle 

attributs de style (exemple d'utilisation de ces attributs) aquí ) mais je n'ai pas réussi à lui faire produire un résultat multi-lignes.

Toutefois, si l'on modifie le style de l'élément Spinner et n'héritent pas spinnerStyle de Holo :

 <style name="AppTheme" parent="android:Theme.Holo.Light">
    <item name="android:spinnerStyle">@style/spinnerStyle</item>
</style>

<--no parent attribute-->
 <style name="spinnerStyle">
    <item name="android:clickable">true</item>
</style>

alors l'élément déroulant permettra d'afficher plusieurs lignes. Mais maintenant, nous avons perdu le Holo sur le Spinner et l'état fermé ressemble à un TextView pas un Spinner sans flèche ou indice visuel, c'est un Spinner . Si, au lieu de cela, nous définissons spinnerStyle parent à : parent="android:style/Widget.Spinner :

<style name="spinnerStyle" parent="android:style/Widget.Spinner">
    <item name="android:clickable">true</item>
</style>

le site Spinner l'état fermé montrera la flèche mais sera stylisé comme le gris pré-Holo Spinner qui ne semble pas à sa place dans un Holo app.

Une solution possible est alors :

  • Remplacer le thème pour spinnerStyle et n'utilisez pas Holo pour les parents. Cela permettra d'activer le texte à plusieurs lignes dans les éléments de la liste déroulante.
  • Changez le Spinner pour donner l'impression qu'il hérite de l'arrière-plan Holo thème.

Voici un exemple :

Créez une activité de base :

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Spinner spinner = (Spinner)findViewById(R.id.styled_spinner);

    spinner.setAdapter(ArrayAdapter.createFromResource(this,
            R.array.items,
            R.layout.spinner_item));        
}

Disposition des activités :

<LinearLayout 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"
    android:padding="50dip"
    tools:context=".MainActivity" >

    <Spinner
        android:id="@+id/styled_spinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

les styles :

<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <style name="AppTheme" parent="android:Theme.Holo.Light">
        <item name="android:spinnerStyle">@style/spinnerStyle</item>
    </style>
    <style name="spinnerStyle">
        <item name="android:clickable">true</item>
        <item name="android:background">@drawable/spinner_background_holo_light</item>
    </style>
</resources>

dans le dossier drawable, placez spinner_background_holo_light :

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="false"
        android:drawable="@drawable/spinner_disabled_holo_light" />
    <item android:state_pressed="true"
        android:drawable="@drawable/spinner_pressed_holo_light" />
    <item android:state_pressed="false" android:state_focused="true"
        android:drawable="@drawable/spinner_focused_holo_light" />
    <item android:drawable="@drawable/spinner_default_holo_light" />
</selector>

et inclure ces éléments dans votre drawables-hdpi dossier :

enter image description here spinner_default_holo_light.9.png

enter image description here spinner_disabled_holo_light.9.png

enter image description here spinner_focused_holo_light.9.png

enter image description here spinner_pressed_holo_light.9.png

Cela produit un spinner avec Holo L'état fermé et les éléments multi-lignes, comme le montrent les captures d'écran ci-dessus.

Les éléments de la liste déroulante dans cet exemple ne sont pas Holo -mais c'est peut-être un compromis acceptable si l'affichage sur plusieurs lignes des éléments de la liste déroulante est vraiment important.

Dans cet exemple, android:minSdkVersion a été fixé à 14 y android:targetSdkVersion a 17 dans Manifest.

Holo graphiques et le spinner_background_holo_light.xml proviennent de HoloEverywhere Copyright (c) 2012 Christophe Versieux, Sergey Shatunov. Voir le projet github lié pour les détails de la licence.

0 votes

Ces informations ont été très utiles, mais qu'y a-t-il exactement dans le fichier spinner_item.xml ? style="@style/spinnerStyle" dans mon spinner_item.xml checkedtextview ? J'ai besoin de demander parce que lorsque je clique sur l'élément, rien ne se passe, il se met en évidence mais il n'y a pas de liste d'éléments qui s'affiche.

0 votes

@CQM C'est un TextView dont la largeur est réglée sur fill_parent et la hauteur sur wrap_content.

0 votes

Merci, où est-ce que c'est fixé à un objet ? style="@style/spinnerStyle"

6voto

girlOnSledge Points 18

J'ai été confronté au même problème. Je veux voir 2 lignes dans la liste déroulante du spinner, mais toutes les solutions que j'ai trouvées me semblent déraisonnables pour résoudre un problème aussi simple. J'enquête Code source du Spinner et j'ai trouvé que si on utilise .xml personnalisé avec l'attribut Android:singleLine="false".

<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/multiline_spinner_text_view"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:singleLine="false" />

et par défaut ArrayAdapter, le code suivant exécuté dans ListPopupWindow chaque fois

    @Override
       View More obtainView(int position, boolean[] isScrap) {
            View view = super.obtainView(position, isScrap);

           if (view instanceof TextView) {
                ((TextView) view).setHorizontallyScrolling(true);
            }

            return view;        
}

et c'est pourquoi nous ne voyons qu'une seule ligne de chaîne par ligne de liste, c'est en fait un défilement.

Pour résoudre ce problème, notre point de vue devrait être le suivant pas d'instance de TextView, il suffit de placer votre TextView dans FrameLayout ou LinearLayout.

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

        <CheckedTextView
            android:id="@+id/multiline_spinner_text_view"
            android:layout_width="fill_parent"
            android:layout_height="?android:attr/listPreferredItemHeight"
            android:singleLine="false" />

    </LinearLayout>

et

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, 
               R.layout.multiline_spinner_dropdown_item,R.id.multiline_spinner_text_view,
                awasomeListValues);

Cette solution fonctionne dans les deux modes de rotation : MODE_DROPDOWN et MODE_DROPDOWN. J'espère que cela vous aidera !

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