Wie erstellen Sie die Funktion setError()
(ähnlich einer TextView/EditText
) für eine Spinner
? Folgendes funktioniert nicht:
Ich habe versucht, die Spinner-Klasse und den Konstruktor zu erweitern:
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
Methode:
public void setError(String str) {
if (tv != null)
tv.setError(str);
if(tv2!=null)
tv2.setError(str);
if (ctv != null)
ctv.setError(str);
}
Ähnlich wie bei @ Gábor, aber ich musste keinen eigenen Adapter erstellen. Ich rufe einfach den folgenden Code in meiner Überprüfungsfunktion auf (d. H. Beim Klicken auf "Senden".)
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
Ich habe eine Lösung, bei der kein zusätzliches Editierfeld erstellt wird, Sie benötigen jedoch wie üblich Ihre eigene SpinnerAdapter
.
Vergewissern Sie sich, dass Sie mindestens eine TextView
in dem Layout haben, das Sie in der getView()
Ihres Adapters verwenden (dies ist normalerweise der Fall).
Fügen Sie Ihrem Adapter folgende Funktion hinzu (ändern Sie name
in die ID Ihrer TextView
):
public void setError(View v, CharSequence s) {
TextView name = (TextView) v.findViewById(R.id.name);
name.setError(s);
}
Rufen Sie die setError()
aus Ihrem Code auf folgende Weise auf:
YourAdapter adapter = (YourAdapter)spinner.getAdapter();
View view = spinner.getSelectedView();
adapter.setError(view, getActivity().getString(R.string.error_message));
Grundsätzlich gilt, wie bei jedem anderen Steuerelement, nur, dass Sie es auf Ihrem Adapter aufrufen und die Ansicht ebenfalls bereitstellen müssen.
Dadurch wird das Fehlersymbol auf dem Spinner angezeigt, wie dies bei anderen Steuerelementen der Fall ist.
Diese Lösung umfasst das Hinzufügen eines zusätzlichen ausgeblendeten Textfelds direkt unterhalb des Drehfelds genau an der richtigen Position, damit das Fehlerdialogfeld von TextView angezeigt werden kann. Außerdem wird das TextView-Set im Layout-XML des Drehfelds verwendet, um das rote Symbol (!) Anzuzeigen. Es werden also zwei Textansichten verwendet - eine für das Symbol und eine (verborgene) für den Fehlerdialog.
So sieht es aus, wenn es sich nicht in einem Fehlerzustand befindet (verwenden Sie SetError(null)
):
So sieht es aus, wenn ein Fehler auftritt (verwenden Sie SetError("my error text, ideally from a resource!")
):
Hier ist der Auszug aus dem XML-Layout des Spinners. Es wird ein RelativeLayout verwendet, um sicherzustellen, dass sich die TextView so nahe wie möglich am Spinner befindet und gerade genug paddingRight vorhanden ist, um sicherzustellen, dass der Pfeil im Nachrichtendialogfeld unter dem roten Fehlersymbol (!) Ausgerichtet ist. Die versteckte (gefälschte) TextView wird relativ zum Spinner positioniert.
<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>
Hinweis: @drawable/selector_listview
wird außerhalb des Umfangs dieser Lösung definiert. Siehe Beispiel hier , wie man dies zum Laufen bringt, da es für diese Antwort ein Thema ist.
Hier ist der Code, damit es funktioniert. Rufen Sie einfach SetError(errMsg)
entweder mit null
auf, um den Fehler zu löschen, oder mit Text, um ihn in einen Fehlerzustand zu setzen.
/**
* 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);
}
}
In der obigen Funktion SetError
gibt es einen zusätzlichen Code, der dazu führt, dass der Text im Spinner beim Setzen des Fehlers verwackelt. Dies ist nicht erforderlich, um die Lösung zum Laufen zu bringen, ist jedoch eine nette Ergänzung. Siehe hier für die Inspiration für diesen Ansatz.
Ein dickes Lob an @ Gábor für seine Lösung , die die TextView im XML-Elementlayout des Spinners verwendet. Der Code View view = spnMySpinner.getSelectedView();
(basierend auf der Lösung von @ Gábor) ist erforderlich, da er die aktuell angezeigte TextView abruft, anstatt eine findViewById
zu verwenden, die lediglich die erste TextView in der Liste abrufen würde (basierend auf der bereitgestellten Ressourcen-ID) und daher nicht work (um das rote (!) Symbol anzuzeigen), wenn der erste Eintrag in der Liste nicht ausgewählt ist.
Dies kann ohne Verwendung eines benutzerdefinierten Layouts oder Adapters erfolgen.
((TextView)spinner.getChildAt(0)).setError("Message");
Der einzige Nachteil dieses Ansatzes ist, dass das kleine Popup mit dem Fehlertext nicht angezeigt wird, wenn auf das Symbol getippt wird.
Ich würde dir vorschlagen, ein leeres EditText
direkt hinter deinen Spinner zu legen.
Legen Sie in der XML-Datei fest, dass EditText
Android:enabled="false"
Android:inputType="none"
Wenn Sie jetzt einen Fehler für Ihren Spinner festlegen möchten, setzen Sie diesen Fehler einfach auf EditText
.
Denken Sie daran, dass EditText
nicht invisibille
/gone
gesetzt ist. So wird es nicht funktionieren.
Beachten Sie außerdem, dass Sie mit dieser Methode genau entscheiden können, wo der Fehler angezeigt werden soll.
Danke, Gabor, für deine fantastische Lösung. Im Hinblick auf Ihren Punkt ist meine Lösung also:
Kundenspezifischer Adapter
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);
}
}
}
Verwenden Sie einen Adapter für den 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
}
});
Überprüfen Sie die Validierung
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;
}
}
Ich denke, dass Spinner nicht der richtige Ort für diese Methode ist. Im Falle von Spinner sollten Sie einen Wert auswählen und die Werte im Spinner sollten auf der Ebene Ihres Adapters gefiltert werden. Daher kann ein Benutzer nur die Werte auswählen, die sich im Spinner befinden.
Eigentlich ist dies sehr, Sie brauchen nur eine TextView
in Ihrer Ansicht und dann die ausgewählte Ansicht von Ihrem Spinner mit getSelectedView()
abzurufen, wenn die Hauptansicht in Ihrer ausgewählten Ansicht eine TextView
ist, und wandeln Sie Ihre Ansicht direkt in TextView
und setError
wie folgt :
((TextView) jobCategory.getSelectedView()).setError("Field Required");
Andernfalls, wenn The Textview nicht direkt die MAIN-Ansicht ist, müssen Sie sie anhand der ID suchen und erneut umwandeln und setError
folgendermaßen:
((TextView) jobCategory.getSelectedView().findViewById(R.id.firstName)).setError("Field Required");
Sie können Ihren eigenen Adapter erstellen (erweitert BaseAdapter implementiert SpinnerAdapter). Auf diese Weise können Sie auf die im Spinner angezeigten TextViews zugreifen. (Methoden getView und createViewFromResource - Beispiel: ArrayAdapter ) Wenn Sie ein leeres Listenelement hinzufügen, damit der Benutzer das Feld leer lassen kann, bis es obligatorisch ist (erstes Element im Spinner), können Sie dessen TextView als privat speichern Mitglied im Adapter. Wenn dann der Aufruf von setError ("...") aus der Aktivität oder dem Fragment erfolgt, können Sie es auf dem Adapter aufrufen, der es an die leere TextView übergeben kann.
@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");
}
}