Comment ajouter une étiquette flottante sur Spinner

87

Après avoir utilisé la bibliothèque de support de conception Android TextInputLayoutpour placer une étiquette flottante au-dessus d'un EditTextcomposant, je me demandais s'il existe un moyen d'ajouter une étiquette flottante au Spinnercomposant (pas nécessairement en utilisant la bibliothèque de conception).

Par cela, je veux dire quelque chose comme un TextViewplacé au-dessus du Spinner(évidemment pas d'animations comme le TextInputLayout), mais je veux que la tailleTextInputLayout du texte, la police et la couleur correspondent à celles de l' étiquette flottante du .

Par exemple, cela ressemblerait à quelque chose comme ceci (voir les étiquettes au-dessus des Spinners):

entrez la description de l'image ici

Comme je l'ai mentionné précédemment, mon objectif principal est d'avoir une étiquette au-dessus du Spinner, tout comme dans le TextInputLayout- donc la taille du texte, la police, la couleur et les distances entre l'étiquette et le composant seraient les mêmes.

Sur la page Google Design concernant les champs de texte d'étiquette flottante , il y a un diagramme montrant les dimensions de l'étiquette par rapport au composant, mais il n'y a aucune indication sur la couleur ou la taille du texte de l'étiquette:

entrez la description de l'image ici

Donc, pour résumer, je demande:
- S'il y a un composant spécial pour réaliser ce que je demande ou une vue personnalisée que je peux utiliser, quel serait-il et comment puis-je l'utiliser.
- Sinon, quelle est la taille, la couleur et la police du texte de l'étiquette flottante, afin que je puisse placer un TextViewau-dessus de mon Spinneravec les dimensions de mise en page indiquées dans l'image ci-dessus.


ÉDITER:

D'après les directives Google Design pour les champs de texte , il contient les éléments suivants pour les libellés flottants:

Astuce et police d'entrée: Roboto Regular 16sp
Police d'étiquette: Roboto Regular 12sp
Hauteur de la tuile: 72dp Rembourrage du
haut et du bas du
texte: 16dp Rembourrage du séparateur de champ de texte: 8dp

ainsi que les images ci-dessus.

La police d'étiquette flottante est donc: Roboto Regular 12sp . Vous pouvez donc utiliser a TextViewpour afficher l' Spinnerétiquette car je ne connais pas de Viewcomposants personnalisés ou spéciaux que vous pourriez utiliser.

Cependant , après l'avoir essayé, il n'a pas l'air aussi bon que l'exemple montré dans l'image. Une vue personnalisée peut être meilleure pour cela , car elle pourrait paraître plus jolie, mais la solution ci-dessus n'est qu'une façon d'obtenir quelque chose de proche de ce que je voulais à l'origine.

Farbod Salamat-Zadeh
la source

Réponses:

44

Je veux que la taille, la police et la couleurTextInputLayout du texte correspondent à celles de l 'étiquette flottante .

Cela peut être réalisé facilement sans aucune bibliothèque externe. Après avoir essayé de pirater TextInputLayoutet même de créer ma propre vue personnalisée, j'ai réalisé qu'en utilisant un simpleTextView prend beaucoup moins de code et que c'est probablement plus efficace.

Le style de texte peut être copié à partir duAppCompat bibliothèque.

Le style

Les directives de conception des matériaux nous permettent d'obtenir les informations suivantes:

  • l'étiquette doit avoir une marge inférieure de 8dp
  • l'étiquette doit être alignée verticalement avec le texte d'entrée

Voici ce que les directives ne mentionnent pas à propos du matériel EditText:

  • il a un rembourrage gauche de 4dp
  • son étiquette n'a en fait pas d'espacement au- 16dpdessus, c'est laissé au concepteur de l'interface: cela a du sens car si vous le placez sous un autre EditText, vous n'aurez besoin que d'un8dp espace

De plus, la bibliothèque de support de conception contient ce style pour l'étiquette d'un élément ciblé:

<style name="TextAppearance.Design.Hint" parent="TextAppearance.AppCompat.Caption">
    <item name="android:textColor">?attr/colorControlActivated</item>
</style>

Les éléments inactifs utilisent simplement TextAppearance.AppCompat.Caption .

la mise en oeuvre

Ajoutez ce qui suit à votre dimens.xmlfichier:

<dimen name="input_label_vertical_spacing">8dp</dimen>
<dimen name="input_label_horizontal_spacing">4dp</dimen>

Ajoutez ensuite ceci à styles.xml:

<style name="InputLabel" parent="TextAppearance.AppCompat.Caption">
    <item name="android:paddingBottom">@dimen/input_label_vertical_spacing</item>
    <item name="android:paddingLeft">@dimen/input_label_horizontal_spacing</item>
    <item name="android:paddingRight">@dimen/input_label_horizontal_spacing</item>
</style>

Si vous souhaitez que l'étiquette ait toujours la couleur en surbrillance (accent), remplacez-la TextAppearance.AppCompat.Captionpar à TextAppearance.Design.Hintpartir de la bibliothèque d'assistance Google Design. Cependant, cela semblera probablement un peu étrange si vous avez également des EditTextvues étiquetées sur le même écran.

Enfin, vous pouvez mettre un TextViewau - dessus de votre Spinner(ou de tout autre élément) avec le style appliqué:

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/category"
    style="@style/InputLabel" />

Résultat

La capture d'écran suivante montre un exemple simple avec deux TextInputLayoutvues normales suivies d'une étiquette et d'un fichier Spinner. Je n'ai pas appliqué les8dp espacement pour les séparer davantage, mais cela vous montre que la taille, la police et la couleur sont reflétées.

Les éléments à l'intérieur Spinneront un rembourrage différent, mais je préfère garder l'alignement vertical avec toutes les autres étiquettes pour obtenir un aspect plus uniforme.

entrez la description de l'image ici

Andrea Lazzarotto
la source
1
Oui, il est parfaitement possible d'y parvenir sans bibliothèques externes. En fait, j'ai utilisé la même approche lors de la création de ma vue personnalisée, qui est en fait une vue composée composée de la vue stylisée TextView, Spinner et divider. La raison pour laquelle j'ai créé la vue personnalisée est qu'il est plus facile de la gérer comme une vue dans la hiérarchie de mise en page, au lieu d'essayer de gérer deux ou trois vues.
Farbod Salamat-Zadeh
3
Vous souhaitez probablement ajouter <item name="android:textColor">?android:textColorHint</item>dans votre style pour obtenir la même couleur de texte dans l'étiquette personnalisée que celle utilisée dans TextInputLayout dans un état non focalisé.
se.solovyev
"Cependant, cela semblera probablement un peu étrange si vous avez également étiqueté les vues EditText sur le même écran." ... Vous pouvez simplement être précis et nommer le style SpinnerLabelau lieu de InputLabelafin de ne les utiliser que pour les fileurs.
Robin Hood
34

J'ai une idée essentielle pour résoudre le même problème que vous.

Vérifiez-le:

https://gist.github.com/rodrigohenriques/77398a81b5d01ac71c3b

Maintenant, je n'ai plus besoin de fileurs. Vous aurez toujours un effet d'étiquette flottante avec des animations incluses.

Rodrigo Henriques
la source
1
Qu'entendez-vous exactement par "Je n'ai pas besoin de spinners" , et où dans votre sens, vous dites "Utilisé pour faire de votre EditText une meilleure option que Spinners" ? L'interface utilisateur est-elle simplement un EditText?
Farbod Salamat-Zadeh
J'utilise EditText avec TextInput Layout comme alternative pour les fileurs. Lorsque l'utilisateur clique dans ce EditText personnalisé, j'ouvre une boîte de dialogue avec des options à sélectionner. Bien sûr, cela pourrait être mieux et j'accepte toute amélioration, comme montrer les options avec d'autres types de vues, tout comme le spinner.
Rodrigo Henriques
1
Je vais mettre à niveau cet essentiel dans un projet github avec des exemples, puis j'espère obtenir de l'aide pour améliorer cette solution.
Rodrigo Henriques
5
Ajoutez setInputType(InputType.TYPE_NULL);à la onDraw(canvas)méthode pour désactiver toute entrée dans l'edittext. Sinon, par exemple, de longs clics feront apparaître un menu contextuel et permettront à l'utilisateur de coller du texte.
Capricorne
1
eh bien c'était génial, mais si jamais vous avez décidé de le mettre à niveau dans une bibliothèque github, assurez-vous de donner une option entre EditTextou AppCompatEditText, s'il vous plaît!
Muhammad Naderi
33

J'ai réalisé cela en utilisant un AutoCompleteTextView, en désactivant le clavier et en affichant les options au toucher.

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, getResources().getStringArray(R.array.locations));

