为Spinner创建一个setError()方法

51

如何为Spinner创建类似于TextView/EditTextsetError()函数?以下内容无法正常工作:

我尝试扩展Spinner类并在构造函数中:

ArrayAdapter<String> aa = new ArrayAdapter<String>(getContext(),
                    android.R.layout.simple_spinner_item, android.R.id.text1,
                    items);
            aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            setAdapter(aa);
             tv = (TextView) findViewById(android.R.id.text1);
            // types_layout_list_tv

            ctv = (CheckedTextView) aa.getDropDownView(1, null, null);
            tv2 = (TextView) aa.getView(1, null, null);

setError方法:

    public void setError(String str) {
        if (tv != null)
            tv.setError(str);
        if(tv2!=null)
            tv2.setError(str);
        if (ctv != null)
            ctv.setError(str);
    }

在您的示例中让我困惑的是:TextView tv、ctv和tv2从哪里来,它们与Spinner有什么关系?问候,Ready4Android - Ready4Android
你有这个问题的解决方案吗? - Code_Life
10个回答

49

与 @Gábor 的解决方案类似,但是我不需要创建自己的适配器。我只需在我的验证函数中调用以下代码(即在提交按钮点击时)。

        TextView errorText = (TextView)mySpinner.getSelectedView();                  
        errorText.setError("anything here, just to add the icon");
        errorText.setTextColor(Color.RED);//just to highlight that this is an error
        errorText.setText("my actual error text");//changes the selected item text to this

1
无法让那个工作。 我有两个微调器,都在getSelectedView()上返回null 除此之外,该解决方案看起来最好。 - John
2
@John 我在onCreateView方法中将我的android.widget.Spinner保存为实例变量,然后在另一个方法中再次访问它,即在我的情况下是在onDoneAction方法中访问。请确保在选择Spinner中的项后调用getSelectedView,即getSelectedItem()也返回非空值。 - EdmundYeung99
1
很棒的答案!所选的视图实际上是一个TextView,因此这将按预期工作! - Ryan Amaral

22

我有一个解决方案,它不需要创建额外的编辑字段,但您需要拥有自己的SpinnerAdapter,像往常一样。

确保您在适配器的getView()中使用的布局中至少有一个TextView(通常情况下您都具有该功能)。

将以下函数添加到适配器中(将name更改为您的TextView的ID):

public void setError(View v, CharSequence s) {
  TextView name = (TextView) v.findViewById(R.id.name);
  name.setError(s);
}

从您的代码中以这种方式调用setError()

YourAdapter adapter = (YourAdapter)spinner.getAdapter();
View view = spinner.getSelectedView();
adapter.setError(view, getActivity().getString(R.string.error_message));

基本上,与任何其他控件一样,您只需要在适配器上调用它,并且还必须提供视图。

这将像其他控件一样在下拉列表中显示错误图标。


这是一个很棒的解决方案。 - EladHackim
当宽度为wrap_content时,错误图标显示不正确 - 被隐藏在下拉菜单图标下面。 - Wooff

12

使用隐藏的TextView显示弹出消息

这个解决方案涉及在下拉列表框下方添加一个隐藏的文本框,使得TextView的错误对话框可以显示出来,同时也使用了在下拉列表框布局XML中设置的TextView,以便显示红色(!)图标。因此,实际上使用了两个TextView——一个用于图标,另一个(隐藏的)用于允许错误对话框。

当没有错误状态时,它看起来像这样(使用SetError(null)):

Spinner in valid state

当有错误时,它看起来像这样(使用SetError("my error text, ideally from a resource!")):

Spinner in invalid state

以下是下拉列表框布局XML的摘录。使用RelativeLayout确保TextView尽可能靠近下拉列表框,并具有足够的paddingRight,以确保消息对话框上的箭头与红色错误(!)图标对齐。隐藏的(假的)TextView相对于Spinner定位。

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="top|left"
        >

        <Spinner
            android:id="@+id/spnMySpinner"
            android:layout_width="400dp"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:dropDownSelector="@drawable/selector_listview"
            android:background="@android:drawable/btn_dropdown"
            android:paddingBottom="0dp"
            android:layout_marginBottom="0dp"
            />

        <!-- Fake TextView to use to set in an error state to allow an error to be shown for the TextView -->
        <android.widget.TextView
            android:id="@+id/tvInvisibleError"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_alignRight="@+id/spnMySpinner"
            android:layout_alignBottom="@+id/spnMySpinner"
            android:layout_marginTop="0dp"
            android:paddingTop="0dp"
            android:paddingRight="50dp"
            android:focusable="true"
            android:focusableInTouchMode="true"
            />

    </RelativeLayout>
