Jak dodać pływającą etykietę na Spinner


88

Po użyciu biblioteki obsługi projektów systemu Android w TextInputLayoutcelu umieszczenia pływającej etykiety nad EditTextkomponentem, zastanawiałem się, czy istnieje sposób na dodanie pływającej etykiety do Spinnerkomponentu (niekoniecznie przy użyciu biblioteki projektu).

Rozumiem przez to coś w rodzaju a TextViewumieszczonego powyżej Spinner(oczywiście bez animacji takich jak TextInputLayout), ale chcę, aby rozmiar tekstu, czcionka i kolor były zgodne z TextInputLayoutpływającą etykietą .

Na przykład wyglądałoby to mniej więcej tak (zobacz etykiety nad Spinnerznakami):

wprowadź opis obrazu tutaj

Jak wspomniałem wcześniej, moim głównym celem jest umieszczenie etykiety powyżej Spinner, tak jak w TextInputLayout- aby rozmiar tekstu, czcionka, kolor i odległości między etykietą a komponentem były takie same.

Na stronie Google Design dotyczącej pływających pól tekstowych etykiet znajduje się diagram przedstawiający wymiary etykiety w stosunku do komponentu, ale nie ma wskazania koloru ani rozmiaru tekstu etykiety:

wprowadź opis obrazu tutaj

Podsumowując, pytam:
- Jeśli istnieje specjalny komponent do osiągnięcia tego, o co proszę, lub niestandardowy widok, którego mogę użyć, co by to było i jak mogę go użyć.
- Jeśli nie, jaki jest rozmiar, kolor i czcionka pływającego tekstu etykiety, abym mógł umieścić go TextViewnad moim Spinnerz wymiarami układu pokazanymi na powyższym obrazku.


EDYTOWAĆ:

Ze wskazówek Google dotyczących projektowania pól tekstowych wynika, że ​​dla etykiet pływających są następujące:

Wskazówka i czcionka: Roboto Regular 16 sp
Czcionka etykiety: Roboto Regular 12 sp
Wysokość kafelka: 72 dp Wypełnienie
tekstu na górze i na dole: 16 dp Dopełnienie
pola tekstowego : 8 dp

jak również na zdjęciach pokazanych powyżej.

Tak więc czcionka pływającej etykiety to: Roboto Regular 12sp . Możesz zatem użyć a TextViewdo wyświetlenia Spinneretykiety, ponieważ nie znam żadnych niestandardowych Viewani specjalnych komponentów, których mógłbyś użyć.

Jednak po wypróbowaniu nie wygląda tak dobrze, jak przykład pokazany na obrazku. Niestandardowy widok może być lepiej dla tego , jak mógłby wyglądać ładniej, ale rozwiązanie jest tylko jednym ze sposobów osiągnięcia czegoś blisko tego, co pierwotnie chciał.

Odpowiedzi:


44

Chcę, aby rozmiar, czcionka i kolor tekstu były zgodne z TextInputLayoutpływającą etykietą .

Można to łatwo osiągnąć bez żadnych zewnętrznych bibliotek. Po próbie zhakowania, TextInputLayouta nawet stworzenia własnego widoku niestandardowego, zdałem sobie sprawę, że użycie prostego TextViewzajmuje znacznie mniej kodu i prawdopodobnie jest bardziej wydajne.

Styl tekstu można skopiować z AppCompatbiblioteki.

Styl

Z wytycznych Material Design otrzymujemy następujące informacje:

  • etykieta powinna mieć dolny margines 8dp
  • etykieta powinna być wyrównana w pionie z tekstem wejściowym

Oto, czego wytyczne nie wspominają o materiale EditText:

  • ma lewe wypełnienie 4dp
  • jego etykieta faktycznie nie ma żadnych odstępów 16dpnad nią, pozostawia to projektantowi interfejsu: ma to sens, ponieważ jeśli umieścisz ją pod inną EditText, będziesz potrzebować tylko dodatkowej 8dpprzestrzeni

Ponadto biblioteka Design Support Library zawiera następujący styl dla etykiety elementu, na którym skupia się uwaga:

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

Nieaktywne elementy po prostu używają TextAppearance.AppCompat.Caption.

Realizacja

Dodaj do swojego dimens.xmlpliku:

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

Następnie dodaj to do 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>

