【问题标题】:How to add floating label on Spinner如何在 Spinner 上添加浮动标签
【发布时间】:2015-10-16 00:24:30
【问题描述】:

使用Android设计支持库的TextInputLayoutEditText组件上方放置浮动标签后,我想知道是否有办法向Spinner组件添加浮动标签(不一定使用设计图书馆)。

我的意思是TextView 放在Spinner 上方(显然没有像TextInputLayout 这样的动画),但我希望文本大小、字体和颜色与@ 匹配987654337@的浮动标签.

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

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

Google Design page about floating label text fields上,有一个图表显示了标签相对于组件的尺寸,但没有说明标签文本颜色或大小:

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


编辑:

Google Design guidelines for text fields,它有以下浮动标签:

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

以及上面显示的图像。

所以浮动标签字体为:Roboto Regular 12sp。因此,您可以使用TextView 来显示Spinner 标签,因为我不知道您可以使用任何自定义Views 或特殊组件。

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

【问题讨论】:

    标签: android android-layout android-spinner material-design android-textinputlayout


    【解决方案1】:

    我希望文本大小、字体和颜色与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>
    

    如果您希望标签始终具有突出显示(强调)颜色,请将 Google 设计支持库中的 TextAppearance.AppCompat.Caption 替换为 TextAppearance.Design.Hint。但是,如果您还在同一屏幕上标记了 EditText 视图,这可能看起来有点奇怪。

    最后,您可以在 Spinner(或任何其他元素)上方放置一个 TextView 并应用样式:

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

    结果

    以下屏幕截图显示了一个简单示例,其中包含两个普通的 TextInputLayout 视图,后跟一个标签和一个 Spinner。我没有应用额外的8dp 间距来进一步分隔它们,但这表明大小、字体和颜色都得到了反映。

    Spinner 内的元素具有不同的填充,但我更喜欢与所有其他标签保持垂直对齐以获得更统一的外观。

    【讨论】:

    • 是的,完全有可能在没有外部库的情况下实现这一点。事实上,我在创建自定义视图时使用了相同的方法,它实际上是由样式化的 TextView、Spinner 和分隔视图组成的复合视图。我制作自定义视图的原因是因为它更容易在布局层次结构中作为一个视图进行管理,而不是尝试管理两个或三个视图。
    • 您可能希望在样式中添加&lt;item name="android:textColor"&gt;?android:textColorHint&lt;/item&gt;,以便在自定义标签中获得与未聚焦状态下 TextInputLayout 中使用的相同的文本颜色。
    • “但是,如果您在同一屏幕上还标记了 EditText 视图,这可能看起来有点奇怪。”... 您可以具体并将样式命名为 SpinnerLabel 而不是 InputLabel,以便仅将它们用于微调器。
    • “Spinner 内部的元素有不同的填充,但是我更喜欢与所有其他标签保持垂直对齐以获得更统一的外观。”尽管如此,d 还是希望将相同的左侧填充应用于标签,以便它与微调器元素对齐,值是 8dp,如定义 here.
    【解决方案2】:

    我通过使用 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,从而确保您拥有完全正确的外观。
    • 只是一个小改进:使用R.layout.support_simple_spinner_dropdown_item 而不是android.R.layout.simple_spinner_item
    • 非常聪明的 hack。但它仍然是一个 hack。
    • 如果您使用mTextView.setText(...),您将不会在下拉列表中收到来自适配器的所有值...调用adapter.getFilter().filter(null); 再次显示所有建议
    • 另外,如果您设置文本,则无法以编程方式关闭下拉菜单
    【解决方案3】:

    我自己制定了一个要点来解决你遇到的同样的问题。

    检查一下:

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

    现在我不需要旋转器了。您仍将具有包含动画的浮动标签效果。

    【讨论】:

    • 你所说的 “我不需要微调器” 到底是什么意思,在你的要点中,你说 “用来让你的 EditText 成为更好的选择比 Spinners"? UI 只是一个 EditText 吗?
    • 我使用带有 TextInput 布局的 EditText 作为微调器的替代方案。当用户单击此自定义 EditText 时,我会打开一个对话框,其中包含可供选择的选项。当然这可能会更好,我接受任何改进,比如用另一种视图显示选项,就像微调器一样。
    • 我会将这个 gist 升级为一个带有示例的 github 项目,然后我希望得到帮助来改进这个解决方案。
    • setInputType(InputType.TYPE_NULL); 添加到onDraw(canvas) 方法以禁用对edittext 的任何输入。否则例如长按会弹出一个上下文菜单并允许用户粘贴文本。
    • 这太棒了,但是如果您决定将其升级为 github 库,请确保提供EditTextAppCompatEditText 之间的选项!
    【解决方案4】:

    我创建了一个复合View 组件,它在Spinner 上方显示一个标签。标签的文本可以使用 XML 或 Java 设置。

    该组件具有Spinner 的主要功能(不是全部),并且看起来类似于TextInputLayout 组件。

    我已将其命名为 LabelledSpinner,它可以作为我在 GitHub 上 Apache 2.0 License 下的 UsefulViews Android 库的一部分。

    要使用它,请在 build.gradle 文件中添加库依赖项:

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

    GitHub 存储库中提供了其使用示例(示例应用和使用指南)。

    【讨论】:

    • @LavekushAgrawal 让我知道究竟是什么不起作用或在 repo 上打开一个问题
    • 实际上它的工作,但标签不像edittext那样浮动
    • @LavekushAgrawal 在Spinner上方有一个小标签,类似于EditText组件上方的小标签,包裹在TextInputLayout中。你是怎么想象的?
    • @FarbodSalamat-Zadeh 这是一个很酷的库,但它不适用于 Lollipop。
    • 不能在 gradle 3.5 上工作,只有 gradle 2.0 非常老的库
    【解决方案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/

    更新: 不可编辑的变体: 通过在AutoCompleteTextView 上设置android:inputType="none",禁用*AutoCompleteTextView* 中的用户输入以具有菜单的不可编辑变体。

    【讨论】:

    • 它不是微调器
    • 它是一个微调器,只是设计不同
    【解决方案6】:

    我修改了 Rodrigo 的解决方案以使用适配器,即更像标准 Spinner https://gist.github.com/smithaaron/d2acd57937d7a4201a79

    【讨论】:

      【解决方案7】:

      我有一个替代解决方案,它使用 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) 创建自定义 Spinner

      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" 不会让使用键盘或语音与应用程序交互的用户无法访问此视图吗?
      • 我从未使用键盘或语音与微调器进行交互,但我设置android:focusable="false" 的原因是用户无法键入不在选择范围内的输入。很遗憾,我没有实体 Android 手机来测试这些行为让您知道。
      【解决方案8】:

      这是我的窍门,

      好在一切都会如你所愿,

      但不好的是它增加了布局层次,你必须在代码中处理功能,这是一个丑陋的解决方案:

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

      有什么问题可以问我

      【讨论】:

      • 你能帮我我卡住了吗?我有多个编辑文本,所以布局焦点的创建转到第一个编辑文本,而我直接按下微调器,所以上面的编辑文本代码没有获得焦点。但我需要关注微调器的点击我怎么能?
      • @sunita 你能告诉我视图的顺序吗,因为当我尝试时,我能够专注于微调器
      【解决方案9】:

      这是我用于浮动标签微调器的库 rey5137 Material Library

      另外,为了将来参考,这里列出了一些很棒的库。 UI Libraries Core Libraries

      【讨论】:

        【解决方案10】:

        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>
        

        【讨论】:

          猜你喜欢
          • 2022-12-20
          • 1970-01-01
          • 2020-02-12
          • 1970-01-01
          • 2012-10-29
          • 1970-01-01
          • 2023-03-22
          • 1970-01-01
          相关资源
          最近更新 更多