AutoCompleteTextView mTextView = (AutoCompleteTextView) findViewById(R.id.location);

mTextView.setAdapter(adapter);
mTextView.setKeyListener(null);
mTextView.setOnTouchListener(new View.OnTouchListener(){
    @Override
    public boolean onTouch(View v, MotionEvent event){
        ((AutoCompleteTextView) v).showDropDown();
        return false;
    }
});
Sidney Veiga
la source
C'est de loin la meilleure solution. Ceci est un petit hack, et permet d'utiliser le standard TextInputLayout, vous assurant ainsi d'avoir exactement l'apparence correcte.
BoD du
3
Juste une petite amélioration: utiliser à la R.layout.support_simple_spinner_dropdown_itemplace deandroid.R.layout.simple_spinner_item
BoD
Hack très intelligent. Mais c'est toujours un hack.
Sevastyan Savanyuk
3
Si vous utilisez mTextView.setText(...), vous ne recevrez pas toutes les valeurs de l'adaptateur dans la liste déroulante ... L'appel adapter.getFilter().filter(null);affiche à nouveau toutes les suggestions
mmrmartin
également si vous définissez du texte, vous ne pouvez pas fermer la liste déroulante par programme
Mike6679
11

J'ai créé un Viewcomposant composé qui affiche une étiquette au-dessus du Spinner. Le texte de l'étiquette peut être défini à l'aide de XML ou en Java.

