Como adicionar etiqueta flutuante no Spinner

87

Depois de usar a Android Design Support Library TextInputLayoutpara colocar um rótulo flutuante acima de um EditTextcomponente, eu queria saber se há uma maneira de adicionar um rótulo flutuante ao Spinnercomponente (não necessariamente usando a Design Library).

Com isso, quero dizer algo como um TextViewcolocado acima de Spinner(obviamente não há animações como o TextInputLayout), mas quero que o tamanhoTextInputLayout do texto, a fonte e a cor correspondam aos do rótulo flutuante de .

Por exemplo, seria algo assim (veja os rótulos acima dos Spinners):

insira a descrição da imagem aqui

Como mencionei antes, meu principal objetivo é ter um rótulo acima do Spinner, assim como no TextInputLayout- então o tamanho do texto, a fonte, a cor e as distâncias entre o rótulo e o componente seriam os mesmos.

Na página de Design do Google sobre campos de texto de rótulo flutuante , há um diagrama que mostra as dimensões do rótulo em relação ao componente, mas não há indicação da cor ou tamanho do texto do rótulo:

insira a descrição da imagem aqui

Portanto, para resumir, estou perguntando:
- Se existe um componente especial para alcançar o que estou pedindo ou uma visualização personalizada que posso usar, o que seria e como posso usá-lo.
- Se não, qual é o tamanho, cor e fonte do texto do rótulo flutuante, para que eu possa colocar um TextViewacima do meu Spinnercom as dimensões do layout mostradas na imagem acima.


EDITAR:

A partir das diretrizes de design do Google para campos de texto , tem o seguinte para rótulos flutuantes:

Dica e fonte de entrada: Roboto Regular 16sp
Fonte do rótulo: Roboto Regular 12sp
Altura do bloco: 72dp Preenchimento
superior e inferior do
texto : 16dp Preenchimento do divisor do campo de texto: 8dp

bem como as imagens mostradas acima.

Portanto, a fonte do rótulo flutuante é: Roboto Regular 12sp . Portanto, você pode usar um TextViewpara exibir o Spinnerrótulo, pois não conheço nenhum Views personalizado ou componente especial que você possa usar.

No entanto , depois de experimentá-lo, ele não parece tão bom quanto o exemplo mostrado na imagem. Uma visualização personalizada pode ser melhor para isso , pois poderia parecer mais agradável, mas a solução acima é apenas uma maneira de conseguir algo próximo ao que eu queria originalmente.

Farbod Salamat-Zadeh
fonte

Respostas:

44

Quero que o tamanho, a fonte e a corTextInputLayout do texto correspondam aos do rótulo flutuante do .

Isso pode ser alcançado facilmente sem quaisquer bibliotecas externas. Depois de tentar hackear o TextInputLayoute até mesmo fazer minha própria visualização customizada, percebi que usar um simples TextViewrequer muito menos código e provavelmente é mais eficiente.

O estilo do texto pode ser copiado da AppCompatbiblioteca.

O estilo

A partir das diretrizes do Material Design, obtemos as seguintes informações:

  • o rótulo deve ter uma margem inferior de 8dp
  • o rótulo deve ser alinhado verticalmente com o texto de entrada

Aqui está o que as diretrizes não mencionam sobre o material EditText:

  • tem um preenchimento esquerdo de 4dp
  • seu rótulo na verdade não tem nenhum espaçamento 16dpacima dele, isso é deixado para o designer da interface: isso faz sentido porque se você colocá-lo sob outro EditText, você só precisará de um 8dpespaço adicional

Além disso, a Design Support Library contém este estilo para o rótulo de um elemento em foco:

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

Elementos inativos simplesmente usam TextAppearance.AppCompat.Caption.

Implementação

Adicione o seguinte ao seu dimens.xmlarquivo:

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

Em seguida, adicione isso a 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>