Jeśli chcesz, aby etykieta zawsze miała wyróżniony (akcent) kolor, zastąp go na TextAppearance.AppCompat.Captionz TextAppearance.Design.HintBiblioteki Google Design Support. Jednak prawdopodobnie będzie to wyglądać nieco dziwnie, jeśli masz również oznaczone EditTextwidoki na tym samym ekranie.

Na koniec możesz umieścić TextViewnad swoim Spinner(lub dowolnym innym elementem) z zastosowanym stylem:

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

Wynik

Poniższy zrzut ekranu przedstawia prosty przykład z dwoma normalnymi TextInputLayoutwidokami, po których następuje etykieta i plik Spinner. Nie zastosowałem dodatkowych 8dpodstępów, aby je bardziej rozdzielić, ale to pokazuje, że rozmiar, czcionka i kolor są odzwierciedlone.

Elementy wewnątrz Spinnermają inne wypełnienie, jednak wolę zachować wyrównanie w pionie ze wszystkimi innymi etykietami, aby uzyskać bardziej jednolity wygląd.

wprowadź opis obrazu tutaj


1
Tak, jest to jak najbardziej możliwe bez zewnętrznych bibliotek. W rzeczywistości zastosowałem to samo podejście podczas tworzenia widoku niestandardowego, który w rzeczywistości jest widokiem złożonym składającym się z stylizowanego widoku TextView, Spinner i widoku dzielnika. Powodem, dla którego utworzyłem widok niestandardowy, było to, że łatwiej jest nim zarządzać jako jednym widokiem w hierarchii układu, zamiast próbować zarządzać dwoma lub trzema widokami.
Farbod Salamat-Zadeh

3
Prawdopodobnie chcesz dodać <item name="android:textColor">?android:textColorHint</item>swój styl, aby uzyskać ten sam kolor tekstu w etykiecie niestandardowej, jaki jest używany w TextInputLayout w stanie niezogniskowanym.
se.solovyev

„Jednak będzie to prawdopodobnie wyglądać nieco dziwnie, jeśli na tym samym ekranie oznaczyłeś również widoki EditText”. ... Możesz po prostu być konkretny i nazwać styl SpinnerLabelzamiast InputLabel, aby używać ich tylko dla spinnerów.
Robin Hood

35

Osiągnąłem to, używając AutoCompleteTextView, wyłączając klawiaturę i pokazując opcje dotykowe.

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

To zdecydowanie najlepsze rozwiązanie. Jest to mały hack i pozwala na użycie standardu TextInputLayout, zapewniając w ten sposób dokładny poprawny wygląd.
BoD

3
Tylko mała poprawa: używaj R.layout.support_simple_spinner_dropdown_itemzamiastandroid.R.layout.simple_spinner_item
BoD

Bardzo sprytny hack. Ale to wciąż hack.
Sevastyan Savanyuk

4
Jeśli użyjesz mTextView.setText(...), nie otrzymasz wszystkich wartości z adaptera w menu rozwijanym ... Wywołanie adapter.getFilter().filter(null);pokazuje ponownie wszystkie sugestie
mmrmartin

również jeśli ustawisz tekst, nie możesz programowo zamknąć listy rozwijanej
Mike6679

34

Mam sedno rozwiązania tego samego problemu, co ty.

Sprawdź to:

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

Teraz nie potrzebuję błystek. Nadal będziesz mieć efekt pływającej etykiety z dołączonymi animacjami.


1
Co dokładnie masz na myśli, mówiąc „nie potrzebuję spinnerów” i gdzie w swojej istocie mówisz „Używasz, aby Twój EditText był lepszą opcją niż Spinners” ? Czy interfejs użytkownika to po prostu EditText?
Farbod Salamat-Zadeh

Używam EditText z TextInput Layout jako alternatywy dla spinnerów. Kiedy użytkownik kliknie ten niestandardowy tekst edycji, otwieram okno dialogowe z opcjami do wyboru. Oczywiście mogłoby być lepiej i akceptuję każdą poprawę, taką jak pokazywanie opcji z innymi widokami, tak jak spinner.
Rodrigo Henriques

1
Zaktualizuję to streszczenie do projektu github z przykładami, a następnie mam nadzieję, że uzyskam pomoc w ulepszeniu tego rozwiązania.
Rodrigo Henriques

5
Dodaj setInputType(InputType.TYPE_NULL);do onDraw(canvas)metody, aby wyłączyć wszelkie dane wejściowe do pliku edittext. W przeciwnym razie, np. Długie kliknięcia, wywołają menu kontekstowe i pozwolą użytkownikowi wkleić tekst.
Koziorożec