Le composant présente les principales caractéristiques d'un Spinner( pas tous ) et ressemble au TextInputLayoutcomposant.

Je l'ai nommé a LabelledSpinner, et il est disponible dans le cadre de ma bibliothèque Android UsefulViews sur GitHub sous la licence Apache 2.0 .

Pour l'utiliser, ajoutez la dépendance de bibliothèque dans votre build.gradlefichier:

compile 'com.satsuware.lib:usefulviews:+'

Des exemples de son utilisation sont disponibles dans le référentiel GitHub (à la fois un exemple d'application et un guide d'utilisation).

Farbod Salamat-Zadeh
la source
@LavekushAgrawal Faites-moi savoir ce qui ne fonctionne pas exactement ou ouvrez un problème sur le repo
Farbod Salamat-Zadeh
1
En fait, ça marche, mais l'étiquette n'est pas flottante comme edittext
Lavekush Agrawal
@LavekushAgrawal Il y a une petite étiquette positionnée au-dessus du Spinner, qui est similaire à la façon dont il y a de petites étiquettes au-dessus des EditTextcomposants, enveloppées dans TextInputLayout. Comment l'avez-vous imaginé?
Farbod Salamat-Zadeh
@ FarbodSalamat-Zadeh C'est une bibliothèque sympa à utiliser, mais elle ne fonctionne pas sur Lollipop.
Jorge
2
ne fonctionne pas sur gradle 3.5, seulement gradle 2.0 très ancienne bibliothèque
Arthur Melo
5

J'ai une solution alternative qui utilise le comportement de TextInputLayout et un DialogFragment personnalisé (AlertDialog) pour émuler une fenêtre contextuelle de dialogue spinner.

layout.xml:

<android.support.design.widget.TextInputLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <EditText
        android:id="@+id/your_et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/your_label"
        android:maxLines="1"
        android:inputType="textNoSuggestions"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        android:focusable="false"
        style="@style/Base.Widget.AppCompat.Spinner.Underlined"/>
</android.support.design.widget.TextInputLayout>

Créer un spinner personnalisé via DialogFragment (AlertDialog)

SpinnerFragment.java:

public class SpinnerFragment extends DialogFragment {

private static final String TITLEID = "titleId";
private static final String LISTID = "listId";
private static final String EDITTEXTID = "editTextId";

public static SpinnerFragment newInstance(int titleId, int listId, int editTextId) {
    Bundle bundle = new Bundle();
    bundle.putInt(TITLEID, titleId);
    bundle.putInt(LISTID, listId);
    bundle.putInt(EDITTEXTID, editTextId);
    SpinnerFragment spinnerFragment = new SpinnerFragment();
    spinnerFragment.setArguments(bundle);

    return spinnerFragment;
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    final int titleId = getArguments().getInt(TITLEID);
    final int listId = getArguments().getInt(LISTID);
    final int editTextId = getArguments().getInt(EDITTEXTID);
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

    try {
        final String[] items = getResources().getStringArray(listId);

        builder.setTitle(titleId)
                .setItems(listId, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int pos) {
                        EditText et = (EditText) getActivity().findViewById(editTextId);
                        String selectedText = items[pos];
                        if (!TextUtils.isEmpty(selectedText)) {
                            et.setText(selectedText);
                        } else {
                            et.getText().clear();
                        }
                    }
                });

    } catch (NullPointerException e) {
        Log.e(getClass().toString(), "Failed to select option in " + getActivity().toString() + " as there are no references for passed in resource Ids in Bundle", e);
        Toast.makeText(getActivity(), getString(R.string.error_failed_to_select), Toast.LENGTH_LONG).show();
    }

    return builder.create();
}

}

