web-dev-qa-db-fra.com

Spinner n'encapsule pas le texte - est-ce un bug Android?

Si le texte d'un élément Spinner est trop long pour tenir sur une seule ligne, le texte n'est pas encapsulé mais coupé. C'est seulement le cas pour niveau API> = 11 . Voici des captures d'écran de Android 4.2.2 (gauche) qui montre le mauvais comportement et Android 2.3.3 (à droite) où il se présente comme prévu.

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

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


Notez s'il vous plaît:

Certaines réponses suggèrent des solutions relativement simples.

  • Ecrire un Adapter personnalisé et remplacer getView() ainsi que getDropDownView(). Ce n'est pas la solution ici, car à ce stade, il y a toujours le problème d'origine: à quoi la disposition doit-elle ressembler pour gérer le retour à la ligne approprié?

  • Envelopper le 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 illimitée.

  • Donner la vue déroulante une largeur fixe. Cela ne convient pas aux différentes largeurs que Spinner peut avoir.

  • Et bien sûr, aucune solution n'est d'insérer manuellement \n N'importe où dans le texte.


Reproduisez avec le code suivant:

MISE À JOUR: J'ai également téléchargé ceci comme exemple de projet sur GitHub : Download

/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" />

Définissez Adapter:

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

Dans le thème holo, le spinner utilise par défaut le mode déroulant. Et tous les mouvements avec des styles par défaut remplaçants se déplacent simplement pour passer du mode spinner au mode dialogue qui encapsule avec succès 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 c'est une boîte de dialogue, pas une liste déroulante.

J'ai trouvé une autre solution à ce problème: remplacer la méthode getDropDownView dans ArrayAdapter et mettre setSingleLine(false) dans la méthode de visualisation post. Ainsi, lorsque la vue est complètement créée, elle enveloppe le texte sur 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;
}

MISE À JOUR:

Et voici une autre réponse.

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

Implémentation 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);
            }
        });
    }
}
32
Kolchuga

Seule une combinaison de solutions a fonctionné pour moi (testée sur Android 5.1 aussi):

spinner_item.xml

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

  <TextView
    Android:id="@Android:id/text1"
    style="?android:attr/spinnerItemStyle"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:singleLine="false"
    Android:textAlignment="inherit"/>
</LinearLayout>

code

  final ArrayAdapter<String> spinnerArrayAdapter=new ArrayAdapter<String>(activity,R.layout.spinner_item,Android.R.id.text1,spinnerItemsList)
  {
  @Override
  public View getDropDownView(final int position,final View convertView,final ViewGroup parent)
    {
    final View v=super.getDropDownView(position,convertView,parent);
    v.post(new Runnable()
    {
    @Override
    public void run()
      {
      ((TextView)v.findViewById(Android.R.id.text1)).setSingleLine(false);
      }
    });
    return v;
    }
  };
  spinnerArrayAdapter.setDropDownViewResource(Android.R.layout.simple_spinner_dropdown_item);
13
android developer

J'ai résolu ce problème en passant au spinner de style boîte de dialogue:

<Spinner
  ...
Android:spinnerMode="dialog" />
13
cVoronin

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

Présentation (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;
    }

}
7
Mike Schall

Il n'est pas possible d'obtenir des éléments déroulants sur plusieurs lignes sur un Spinner tout en utilisant un thème Holo, 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 activera les éléments déroulants sur plusieurs lignes.
  • Stylisez le Spinner "manuellement" pour qu'il ressemble à son thème Holo.

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

enter image description here

Détails de mise en œuvre:

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

Android:spinnerStyle
Android:spinnerItemStyle 
Android:spinnerDropDownItemStyle 

