How to add floating label on Spinner
Asked Answered
I

10

90

After using the Android Design Support Library's TextInputLayout to place a floating label above an EditText component, I was wondering if there is a way to add a floating label to the Spinner component (not necessarily using the Design Library).

By this, I mean something like a TextView placed above the Spinner (obviously no animations like the TextInputLayout), but I want the text size, font and colour to match that of the TextInputLayout's floating label.

For example, it would look something like this (see the labels above the Spinners):

enter image description here

As I mentioned before, my main aim is to have a label above the Spinner, just as in the TextInputLayout - so text size, font, colour, and distances between the label and the component would be the same.

On the Google Design page about floating label text fields, there is a diagram showing dimensions of the label relative to the component, but there is no indication of the label text colour or size:

enter image description here

So, to summarise, I am asking:
- If there is a special component to achieve what I am asking or a custom view I can use, what would it be, and how can I use it.
- If not, what is the floating label text size, colour and font, so that I can place a TextView above my Spinner with the layout dimensions shown in the above image.


EDIT:

From the Google Design guidelines for text fields, it has the following for floating labels:

Hint and input font: Roboto Regular 16sp
Label font: Roboto Regular 12sp
Tile height: 72dp
Text top and bottom padding: 16dp
Text field divider padding: 8dp

as well as the images shown above.

So the floating label font is: Roboto Regular 12sp. You can therefore use a TextView to display the Spinner label as I do not know of any custom Views or special components you could use.

However, after trying it out, it does not look quite as good as the example shown in the image. A custom view may be better for this, as it could look nicer, but the solution above is just one way of achieving something close to what I originally wanted.

Implode answered 25/7, 2015 at 10:44 Comment(0)
F
45

I want the text size, font and colour to match that of the TextInputLayout's floating label.

This can be achieved easily without any external libraries. After trying to hack the TextInputLayout and even making my own custom view, I realized that using a simple TextView takes much less code and it's probably more efficient.

The text style can be copied from the AppCompat library.

The style

From the Material Design guidelines we get the following information:

  • the label should have a bottom margin of 8dp
  • the label should be vertically aligned with the input text

Here's what the guidelines do not mention about the Material EditText:

  • it has a left padding of 4dp
  • its label actually doesn't have any spacing of 16dp above it, this is left to the interface designer: this makes sense because if you place it under another EditText, you will only need an additional 8dp of space

Moreover, the Design Support Library contains this style for the label of a focused element:

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

Inactive elements simply use TextAppearance.AppCompat.Caption.

Implementation

Add the following to your dimens.xml file:

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

Then add this to 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>

If you want the label to always have the highlighted (accent) color, replace TextAppearance.AppCompat.Caption with TextAppearance.Design.Hint from the Google Design Support Library. However, this will probably look a bit weird if you also have labeled EditText views on the same screen.

Finally, you can put a TextView above your Spinner (or any other element) with the style applied:

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

Result

The following screenshot shows a simple example with two normal TextInputLayout views followed by a label and a Spinner. I didn't apply the additional 8dp spacing to separate them further, but this shows you that the size, font and color are reflected.

The elements inside the Spinner have a different padding, however I prefer to keep the vertical alignment with all the other labels to get a more uniform look.

enter image description here

Formenti answered 12/8, 2016 at 11:34 Comment(4)
Yes, it is perfectly possible to achieve this without external libraries. In fact, I used the same approach when creating my custom view, which is actually a compound view consisting of the styled TextView, Spinner and divider View. The reason I made the custom view was because it is easier to manage as one view in the layout hierarchy, instead of trying to manage two or three views.Implode
You probably want to add <item name="android:textColor">?android:textColorHint</item> in your style to get the same text color in the custom label as used in TextInputLayout in unfocused state.Babbling
"However, this will probably look a bit weird if you also have labeled EditText views on the same screen."... You can just be specific and name the style SpinnerLabel instead of InputLabel so that you only use them for spinners.Alpers
"The elements inside the Spinner have a different padding, however I prefer to keep the vertical alignment with all the other labels to get a more uniform look." If you'd nevertheless like to apply that same left padding to the label so that it aligns with the spinner elements, the value is 8dp, as defined here.Calotte
L
36

I achieved this by using an AutoCompleteTextView, disabling the keyboard and showing the options on touch.

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;
    }
});
Lenna answered 6/6, 2016 at 11:10 Comment(8)
This is by far the best solution. This is a small hack, and allows to use the standard TextInputLayout, thus ensuring you will have the exact correct appearance.Obscurity
Just a small improvement: use R.layout.support_simple_spinner_dropdown_item instead of android.R.layout.simple_spinner_itemObscurity
Very clever hack. But it is still a hack though.Eatmon
If you use mTextView.setText(...), you will not receive all values from adapter in drop down... Calling adapter.getFilter().filter(null); shows again all suggestionsKumler
also if you set text, you then cannot close the dropdown programmaticallyK2
If you call setKeyListener(null) then physical keyboards will not be able to trigger the autocomplete, so this solution is incomplete as written.Wendellwendi
Sadly this breaks most UI testing tools as AutoCompleteTextView does an odd pop-up that can't be seen as part of the screen.Chimney
for showing all suggestion if (!mTextView.getText().toString().equals("")) adapter.getFilter().filter(null) should be called inside the onTouch Listener before showDropDown https://mcmap.net/q/246155/-autocompletetextview-force-to-show-all-itemsTeleran
D
34