Activity.java:

private void addCustomSpinner() {
    EditText yourEt = (EditText) findViewById(R.id.your_et);
    yourEt.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            showCustomSpinnerDialog(view);
        }
    });
}

private void showCustomSpinnerDialog(View v) {
    int titleId = R.string.your_label;
    int listId = R.array.spinner_selections;
    int editTextId = R.id.your_et;
    SpinnerFragment spinnerFragment = SpinnerFragment.newInstance(titleId, listId, editTextId);
    spinnerFragment.show(getFragmentManager(), "customSpinner");
}

Résultat

Lorsque vous cliquez sur le spinner stylisé TextInputLayout, il déclenchera une boîte de dialogue d'alerte contenant votre liste de sélections. Une fois qu'une sélection est choisie, le EditText sera rempli avec votre sélection et l'étiquette flottera comme vous le souhaitez.

Kenny Li
la source
Cela ne android:focusable="false"rend- il pas cette vue inaccessible à ceux qui utilisent le clavier ou la voix pour interagir avec l'application?
sargas
Je n'ai jamais utilisé le clavier ou la voix pour interagir avec un spinner, mais la raison pour laquelle je l'ai défini android:focusable="false"est que l'utilisateur ne peut pas taper une entrée qui ne fait pas partie de la sélection. Malheureusement, je n'ai pas de téléphone Android physique pour tester les comportements et vous en informer.
Kenny Li
4

Voici mon truc,

la bonne chose est que tout fonctionnera comme vous le souhaitez,

mais le problème est que cela augmente la hiérarchie de la mise en page, et vous devez gérer les fonctionnalités dans le code, et c'est une solution moche:

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.design.widget.TextInputLayout
            android:id="@+id/til"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <EditText
                android:id="@+id/edt"
                android:layout_width="match_parent"
                android:layout_height="@dimen/edt_height"
                android:hint="@string/create_gcc_visa_txt_step" />

        </android.support.design.widget.TextInputLayout>

        <Spinner
            android:id="@+id/spn"
            style="@style/MyAppTheme.Base.Spinner"
            android:layout_height="@dimen/edt_height"
            android:layout_alignBottom="@id/til" />

    </RelativeLayout>

et remplacer l'adaptateur pour le spinner, pour rendre les valeurs sélectionnées transparentes

public class MySpinnerAdapter extends SimpleAdapter {
    Context mContext;

    public MySpinnerAdapter(Context context, List<String> data, int resource, String[] from, int[] to) {
        super(context, data, resource, from, to);
        mContext = context;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        convertView = super.getView(position, convertView, parent);

        TextView tv = (TextView) convertView.findViewById(android.R.id.text1);
            tv.setTextColor(ContextCompat.getColor(mContext, R.color.transparent));
        return convertView;
    }
}

et après avoir sélectionné dans le spinner, obtenez simplement le texte sélectionné et réglez-le sur EditText et il aura le même effet avec l'animation

yourSpinnerView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<String> adapterView, View view, int i, long l) {
            //get your selected text from adapter or from where you want 
            String selectedText = adapterView.getItemAtPosition(i));

            if (i != 0) { 
                edt.setText(selectedText);
            } else { 
                // if in case your spinner have first empty text, 
                // then when spinner selected, just empty EditText.
                edt.setText("");
            }
        }

        @Override
        public void onNothingSelected(AdapterView<?> adapterView) {

        }
    });

si vous avez des questions posez-moi

Damir Mailybayev
la source
pouvez-vous m'aider à rester coincé? J'ai plusieurs textes d'édition donc oncreate of layout focus va au premier edittext et pendant que j'appuie directement sur le spinner donc le code ci-dessus de edittext ne se concentre pas. Mais je dois me concentrer sur le clic du spinner comment puis-je?
sunita
@sunita pouvez-vous s'il vous plaît me dire l'ordre de la vue, car lorsque j'essaye, j'ai pu me concentrer sur le spinner
Damir Mailybayev
2

