Как добавить плавающую этикетку на Spinner


88

После использования библиотеки поддержки дизайна Android TextInputLayoutдля размещения плавающей метки над EditTextкомпонентом мне стало интересно, есть ли способ добавить плавающую метку к Spinnerкомпоненту (не обязательно с использованием библиотеки дизайна).

Под этим я подразумеваю что-то вроде TextViewразмещенного над Spinner(очевидно, без анимации, подобной TextInputLayout), но я хочу, чтобы размер, шрифт и цвет текста соответствовали TextInputLayoutплавающей метке .

Например, это будет выглядеть примерно так (см. Метки над буквами Spinners):

введите описание изображения здесь

Как я упоминал ранее, моя основная цель - иметь метку над Spinner, так же, как и в TextInputLayout- чтобы размер текста, шрифт, цвет и расстояния между меткой и компонентом были такими же.

На странице Google Design, посвященной текстовым полям с плавающей меткой , есть диаграмма, показывающая размеры метки относительно компонента, но не указывается цвет или размер текста метки:

введите описание изображения здесь

Итак, чтобы подвести итог, я спрашиваю:
- Если есть специальный компонент для достижения того, о чем я прошу, или настраиваемое представление, которое я могу использовать, что это будет и как я могу его использовать.
- Если нет, то каков размер, цвет и шрифт текста плавающей метки, чтобы я мог разместить TextViewнад ним Spinnerс размерами макета, показанными на изображении выше.


РЕДАКТИРОВАТЬ:

Из руководящих принципов Google дизайна для текстовых полей , он имеет следующий для плавающих меток:

Подсказка и шрифт ввода: Roboto Regular 16sp
Шрифт ярлыка: Roboto Regular 12sp
Высота плитки: 72dp Отступ
сверху и снизу
текста : 16dp Отступ разделителя текстового поля: 8dp

а также изображения, показанные выше.

Итак, шрифт плавающей метки: Roboto Regular 12sp . Поэтому вы можете использовать a TextViewдля отображения Spinnerметки, так как я не знаю каких-либо пользовательских Views или специальных компонентов, которые вы могли бы использовать.

Однако после опробования он выглядит не так хорошо, как пример, показанный на изображении. Настраиваемое представление может быть лучше для этого , так как это может выглядеть лучше, но решение выше , является лишь одним из способов достижения чего - то близко к тому , что я изначально хотел.

Ответы:


44

Я хочу, чтобы размер, шрифт и цвет текста соответствовали TextInputLayoutплавающей метке .

Этого легко достичь без каких-либо внешних библиотек. После попытки взломать TextInputLayoutи даже создать свое собственное представление я понял, что использование простого TextViewтребует гораздо меньше кода и, вероятно, более эффективно.

Текстовый стиль можно скопировать из AppCompatбиблиотеки.

Стиль

Из рекомендаций по материальному дизайну мы получаем следующую информацию:

  • этикетка должна иметь нижнее поле 8dp
  • метка должна быть выровнена по вертикали с вводимым текстом

Вот что в руководстве не упоминается о Материале EditText:

  • у него есть левое дополнение 4dp
  • его метка на самом деле не имеет промежутка 16dpнад ней, это оставлено на усмотрение дизайнера интерфейса: это имеет смысл, потому что, если вы поместите его под другим EditText, вам понадобится только дополнительное 8dpпространство

Более того, Библиотека поддержки дизайна содержит этот стиль для подписи элемента с фокусом:

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

Неактивные элементы просто используют TextAppearance.AppCompat.Caption.

Реализация

Добавьте в свой dimens.xmlфайл следующее:

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

Затем добавьте это в 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>

Если вы хотите, чтобы метка всегда имела выделенный (акцентный) цвет, замените его TextAppearance.AppCompat.Captionна TextAppearance.Design.Hintиз библиотеки поддержки дизайна Google. Однако это, вероятно, будет выглядеть немного странно, если у вас также есть помеченные EditTextпредставления на том же экране.

Наконец, вы можете поместить TextViewнад своим Spinner(или любым другим элементом) с примененным стилем:

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

Результат

На следующем снимке экрана показан простой пример с двумя обычными TextInputLayoutпредставлениями, за которыми следуют метка и Spinner. Я не применял дополнительный 8dpинтервал для их дальнейшего разделения, но это показывает, что размер, шрифт и цвет отражены.

Элементы внутри Spinnerимеют другое заполнение, однако я предпочитаю сохранять вертикальное выравнивание со всеми другими метками, чтобы получить более однородный вид.

введите описание изображения здесь


1
Да, этого вполне возможно добиться без внешних библиотек. Фактически, я использовал тот же подход при создании своего пользовательского представления, которое на самом деле представляет собой составное представление, состоящее из стилизованного TextView, Spinner и Divider View. Причина, по которой я сделал настраиваемое представление, заключалась в том, что им легче управлять как одним представлением в иерархии макета, чем пытаться управлять двумя или тремя представлениями.
Фарбод Саламат-Заде,

3
Вероятно, вы захотите добавить <item name="android:textColor">?android:textColorHint</item>в свой стиль тот же цвет текста в настраиваемой метке, который используется в TextInputLayout в несфокусированном состоянии.
se.solovyev

«Однако это, вероятно, будет выглядеть немного странно, если вы также отметили представления EditText на том же экране». ... Вы можете просто указать стиль, SpinnerLabelа InputLabelне использовать его только для прядильщиков.
Робин Гуд

35

Я добился этого, используя AutoCompleteTextView, отключив клавиатуру и показав параметры на ощупь.

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