1
cóż, to było niesamowite, ale jeśli kiedykolwiek zdecydowałeś się uaktualnić ją do biblioteki github, upewnij się, że masz możliwość wyboru między EditTextlub AppCompatEditText, proszę!
Muhammad Naderi

11

Utworzyłem Viewkomponent złożony , który wyświetla etykietę powyżej Spinner. Tekst etykiety można ustawić za pomocą XML lub Java.

Komponent ma kluczowe cechy Spinner( nie wszystkie ), a także wygląda podobnie do TextInputLayoutkomponentu.

I nazwali go do LabelledSpinner, i jest dostępny jako część mojego UsefulViews biblioteki Androida na GitHub pod Apache 2.0 License .

Aby z niego skorzystać, dodaj zależność biblioteki w swoim build.gradlepliku:

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

Przykłady jego użycia są dostępne w repozytorium GitHub (zarówno przykładowa aplikacja, jak i przewodnik użytkowania).


@LavekushAgrawal Daj mi znać, co dokładnie nie działa, lub otwórz problem w repozytorium
Farbod Salamat-Zadeh

1
Właściwie to działa, ale etykieta nie jest płynna jak edittext
Lavekush Agrawal

@LavekushAgrawal Powyżej znajduje się mała etykieta Spinner, która jest podobna do małych etykiet umieszczonych nad EditTextkomponentami TextInputLayout. Jak sobie to wyobrażałeś?
Farbod Salamat-Zadeh

@ FarbodSalamat-Zadeh To fajna biblioteka, ale nie działa na Lollipop.
Jorge

2
nie działa na gradle 3.5, tylko bardzo stara biblioteka gradle 2.0
Arthur Melo


5

Mam alternatywne rozwiązanie, które wykorzystuje zachowanie TextInputLayout i niestandardowy DialogFragment (AlertDialog) do emulacji wyskakującego okna dialogowego pokrętła.

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>

Utwórz niestandardowy spinner za pomocą 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");
}

Wynik

Kliknięcie elementu TextInputLayout w stylu pokrętła spowoduje wyświetlenie okna dialogowego z ostrzeżeniem zawierającego listę wyborów. Po wybraniu zaznaczenia EditText zostanie wypełniony zaznaczeniem, a etykieta będzie pływać tak, jak chcesz.


Czy android:focusable="false"ten widok nie jest niedostępny dla osób używających klawiatury lub głosu do interakcji z aplikacją?
sargas

Nigdy nie używałem klawiatury ani głosu do interakcji z przędzarką, ale powodem, dla którego ustawiłem, android:focusable="false"jest to, że użytkownik nie może wpisać wejścia, którego nie ma wśród zaznaczenia. Niestety, nie mam fizycznego telefonu z Androidem, aby przetestować zachowania i poinformować Cię o tym.
Kenny Li

5

Możesz to osiągnąć: wprowadź opis obrazu tutaj

Dzięki nowym stylom biblioteki materiałów, takim jak ten:

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

więcej informacji: https://material.io/develop/android/components/menu/


to nie jest spinner
user924

1
jest to błystka, tylko z innym wzorem
Amin Keshavarzian

3

Oto moja sztuczka

dobre jest to, że wszystko będzie działać tak, jak chcesz,

ale złe jest to, że zwiększa hierarchię układu i musisz obsługiwać funkcjonalność w kodzie, a to jest brzydkie rozwiązanie:

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

i nadpisać adapter dla pokrętła, aby wybrane wartości stały się przezroczyste

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

a po wybraniu w przędzarce po prostu pobierz zaznaczony tekst i ustaw go na EditText i będzie to miało ten sam efekt z animacją

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

        }
    });

jeśli masz jakieś pytanie, zapytaj mnie


możesz mi pomóc, że utknąłem? Mam wiele tekstów do edycji, więc po utworzeniu fokusu na układ przechodzi do pierwszego edittext i podczas gdy bezpośrednio naciskam pokrętło, więc powyższy kod edittext nie jest fokusowany. ale muszę skupić się na kliknięciu pokrętła, jak to zrobić?
sunita

@sunita, czy możesz mi podać kolejność widoku, ponieważ kiedy próbuję, mogłem skupić się na przędzarce
Damir Mailybayev


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>
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.