Vous pouvez y parvenir: entrez la description de l'image ici

Avec de nouveaux styles de bibliothèque de matériaux comme celui-ci:

<com.google.android.material.textfield.TextInputLayout
        android:id="@+id/fullNameLay"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

    <androidx.appcompat.widget.AppCompatAutoCompleteTextView
            android:id="@+id/fullNameEt"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>

pour plus d'informations: https://material.io/develop/android/components/menu/

Amin Keshavarzian
la source
c'est pas spinner
user924
c'est un spinner, seulement avec un design différent
Amin Keshavarzian
0

SpinnerCustom.java

package com.pozitron.tfkb.customviews;

import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.Nullable;
import android.text.SpannableString;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;

import com.pozitron.commons.customviews.TextViewFont;
import com.pozitron.tfkb.R;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 * Created by so12607 on 31/01/2018.
 */

public class SpinnerCustom extends LinearLayout {

    @BindView(R.id.layoutSpinnerCustomLabel)
    TextViewFont layoutSpinnerCustomLabel;

    @BindView(R.id.layoutSpinnerCustomSpinner)
    TextViewFont layoutSpinnerCustomSpinner;

    @BindView(R.id.layoutSpinner)
    LinearLayout layoutSpinner;

    private View v;

    public SpinnerCustom(Context context) {
        this(context, null);
    }

    public SpinnerCustom(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);

    }

    public SpinnerCustom(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        v = LayoutInflater.from(context).inflate(R.layout.layout_spinner_custom, this, true);
        ButterKnife.bind(this);

        if (!isInEditMode()) {

            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SpinnerCustom, 0, 0);
            final String label = array.getString(R.styleable.SpinnerCustom_label);
            final boolean enable = array.getBoolean(R.styleable.SpinnerCustom_enabled, true);
            layoutSpinnerCustomLabel.setText(label);

            layoutSpinnerCustomLabel.setEnabled(enable);
            layoutSpinnerCustomSpinner.setEnabled(enable);
            layoutSpinner.setEnabled(enable);
            layoutSpinner.setClickable(enable);
            v.setEnabled(enable);
            v.setClickable(enable);
            array.recycle();
        }
    }

    public void setText(String text) {
        layoutSpinnerCustomSpinner.setText(text);
    }

    public void setText(SpannableString text) {
        layoutSpinnerCustomSpinner.setText(text);
    }

    public void setText(CharSequence text) {
        layoutSpinnerCustomSpinner.setText(text);
    }

    public void setLabel(String text) {
        layoutSpinnerCustomLabel.setText(text);
    }

    public void setError(SpannableString text) {
        layoutSpinnerCustomSpinner.setError(text);
    }

    public void setEnabled(boolean enable) {
        layoutSpinnerCustomLabel.setEnabled(enable);
        layoutSpinnerCustomSpinner.setEnabled(enable);
        layoutSpinner.setEnabled(!enable);
        layoutSpinner.setClickable(!enable);
    }
}

layout_spinner_custom.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/layoutSpinner"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <com.pozitron.commons.customviews.TextViewFont
        android:id="@+id/layoutSpinnerCustomLabel"
        style="@style/TextLabel"
        tools:text="label" />

    <com.pozitron.commons.customviews.TextViewFont
        android:id="@+id/layoutSpinnerCustomSpinner"
        style="@style/SpinnerText"
        android:clickable="false" />

</LinearLayout>

style.xml

<style name="TextLabel" parent="android:Widget.TextView">
    <item name="font">@integer/font_GTEestiDisplay_Regular</item>
    <item name="android:layout_width">match_parent</item>
    <item name="android:textSize">14sp</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:gravity">bottom</item>
    <item name="android:textColor">@color/greyLabel</item>
</style>

<style name="SpinnerText" parent="EditText">
    <item name="font">@integer/font_GTEestiDisplay_Medium</item>
    <item name="android:gravity">bottom</item>
    <item name="android:textSize">17sp</item>
    <item name="android:minHeight">35dp</item>
    <item name="android:focusable">false</item>
    <item name="android:background">@drawable/spinner_selector</item>
    <item name="android:text">@string/select</item>
    <item name="android:textColor">@color/selector_spinner_text</item>
</style>
Samet ÖZTOPRAK
la source