Se você deseja que o rótulo sempre tenha a cor destacada (destaque), substitua TextAppearance.AppCompat.Captionpor TextAppearance.Design.Hintda Biblioteca de Suporte ao Design do Google. No entanto, isso provavelmente parecerá um pouco estranho se você também rotular EditTextvisualizações na mesma tela.

Por fim, você pode colocar um TextViewacima de seu Spinner(ou qualquer outro elemento) com o estilo aplicado:

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

Resultado

A captura de tela a seguir mostra um exemplo simples com duas TextInputLayoutvisualizações normais seguidas por um rótulo e um Spinner. Não apliquei o 8dpespaçamento adicional para separá-los ainda mais, mas isso mostra que o tamanho, a fonte e a cor são refletidos.

Os elementos dentro de Spinnertêm um preenchimento diferente, porém prefiro manter o alinhamento vertical com todos os outros rótulos para obter uma aparência mais uniforme.

insira a descrição da imagem aqui

Andrea Lazzarotto
fonte
1
Sim, é perfeitamente possível conseguir isso sem bibliotecas externas. Na verdade, usei a mesma abordagem ao criar minha visualização personalizada, que na verdade é uma visualização composta que consiste em TextView, Spinner e Divider View com estilo. A razão pela qual fiz a visualização customizada foi porque ela é mais fácil de gerenciar como uma visualização na hierarquia de layout, em vez de tentar gerenciar duas ou três visualizações.
Farbod Salamat-Zadeh
3
Você provavelmente deseja adicionar <item name="android:textColor">?android:textColorHint</item>seu estilo para obter a mesma cor de texto no rótulo personalizado usado em TextInputLayout no estado desfocado.
se.solovyev
"No entanto, isso provavelmente parecerá um pouco estranho se você também rotulou visualizações de Editar Texto na mesma tela." ... Você pode apenas ser específico e nomear o estilo em SpinnerLabelvez de InputLabelusá-lo apenas para spinners.
Robin Hood,
34

Eu tenho uma ideia feita por mim mesmo para resolver o mesmo problema que você tem.

Confira:

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

Agora eu não preciso de spinners. Você ainda terá efeito de rótulo flutuante com animações incluídas.

Rodrigo Henriques
fonte
1
O que exatamente você quer dizer com "Eu não preciso de spinners" e onde, em sua essência, você diz "Costumava fazer seu EditText uma opção melhor do que Spinners" ? A IU é simplesmente um EditText?
Farbod Salamat-Zadeh
Eu uso EditText com TextInput Layout como uma alternativa para spinners. Quando o usuário clica neste EditText personalizado, eu abro uma caixa de diálogo com opções para selecionar. Claro que poderia ser melhor e eu aceito qualquer melhoria como mostrar as opções com outros tipos de visualizações como o spinner.
Rodrigo Henriques
1
Vou atualizar esta essência em um projeto github com exemplos, então espero obter ajuda para melhorar esta solução.
Rodrigo Henriques
5
Adicione setInputType(InputType.TYPE_NULL);ao onDraw(canvas)método para desativar qualquer entrada no texto de edição. Caso contrário, por exemplo, cliques longos abrirão um menu de contexto e permitirão ao usuário colar o texto.
Capricórnio
1
bem, isso foi incrível, mas se você alguma vez decidiu atualizá-lo para uma biblioteca do github, certifique-se de dar a opção de escolher entre EditTextou AppCompatEditText, por favor!
Muhammad Naderi
33

Consegui isso usando um AutoCompleteTextView, desabilitando o teclado e mostrando as opções de toque.

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
fonte
Esta é de longe a melhor solução. Este é um pequeno hack, e permite usar o padrão TextInputLayout, garantindo assim que você terá a aparência correta exata.
BoD
3
Apenas uma pequena melhoria: use em R.layout.support_simple_spinner_dropdown_itemvez deandroid.R.layout.simple_spinner_item
BoD
Hack muito inteligente. Mas ainda é um hack.
Sevastyan Savanyuk de
3
Se você usar mTextView.setText(...), não receberá todos os valores do adaptador no menu suspenso ... A chamada adapter.getFilter().filter(null);mostra novamente todas as sugestões
mmrmartin
Além disso, se você definir o texto, não poderá fechar a lista suspensa programaticamente
Mike6679
11

