如何在 Spinner 上添加浮动标签

在使用 Android 设计支持库的 TextInputLayoutEditText组件上放置一个浮动标签之后,我想知道是否有一种方法可以向 Spinner组件添加一个浮动标签(不一定使用 Design Library)。

我的意思是像 TextView放置在 Spinner之上(显然没有像 TextInputLayout那样的动画) ,但是我想要 文本大小,字体和颜色匹配的 TextInputLayout的浮动标签

例如,它看起来像这样(参见 Spinners 上面的标签) :

enter image description here

正如我之前提到的,我的主要目标是在 Spinner之上有一个标签,就像在 TextInputLayout中一样——所以文本大小、字体、颜色以及标签和组件之间的距离都是相同的。

关于浮动标签文本字段的 Google 设计页面上,有一个图表显示标签相对于组件的尺寸,但没有标明标签文本的颜色或大小:

enter image description here

所以,总而言之,我要问:
- 如果有一个特殊的组成部分,以实现我所要求的或一个自定义视图,我可以使用,它会是什么,以及如何使用它。
- 如否,浮动标签的文字大小、颜色和字体为何,以便我可以在我的 Spinner上方放置一个 TextView,并显示上图所示的布局尺寸。


编辑:

Google 文本字段设计指南来看,它有以下浮动标签:

提示和输入字体: Roboto 常规16sp
标签字体: Roboto 常规12sp
瓷砖高度: 72dp
文本顶部和底部填充: 16 dp
文本字段分隔符填充: 8 dp

以及上面显示的图像。

所以浮动标签字体是: < strong > 机器人规则12sp 。因此,您可以使用 TextView来显示 Spinner标签,因为我不知道您可以使用任何定制的 View或特殊组件。

但是 ,在试用之后,它看起来并不像图像中显示的示例那样好。一个 自定义视图可能更好,因为它可以看起来更好,但是上面的解决方案只是实现接近我最初想要的东西的一种方法。

59167 次浏览

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.

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

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

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

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

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

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

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.

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>

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.