I have a gist made by myself to solve the same problem that you have.

Check It out:

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

Now I don't need spinners. You will still having floating label effect with animations included.

Dodd answered 2/8, 2015 at 20:46 Comment(8)
What exactly do you mean by "I don't need spinners", and where in your gist, you say "Used to make your EditText a better option than Spinners"? Is the UI simply an EditText?Implode
I use EditText with TextInput Layout as an alternative for spinners. When the user click in this custom EditText I open a dialog with options to select. Of course It could be better and I accept any improvement like to show the options with another kinds of views just like spinner.Dodd
Could you make it clearer on how to implement the View (usage). There are various parts of the code that seem to be missing, and so I'm not sure how to fill in the gaps.Implode
I will upgrade this gist into a github project with samples then I hope to get help to improve this solution.Dodd
Add setInputType(InputType.TYPE_NULL); to the onDraw(canvas) method to disable any input into the edittext. Otherwise e.g. long clicks will bring up a context menu and enable the user to paste text.Tablet
Thanks @Capricorn. I will fix this.Dodd
well this was awesome, but if you ever decided to upgrade it into a github library, make sure to give an option to choose between EditText or AppCompatEditText, please !Gluconeogenesis
label is not floatingBrandeebranden
I
11

I have created a compound View component which displays a label above the Spinner. The text for the label can be set using XML or in Java.

The component has the key features of a Spinner (not all of them) as well as looking similar to the TextInputLayout component.

I have named it a LabelledSpinner, and it is available as part of my UsefulViews Android library on GitHub under the Apache 2.0 License.

To use it, add the library dependency in your build.gradle file:

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

Examples of its usage are available at the GitHub repository (both a sample app, and a usage guide).

Implode answered 5/8, 2015 at 16:23 Comment(6)
@LavekushAgrawal Let me know what exactly doesn't work or open an issue on the repoImplode
Actually its working , but label is not float like edittextBrandeebranden
@LavekushAgrawal There is a small label positioned above the Spinner, which is similar to how there are small labels above EditText components, wrapped in TextInputLayout. How did you imagine it to be?Implode
@FarbodSalamat-Zadeh This is a cool library to use, but it doesn't work on Lollipop.Coppola
@FarbodSalamat-Zadeh Yep, InflateException on API 23Dipietro
not working on gradle 3.5, only gradle 2.0 very old libraryLaevorotation
A
11

You can achieve this : enter image description here

With new Material library styles like this:

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

for more information: https://material.io/develop/android/components/menu/

UPDATE: Non editable variation: Disable the user input in the *AutoCompleteTextView* to have a non editable variation of the menu by setting android:inputType="none" on the AutoCompleteTextView.

Akkadian answered 1/9, 2019 at 13:23 Comment(2)
it's not spinnerInformality
it is a spinner, only with a different designAkkadian
T
5

I modified Rodrigo's solution to use an adapter, i.e. more like a standard Spinner https://gist.github.com/smithaaron/d2acd57937d7a4201a79

Twedy answered 13/11, 2015 at 15:30 Comment(0)
F
5

I have an alternative solution that uses the behavior of TextInputLayout and a custom DialogFragment (AlertDialog) to emulate a spinner dialog popup.

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>

Create Custom Spinner 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");
}

Result

When you click on the spinner styled TextInputLayout, it will trigger an alert dialog containing your list of selections. Once a selection is chosen, the EditText will be populated with your selection and the label will float like how you want.

Fdic answered 2/1, 2017 at 0:57 Comment(2)
Doesn't android:focusable="false" make this view inaccessible to those using keyboard or voice to interact with the app?Vivianviviana
I have never used keyboard or voice to interact with a spinner, but the reason why I set android:focusable="false" is so the user cannot type an input that is not amongst the selection. Unfortunately, I don't have a physical Android phone to test out the behaviors to let you know.Fdic
O
3

Here is my trick,

the good things is that everything will work like you want,

but the bad is that it is increasing layout hierarchy, and you have to handle functionality in code, and it is an ugly solution:

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

and override adapter for spinner, to make selected values transparent

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

and after selecting in spinner, just get selected text and set it to EditText and it will have the same effect with 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) {

        }
    });

if you have any question Ask me

Ommatophore answered 16/12, 2016 at 12:14 Comment(2)
can you help me i m getting stuck? i have multiple edit text so oncreate of layout focus goes to first edittext and while directly i am pressing spinner so above code of edittext not get focus .but i need to get focus on click of spinner how can i?Loni
@Loni can you please tell my the order of the view, because when i am trying i was able to focus on spinnerOmmatophore
T
1

Here is a library that I use for the Floating label spinner rey5137 Material Library

Also, for future reference, here is a list of some great libraries. UI Libraries Core Libraries

Trigonometry answered 4/8, 2015 at 2:51 Comment(0)
C
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>
Coign answered 23/2, 2018 at 8:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.