Это, безусловно, лучшее решение. Это небольшой прием, позволяющий использовать стандарт TextInputLayout, гарантируя, что у вас будет точный правильный внешний вид.
директоров

3
Просто небольшое улучшение: использовать R.layout.support_simple_spinner_dropdown_itemвместоandroid.R.layout.simple_spinner_item
директоров

Очень хитрый хак. Но это все же взлом.
Севастян Саванюк

4
Если вы используете mTextView.setText(...), вы не получите все значения от адаптера в раскрывающемся adapter.getFilter().filter(null);
списке ... При вызове

также, если вы установите текст, вы не сможете программно закрыть раскрывающийся
список

34

У меня есть смысл решить ту же проблему, что и у вас.

Проверьте это:

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

Теперь мне не нужны блесны. У вас по-прежнему будет эффект плавающей метки с включенной анимацией.


1
Что именно вы имеете в виду , говоря «Мне не нужны прядильщики» , и где в своей сути вы говорите: «Используется, чтобы сделать ваш EditText лучшим вариантом, чем прядильщики» ? Пользовательский интерфейс просто EditText?
Фарбод Саламат-Заде,

Я использую EditText с TextInput Layout в качестве альтернативы для счетчиков. Когда пользователь щелкает этот настраиваемый EditText, я открываю диалоговое окно с параметрами для выбора. Конечно, это могло бы быть лучше, и я принимаю любые улучшения, такие как отображение параметров с другими видами представления, такими как спиннер.
Родриго Энрикес,

1
Я обновлю эту суть до проекта на github с примерами, после чего надеюсь получить помощь в улучшении этого решения.
Родриго Энрикес,

5
Добавьте setInputType(InputType.TYPE_NULL);к onDraw(canvas)методу, чтобы отключить любой ввод в текст редактирования. В противном случае, например, длинные щелчки вызовут контекстное меню и позволят пользователю вставить текст.
Козерог

1
ну, это было потрясающе, но если вы когда-нибудь решили обновить его до библиотеки github, обязательно дайте возможность выбирать между EditTextили AppCompatEditText, пожалуйста!
Мухаммад Надери

11

Я создал составной Viewкомпонент, над которым отображается метка Spinner. Текст для метки можно задать с помощью XML или Java.

Компонент имеет ключевые особенности Spinner( не все из них), а также внешне похож на TextInputLayoutкомпонент.

Я назвал его a LabelledSpinner, и он доступен как часть моей библиотеки Android UsefulViews на GitHub под лицензией Apache 2.0 .

Чтобы использовать его, добавьте в build.gradleфайл зависимость библиотеки :

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

Примеры его использования доступны в репозитории GitHub (как образец приложения, так и руководство по использованию).


@LavekushAgrawal Дайте мне знать, что именно не работает, или открывайте вопрос по репо
Фарбод Саламат-Заде

1
На самом деле это работает, но лейбл не плавает, как edittext
Лавекуш Агравал,

@LavekushAgrawal Над символом расположена небольшая метка Spinner, которая похожа на то, как есть маленькие метки над EditTextкомпонентами, завернутые внутрь TextInputLayout. Как вы себе это представляли?
Фарбод Саламат-Заде,

@ FarbodSalamat-Zadeh Это классная библиотека для использования, но она не работает на Lollipop.
Хорхе

2
не работает на gradle 3.5, только очень старая библиотека gradle 2.0
Артур Мело


5

У меня есть альтернативное решение, которое использует поведение TextInputLayout и настраиваемый DialogFragment (AlertDialog) для имитации всплывающего диалогового окна счетчика.

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>

Создание настраиваемого счетчика через 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");
}

Результат

Когда вы нажимаете на счетчик в стиле TextInputLayout, он запускает диалоговое окно с предупреждением, содержащее ваш список выбора. Как только выбор будет выбран, EditText будет заполнен вашим выбором, и метка будет плавать так, как вы хотите.


Не android:focusable="false"делает ли это представление недоступным для тех, кто использует клавиатуру или голос для взаимодействия с приложением?
sargas

Я никогда не использовал клавиатуру или голос для взаимодействия с прядильщиком, но я установил причину, по которой android:focusable="false"пользователь не может ввести ввод, которого нет в выделенном списке. К сожалению, у меня нет физического телефона Android, чтобы проверить его поведение и сообщить вам об этом.
Кенни Ли

5

Вы можете добиться этого: введите описание изображения здесь

С новыми стилями библиотеки материалов, подобными этому:

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

для получения дополнительной информации: https://material.io/develop/android/components/menu/


это не счетчик
user924

2
это вертушка, только с другим дизайном
Амин Кешаварзян

3

Вот моя уловка,

хорошо то, что все будет работать так, как вы хотите,

но плохо то, что это увеличивает иерархию макета, и вам приходится обрабатывать функциональность в коде, и это уродливое решение:

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

и переопределить адаптер для счетчика, чтобы сделать выбранные значения прозрачными

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

и после выбора в счетчике просто возьмите выделенный текст и установите для него EditText, и он будет иметь тот же эффект с анимацией

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

        }
    });

если у вас есть вопросы Задайте мне


вы можете помочь мне застрять? У меня есть несколько редактируемых текстов, поэтому oncreate of layout focus переходит в первый edittext, и пока я нажимаю spinner, выше код edittext не получает фокус. но мне нужно сосредоточиться на щелчке spinner, как я могу?
sunita

@sunita, не могли бы вы рассказать мой порядок просмотра, потому что, когда я пытался, я смог сосредоточиться на прядильщике
Дамир Майлыбаев


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>
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.