Criei um Viewcomponente composto que exibe um rótulo acima de Spinner. O texto do rótulo pode ser definido usando XML ou em Java.

O componente possui os principais recursos de um Spinner( não todos ), além de ser semelhante ao TextInputLayoutcomponente.

Eu o chamei deLabelledSpinner e ele está disponível como parte da minha biblioteca UsefulViews Android no GitHub sob a licença Apache 2.0 .

Para usá-lo, adicione a dependência da biblioteca em seu build.gradlearquivo:

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

Exemplos de seu uso estão disponíveis no repositório GitHub (um aplicativo de amostra e um guia de uso).

Farbod Salamat-Zadeh
fonte
@LavekushAgrawal Deixe-me saber o que exatamente não funciona ou abre um problema no repo
Farbod Salamat-Zadeh
1
Na verdade está funcionando, mas o rótulo não é flutuante como o texto da edição
Lavekush Agrawal
@LavekushAgrawal Há uma pequena etiqueta posicionada acima do Spinner, que é semelhante a como existem pequenas etiquetas acima dos EditTextcomponentes, embrulhados TextInputLayout. Como você imaginou que seria?
Farbod Salamat-Zadeh de
@ FarbodSalamat-Zadeh Esta é uma biblioteca legal de usar, mas não funciona no Lollipop.
Jorge
2
não está funcionando no gradle 3.5, apenas a biblioteca muito antiga do gradle 2.0
Arthur Melo
5

Eu tenho uma solução alternativa que usa o comportamento de TextInputLayout e um DialogFragment personalizado (AlertDialog) para emular um pop-up de diálogo giratório.

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>

Crie spinner personalizado 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");
}

Resultado

Quando você clica no TextInputLayout com estilo giratório, ele aciona uma caixa de diálogo de alerta contendo sua lista de seleções. Assim que uma seleção for escolhida, o EditText será preenchido com sua seleção e o rótulo irá flutuar como você deseja.

Kenny Li
fonte
Isso não android:focusable="false"torna essa visualização inacessível para quem usa teclado ou voz para interagir com o aplicativo?
sargas
Nunca usei teclado ou voz para interagir com um botão giratório, mas a razão pela qual eu configurei android:focusable="false"é para que o usuário não possa digitar uma entrada que não esteja entre a seleção. Infelizmente, não tenho um telefone físico Android para testar os comportamentos para que você saiba.
Kenny Li
4

Aqui está o meu truque,

o bom é que tudo funcionará como você deseja,

mas o ruim é que isso está aumentando a hierarquia de layout e você tem que lidar com a funcionalidade no código, e é uma solução feia:

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

e substituir o adaptador para girador, para tornar os valores selecionados 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;
    }
}

e depois de selecionar no botão giratório, apenas pegue o texto selecionado e configure-o para EditText e ele terá o mesmo efeito com animação

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

        }
    });

se você tem alguma dúvida me pergunte

Damir Mailybayev
fonte
você pode me ajudar estou ficando preso? Eu tenho texto de edição múltipla, então o foco do layout vai para o primeiro editar texto e enquanto pressiono o botão giratório, acima do código de texto de edição, não consigo o foco. mas eu preciso obter o foco no clique do botão giratório, como posso?
sunita de
@sunita você pode me dizer a ordem da visão, porque quando eu estava tentando, consegui me concentrar no spinner
Damir Mailybayev
2

Você pode conseguir isso: insira a descrição da imagem aqui

Com novos estilos de biblioteca de materiais como este:

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

para mais informações: https://material.io/develop/android/components/menu/

Amin Keshavarzian
fonte
não é giratório
user924
é um spinner, apenas com um design diferente
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
fonte