styles attributs (exemple d'utilisation de ces attributs ici ) mais je n'ai pas pu faire en sorte qu'il produise un résultat multi-lignes.

Cependant, si nous remplaçons le style de Spinner et n'héritons pas de 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>

l'élément déroulant prendra en charge l'affichage de plusieurs lignes. Mais maintenant, nous avons perdu le thème Holo sur le Spinner et l'état fermé ressemble à un TextView pas à un Spinner sans flèche ni indice visuel c'est un Spinner. Si nous définissons plutôt spinnerStyle parent sur: parent="Android:style/Widget.Spinner:

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

l'état Spinner fermé affichera la flèche mais sera stylisé comme le pré-Holo gris Spinner qui semble déplacé dans une application Holo.

Donc, une solution possible est alors:

  • Remplacez le thème pour spinnerStyle et n'utilisez pas Holo pour parent. Cela activera le texte sur plusieurs lignes dans les éléments DropDown.
  • Modifiez l'arrière-plan Spinner pour lui donner l'impression qu'il hérite du thème Holo.

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 de l'activité:

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

modes:

<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 dessinable, 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 incluez ces drawables 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 des éléments fermés à thème Holo et multi-lignes, comme indiqué dans les captures d'écran ci-dessus.

Les éléments déroulants de cet exemple ne sont pas sur le thème Holo mais c'est peut-être un compromis acceptable si l'affichage sur plusieurs lignes des éléments déroulants est vraiment important.

Dans cet exemple, Android:minSdkVersion a été défini sur 14 et Android:targetSdkVersion à 17 dans le manifeste.

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

6
Gunnar Karlsson

Je pense qu'il y a un bug sur Android. Vous pouvez essayer ça. Supprimez les espaces du texte, puis affichez que cela fonctionnerait bien. Si la longueur de la vue de texte est <celle de la chaîne, elle ignore tous les caractères après l'espace. Pour contourner ce problème, vous pouvez essayer ceci:

ajoutez un fichier au dossier res/layout nommé multiline_spinner_dropdown_item.xml avec l'exemple de code:

<CheckedTextView xmlns:Android="http://schemas.Android.com/apk/res/Android"
style="?android:attr/spinnerDropDownItemStyle"
Android:singleLine="false"
Android:layout_width="match_parent"
Android:layout_height="?android:attr/listPreferredItemHeight"
Android:ellipsize="Marquee" />

et lorsque vous créez le spinner, créez-le à partir de cette disposition.

Quelque chose comme :

ArrayAdapter.createFromResource(this, items, R.layout.multiline_spinner_dropdown_item);

Fondamentalement, copiez la disposition Android.R.layout.simple_spinner_dropdown_item dans le projet et modifiez la disposition en définissant l'attribut singleLine sur false dans CheckedTextView.

3
lokoko

J'ai fait face au même problème. Je veux voir 2 lignes dans la liste déroulante de spinner, mais toutes les solutions que j'ai trouvées me semblent déraisonnables pour résoudre un problème aussi simple. J'étudie code source Spinner et j'ai trouvé Si nous utilisons .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, 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 ligne de chaîne par ligne de liste, elle défile en fait.

Pour résoudre ce problème, notre vue doit être pas une 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 spinner: MODE_DROPDOWN et MODE_DROPDOWN.J'espère qu'elle vous aidera!

3
girlOnSledge

En lisant cette réponse: erreur de conversion textview: - Android.widget.LinearLayout ne peut pas être converti en Android.widget.TextView et ce sujet, je pourrais résoudre ce problème: nous avons besoin d'un LinearLayout encapsulant TextView (le spinner texte) pour éviter que le texte sorte de l'écran, mais nous aurons des problèmes à résoudre. Pour commencer, créez la mise en page ( Je l'ai appelée spinner_dd_item.xml ):

    <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="wrap_content"
        Android:orientation="vertical">

        <TextView
            Android:id="@+id/simple_spinner_dropdown"
            Android:layout_width="match_parent"
            Android:layout_height="match_parent"
            Android:paddingBottom="5dp"
            Android:paddingLeft="10dp"
            Android:paddingRight="10dp"
            Android:paddingTop="5dp"
            Android:textColor="@color/colorAccent"
            tools:text="Hello" />
    </LinearLayout>

L'étape suivante consiste à créer une instance ArrayAdapter pour la définir sur le spinner:

    ArrayAdapter<CharSequence> arrayAdapter = new ArrayAdapter<CharSequence>(getActivity(), R.layout.spinner_dd_item,
            R.id.simple_spinner_dropdown, hashmapToString(hashMap, keys)) {
        @Override
        public View getDropDownView(int position, View convertView, ViewGroup parent) {
            return getView(position, convertView, parent);
        }
    };
    spinner.setAdapter(arrayAdapter);

N'oubliez pas d'ajouter le nom de la présentation et l'ID TextView sur ArrayAdapter car nous avons ajouté LinearLayout et nous devons spécifier TextView et remplacer getDropDownView pour obtenir la vue qui affiche les données à la position spécifiée dans l'ensemble de données. Maintenant, nous pouvons voir que le spinner fonctionne bien sur les versions plus récentes et plus anciennes Android versions

1
Jaco

je viens de trouver que Android a un style existant pour ce cas

final Spinner pelanggaran = (Spinner) findViewById(R.id.pelanggaran);
ArrayAdapter<CharSequence> pelanggaran_adapter = ArrayAdapter.createFromResource(this,R.array.pelanggaran_array, Android.R.layout.simple_expandable_list_item_1);
pelanggaran_adapter.setDropDownViewResource(Android.R.layout.simple_expandable_list_item_1);
pelanggaran.setAdapter(pelanggaran_adapter);

j'espère que cela vous a résolu le problème.

1
Fajar Rukmo

Enveloppez simplement TextView avec LinearLayout:

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
              Android:layout_width="match_parent"
              Android:layout_height="wrap_content">
    <TextView
            Android:id="@Android:id/text1"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"/>
</LinearLayout>
0
mixel
ArrayAdapter<?> specAdapter =
   ArrayAdapter.createFromResource(
       getActivity().getBaseContext(),
       aa[position],
       Android.R.layout.select_dialog_item);
specAdapter.setDropDownViewResource(Android.R.layout.select_dialog_item);
0
user5723352

J'ai eu ce même problème et j'ai trouvé une solution.

Je voulais que le texte apparaisse dans l'affichage initial et dans la liste déroulante également.

Le texte était enveloppé dans l'affichage initial et pour la liste déroulante, j'avais trouvé une autre solution recommandant l'utilisation d'une vue personnalisée d'une mise en page linéaire avec une largeur fixe entourant une vue de texte. Cela a rendu la liste déroulante du spinner correcte, ainsi que celle de la sélection initiale. Cependant, cela a causé un problème majeur sur les appareils plus anciens.

L'affichage initial ne s'actualisait pas et le texte prenait un aspect empilé si j'essayais de sélectionner autre chose. et comme il s'est empilé vers le bas, l'ajout d'un arrière-plan n'a pas aidé. enter image description here

Comme il s'avère que les adaptateurs ont une méthode appelée setDropDownViewResource () qui vous permet de définir une vue différente pour la liste déroulante que celle affichée dans la sélection initiale du spinner.

 import org.holoeverywhere.widget.Spinner;

 ArrayAdapter adapter1 = ArrayAdapter.createFromResource(this,R.array.array_of_strings,R.layout.simple_list_item_1);
 adapter1.setDropDownViewResource(R.layout.my_simple_list_item_1);
 spQ1.setAdapter(adapter1);

dans cet exemple, simple_list_item est la vue par défaut fournie par Android et my_simple_list_item est

<LinearLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android" 
Android:layout_width="300dp"
Android:layout_height="wrap_content" >   

<TextView
  Android:id="@+id/Android:text1"
  Android:layout_width="wrap_content"
  Android:layout_height="50dp"
  Android:ellipsize="Marquee"
  Android:layout_gravity="center_vertical"
  Android:singleLine="false"/>

</LinearLayout> 

Maintenant, le texte passe à l'intérieur de la vue déroulante du spinner ET dans la sélection affichée des spinners.

0
Michael Alan Huff

Voici ce que j'ai fait pour que cela fonctionne:

ArrayAdapter<KeyValue> adapter = new ArrayAdapter<>(getContext(), R.layout.simple_dropdown_item_multiline, R.id.nameTextView, choices);

Et voici le contenu de "simple_dropdown_item_multiline":

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
          Android:layout_width="match_parent"
          Android:layout_height="wrap_content">

<TextView Android:id="@+id/nameTextView"
          style="?android:attr/dropDownItemStyle"
          xmlns:Android="http://schemas.Android.com/apk/res/Android"
          Android:layout_width="match_parent"
          Android:layout_height="wrap_content"
          Android:ellipsize="Marquee"
          Android:paddingBottom="@dimen/large"
          Android:paddingTop="@dimen/large"
          Android:singleLine="false"
          Android:textAppearance="?android:attr/textAppearanceLargePopupMenu"/>
0
Marco C.