web-dev-qa-db-fra.com

Créer un setError () pour le spinner

Comment créez-vous la fonction setError() (similaire à celle d'un TextView/EditText) pour un Spinner? Ce qui suit ne fonctionne pas:

J'ai essayé d'étendre la classe Spinner et dans le constructeur:

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 méthode:

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

Semblable à la solution de @ Gábor, mais je n'avais pas besoin de créer mon propre adaptateur. J'appelle simplement le code suivant dans ma fonction de validation (c'est-à-dire en cliquant sur le bouton d'envoi)

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

J'ai une solution qui ne nécessite pas la création d'un champ de saisie supplémentaire, mais vous avez besoin de votre propre variable SpinnerAdapter, comme d'habitude.

Assurez-vous d’avoir au moins une TextView dans la présentation que vous utilisez dans la fonction getView() de votre adaptateur (de toute façon, vous l’avez normalement).

Ajoutez la fonction suivante à votre adaptateur (remplacez name par l'ID de votre TextView):

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

Appelez la setError() à partir de votre code de cette façon:

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

Fondamentalement, comme pour tout autre contrôle, vous l’appelez uniquement sur votre adaptateur et vous devez également fournir la vue.

Ceci affichera l’icône d’erreur sur la roulette comme c’est le cas avec les autres contrôles.

20
Gábor

Utilisation d'un TextView masqué pour faire apparaître un message contextuel

Cette solution implique l'ajout d'une zone de texte masquée supplémentaire juste sous le compteur, juste au bon endroit, afin de permettre à la boîte de dialogue d'erreur de TextView de s'afficher, tout en utilisant le paramètre TextView défini dans le XML de présentation du compteur pour permettre à l'icône rouge (!) D'être affichée. En réalité, deux vues de texte sont utilisées: une pour l'icône et une autre (masquée) pour autoriser le dialogue d'erreur.

Voici à quoi cela ressemble quand il n'y a pas d'erreur (utilisez SetError(null)):

Spinner in valid state

Voici à quoi ça ressemble quand il y a une erreur (utilisez SetError("my error text, ideally from a resource!")):

Spinner in invalid state

Voici l'extrait de la mise en page XML du spinner. Il existe un RelativeLayout utilisé pour garantir que le TextView est aussi proche que possible de la roulette et a juste assez de paddingRight pour que la flèche de la boîte de dialogue du message soit alignée juste en dessous de l'icône d'erreur rouge (!). Le TextView masqué (faux) est positionné par rapport au 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>

Remarque: @drawable/selector_listview défini en dehors de la portée de cette solution. Voir exemple ici sur la façon de faire fonctionner ceci, car il est hors sujet pour cette réponse.

Voici le code pour le faire fonctionner. Appelez simplement la SetError(errMsg) soit avec null pour effacer l'erreur, soit avec du texte pour la définir dans un état d'erreur.

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

Dans la fonction SetError ci-dessus, par exemple, il existe un code supplémentaire qui fait trembler le texte dans le Spinner lorsque l'erreur est définie. Ce n'est pas nécessaire pour que la solution fonctionne, mais c'est un ajout agréable. Voir ici pour l'inspiration de cette approche.

Félicitations à @ Gábor pour sa solution qui utilise TextView sur le format XML de Spinner's layout. Le code View view = spnMySpinner.getSelectedView(); (basé sur la solution de @ Gábor) est nécessaire, car il récupère le TextView actuellement affiché, plutôt que d'utiliser une findViewById, qui obtiendrait simplement le premier TextView de la liste (basé sur l'ID de ressource fourni), et par conséquent travail (pour afficher l’icône rouge (!)) si le tout premier élément de la liste n’est pas sélectionné.

10
CJBS

Cela peut être fait sans utiliser une mise en page personnalisée ou un adaptateur.

((TextView)spinner.getChildAt(0)).setError("Message");

Le seul inconvénient de cette approche est que le petit popup avec le texte d'erreur ne s'affichera pas lorsque l'icône sera exploitée.

6
Jarett Millard

Je vous suggère de mettre une EditText vide juste derrière votre casserole.

Sur le jeu xml que EditText 

Android:enabled="false"
    Android:inputType="none"

Maintenant, lorsque vous voulez définir une erreur sur votre compteur, définissez simplement cette erreur sur EditText.

N'oubliez pas de définir EditText sur invisibille/gone. Cela ne fonctionnera pas comme ça.

Notez également que par cette méthode, vous pouvez décider exactement où vous voulez que votre erreur apparaisse.

5
EladHackim

Merci Gabor pour votre solution fantastique. Dans la suite de votre propos, ma solution est donc la suivante:

Adaptateur personnalisé

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

Utilisez l'adaptateur pour 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
        }
    });

Vérifier la validation

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
Kwex

Je suppose que Spinner n'est pas le bon endroit pour mettre cette méthode. Dans le cas de Spinner, vous devez sélectionner une valeur et filtrer les valeurs dans Spinner au niveau de votre adaptateur. Ainsi, un utilisateur ne peut choisir que les valeurs qui se trouvent dans le compteur.

0
Yury

En fait, c’est très, il vous suffit de n’avoir qu’une seule variable TextView dans votre vue, puis d’obtenir la vue sélectionnée de votre visionneuse à l’aide de getSelectedView() si la vue principale de la vue sélectionnée est une TextView, puis convertissez directement votre vue en TextView et setError comme ceci. :

((TextView) jobCategory.getSelectedView()).setError("Field Required");

Sinon, si TextView n'est pas directement la vue PRINCIPALE, vous devez le rechercher par ID, puis le relancer et setError de cette façon: 

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

Vous pouvez créer votre propre adaptateur (l'extension BaseAdapter implémente SpinnerAdapter). De cette façon, vous pouvez accéder aux TextViews qui sont affichés dans le disque. (Méthodes getView et createViewFromResource - exemple: ArrayAdapter ) Lorsque vous ajoutez un élément de liste vide pour permettre à l'utilisateur de conserver le champ vide jusqu'à ce qu'il devienne obligatoire (premier élément dans le compteur), vous pouvez stocker son TextView en tant que fichier privé membre dans l'adaptateur. Ensuite, lorsque vient le temps d'appeler setError ("...") à partir de l'activité ou du fragment, vous pouvez l'appeler sur l'adaptateur, qui peut le transmettre à TextView vide. 

@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");
    }
}
0
j00ris