注意:@drawable/selector_listview 的定义超出了本解决方案的范围。请参见此处示例,了解如何使其工作,因为这不是本答案的主题。
以下是使其工作的代码。只需使用null调用SetError(errMsg)以清除错误,或者使用文本将其设置为错误状态。
/**
 * When a <code>errorMessage</code> is specified, pops up an error window with the message
 * text, and creates an error icon in the secondary unit spinner. Error cleared through passing
 * in a null string.
 * @param errorMessage Error message to display, or null to clear.
 */
public void SetError(String errorMessage)
{
    View view = spnMySpinner.getSelectedView();

    // Set TextView in Secondary Unit spinner to be in error so that red (!) icon
    // appears, and then shake control if in error
    TextView tvListItem = (TextView)view;

    // Set fake TextView to be in error so that the error message appears
    TextView tvInvisibleError = (TextView)findViewById(R.id.tvInvisibleError);

    // Shake and set error if in error state, otherwise clear error
    if(errorMessage != null)
    {
        tvListItem.setError(errorMessage);
        tvListItem.requestFocus();

        // Shake the spinner to highlight that current selection 
        // is invalid -- SEE COMMENT BELOW
        Animation shake = AnimationUtils.loadAnimation(this, R.anim.shake);
        spnMySpinner.startAnimation(shake);

        tvInvisibleError.requestFocus();
        tvInvisibleError.setError(errorMessage);
    }
    else
    {
        tvListItem.setError(null);
        tvInvisibleError.setError(null);
    }
}

在上面的SetError函数中,例子中有一些额外的代码会导致当错误设置时Spinner中的文本抖动。这不是解决方案所必需的,但是是一个不错的补充。 看这里以获取此方法的灵感。

向@Gábor表示赞扬,感谢他的解决方案,该方案利用了Spinner的项目布局XML中的TextView。代码View view = spnMySpinner.getSelectedView();(基于@Gábor的解决方案)是必要的,因为它获取当前显示的TextView,而不是使用findViewById,后者只会获取列表中的第一个TextView(基于提供的资源ID),因此如果未选择列表中的第一项,则不起作用(无法显示红色“!”图标)。


在这部分中,我有一个线性布局,有任何线索吗? TextView tvListItem =(TextView)view; - vinicius gati

8
这可以在不使用自定义布局或适配器的情况下完成。
((TextView)spinner.getChildAt(0)).setError("Message");

这种方法唯一的缺点是,当点击图标时,不会显示带有错误文本的弹出窗口。

5
我建议您在下拉列表框后插入一个空的EditText
在xml中设置该EditText
android:enabled="false"
    android:inputType="none"

现在,当您想将错误设置为您的“spinner”时,只需将该错误设置为EditText即可。

请注意,不要将EditText设置为invisible/gone。这样是行不通的。

此外,请注意,通过这种方法,您可以决定您希望错误出现的确切位置。


3
将EditText放在Spinner后面的一个限制是,部分错误检查行为会出现问题。虽然显示了表示错误的红色感叹号,但单击感叹号不会像通常情况下那样显示带有错误消息的弹出窗口。 - Theo

1
感谢Gabor提供的出色解决方案。进一步说,我的解决方案如下:
自定义适配器
    public class RequiredSpinnerAdapter<T> extends ArrayAdapter<T> {
        public RequiredSpinnerAdapter(Context context, int textViewResourceId,
                                      java.util.List<T> objects) {
            super(context, textViewResourceId, objects);
        }

        int textViewId = 0;

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View view = super.getView(position, convertView, parent);
            if (view instanceof TextView) {
                textViewId = view.getId();
            }
            return view;
        }

        public View getDropDownView(int position, View convertView, ViewGroup parent) {
            View row = super.getView(position, convertView, parent);
            return (row);
        }

        public void setError(View v, CharSequence s) {
            if(textViewId != 0){
                TextView name = (TextView) v.findViewById(textViewId);
                name.setError(s);
            }
        }
    }

使用适配器来控制下拉列表(Spinner)。
ArrayAdapter<String> arrayAdapter = new RequiredSpinnerAdapter<String>(PropertyAdd.this, R.layout.checked, status_arr);
    marketstatus_spinner.setAdapter(arrayAdapter);
    marketstatus_spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> arg0, View arg1,
                                   int arg2, long arg3) {

            // Put code here
        }

        @Override
        public void onNothingSelected(AdapterView<?> arg0) {
           // Put code here
        }
    });

检查验证

private boolean checkValidation() {
    if(marketstatus_spinner.getSelectedItem().toString().equals("")){
        RequiredSpinnerAdapter adapter = (RequiredSpinnerAdapter)marketstatus_spinner.getAdapter();
        View view = marketstatus_spinner.getSelectedView();
        adapter.setError(view, "Please select a value");

        return false;
    }
}

1
再进一步说,加上一个“推动”! :-) - ban-geoengineering

0

我猜Spinner不是放置这个方法的正确位置。在Spinner中,您应该选择一个值,并且Spinner中的值应该在适配器的级别上进行过滤。因此,用户只能选择Spinner中的那些值。


3
我同意,但在0项目是“选择..”的情况下,如果用户的选择停留在索引0,则需要使用setError。 - Bojan

0

您可以创建自己的适配器(扩展BaseAdapter实现SpinnerAdapter)。这样,您就可以访问在下拉列表中显示的TextViews。(getView和createViewFromResource方法 - 例如:ArrayAdapter)当您添加一个空列表项以允许用户将字段保持为空,直到它变为必填项(在下拉列表中的第一项),您可以将其TextView存储为适配器中的私有成员。然后,在从Activity或Fragment调用setError("...")时,您可以在适配器上调用它,适配器可以将其传递给空的TextView。

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    return createViewFromResource(position, convertView, parent, mTextViewId);
}

private View createViewFromResource(int position, View convertView, ViewGroup parent, int resource) {
    View view;
    TextView text;

    if (convertView == null) {
        view = inflater.inflate(resource, parent, false);
    } else {
        view = convertView;
    }

    try {
        text = (TextView) view;
    } catch (ClassCastException e) {
        Log.e(TAG, "You must supply a resource ID for a TextView", e);
        throw new IllegalStateException("MyAdapter requires the resource ID to be a TextView", e);
    }

    MyItem i = getItem(position);
    String s = (null != i) ? i.toString() : "";
    text.setText(s);

    if ("".equals(s) && null == mEmptyText) {
        this.mEmptyText = text;
    }

    return view;
}

public void setError(String errorMessage) {
    if (null != mEmptyText) {
        mEmptyText.setError(errorMessage);
    } else {
        Log.d(TAG, "mEmptyText is null");
    }
}

1
在类似的情况(与您的示例相同),我只有图标而没有文本错误。 - Helpa

0
其实这很简单,你只需要在你的视图中只有一个TextView,然后使用getSelectedView()从你的下拉列表中获取所选的视图,如果所选视图中的主视图是TextView,那么直接将你的视图强制转换为TextView并像这样设置setError
((TextView) jobCategory.getSelectedView()).setError("Field Required");

如果TextView不是直接在主视图中,那么您需要通过ID找到它并再次进行类型转换,然后以这种方式setError

 ((TextView) jobCategory.getSelectedView().findViewById(R.id.firstName)).setError("Field Required");

0
我建议你在下拉列表框后面放一个空的编辑文本框,并添加以下属性:
android:visibility="visible"
android:enabled="false"
android:inputType="none"
当用户点击按钮或触发事件时,在你的代码中添加以下内容。
            if (YourSpinner.getSelectedItemPosition() > 0) {
          
            String  item= String.valueOf(Yourspinner.getSelectedItem());
             editTextForError.setVisibility(View.GONE);
            Toast.makeText(MainActivity.this,item,Toast.LENGTH_SHORT).show();

            }
           else
            {
              
              editTextForError.requestFocus();
              editTextForError.setError("Erro Message");
            }
 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接