web-dev-qa-db-fra.com

Vue personnalisée ... remplace onTouchEvent mais pas performClick

J'obtiens cet avertissement (du titre de la question) dans une vue personnalisée Android que je suis en train de développer.

Pourquoi suis-je prévenu? Quelle est la logique derrière cela, c'est pourquoi est-ce un bon
pratique pour remplacer également performClick lorsque vous remplacez onTouchEvent?

47
peter.petrov

Quel est le but?

Dans certaines des autres réponses, vous pouvez voir comment faire disparaître l'avertissement, mais il est important de comprendre pourquoi le système veut que vous remplaciez performClick() en premier lieu.

Il y a des millions d'aveugles dans le monde. Peut-être que vous n'y pensez pas beaucoup normalement, mais vous devriez. Ils utilisent également Android. "Comment?" vous pourriez demander. Un moyen important consiste à utiliser l'application TalkBack . Il s'agit d'un lecteur d'écran qui donne un retour audio. Vous pouvez l'activer sur votre téléphone en accédant à Paramètres> Accessibilité> TalkBack . Parcourez le didacticiel ici. C'est vraiment intéressant. Essayez maintenant d'utiliser votre application les yeux fermés. Vous constaterez probablement que votre application est au mieux extrêmement ennuyeuse et au pire complètement cassée. C'est un échec pour vous et une désinstallation rapide par toute personne malvoyante.

Regardez cette excellente vidéo de Google pour découvrir comment rendre votre application accessible.

Comment remplacer performClick()

Examinons un exemple de vue personnalisée pour voir comment la substitution de performClick() fonctionne réellement. Nous allons créer une application de lancement de missiles simple. La vue personnalisée sera le bouton pour la déclencher.

enter image description here

Cela semble beaucoup mieux avec TalkBack activé, mais les gifs animés n'autorisent pas l'audio, vous n'aurez donc qu'à l'essayer vous-même.

Code

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <net.example.customviewaccessibility.CustomView
        Android:layout_width="200dp"
        Android:layout_height="200dp"
        Android:contentDescription="Activate missile launch"
        Android:layout_centerInParent="true"
        />

</RelativeLayout>

Notez que j'ai défini le contentDescription. Cela permet à TalkBack de lire ce qu'est la vue personnalisée lorsque l'utilisateur la ressent.

CustomView.Java

public class CustomView extends View {

    private final static int NORMAL_COLOR = Color.BLUE;
    private final static int PRESSED_COLOR = Color.RED;

    public CustomView(Context context) {
        super(context);
        init();
    }

    public CustomView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        setBackgroundColor(NORMAL_COLOR);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                setBackgroundColor(PRESSED_COLOR);
                return true;

            case MotionEvent.ACTION_UP:
                setBackgroundColor(NORMAL_COLOR);

                // For this particular app we want the main work to happen
                // on ACTION_UP rather than ACTION_DOWN. So this is where
                // we will call performClick(). 
                performClick();
                return true;
        }
        return false;
    }

    // Because we call this from onTouchEvent, this code will be executed for both
    // normal touch events and for when the system calls this using Accessibility 
    @Override
    public boolean performClick() {
        super.performClick();

        launchMissile();

        return true;
    }

    private void launchMissile() {
        Toast.makeText(getContext(), "Missile launched", Toast.LENGTH_SHORT).show();
    }
}

Remarques

  • La documentation utilise également une variable mDownTouch qui semble être utilisée pour filtrer les événements de retouche supplémentaires, mais comme elle n'est pas bien expliquée ou strictement nécessaire pour notre application, je l'ai laissée en dehors. Si vous créez une véritable application de lancement de missiles, je vous suggère d'en savoir plus.
  • La principale méthode qui lance le missile (launchMissile()) est simplement appelée à partir de performClick(). Attention à ne pas l'appeler deux fois si vous l'avez également dans onTouchEvent. Vous devrez décider exactement comment et quand appeler votre méthode de logique métier en fonction des spécificités de votre vue personnalisée.
  • Ne remplacez pas performClick(), puis ne faites rien avec lui juste pour supprimer l'avertissement. Si vous voulez ignorer les millions d'aveugles dans le monde, vous pouvez supprimer l'avertissement. Au moins de cette façon, vous êtes honnête au sujet de votre absence de cœur.

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) { ... }
    

Une étude plus approfondie

59
Suragch

Cet avertissement vous indique de remplacer performClick

@Override
 public boolean performClick() {
  // Calls the super implementation, which generates an AccessibilityEvent
        // and calls the onClick() listener on the view, if any
        super.performClick();

        // Handle the action for the custom click here

        return true;
 }

Mais ce n'est pas obligatoire. Comme j'ai créé un bouton personnalisé et qu'il fonctionne assez bien, je suis également confronté à cet avertissement.

16
Xar E Ahmer

Le onTouchEvent n'est pas appelé par certains services d'accessibilité, comme expliqué en cliquant sur le lien "plus ..." dans les détails de l'avertissement.

Il vous recommande de remplacer performClick pour l'action souhaitée, ou au moins de la remplacer à côté de votre onTouchEvent.

Si votre code convient mieux à l'événement tactile, vous pouvez utiliser quelque chose de similaire à:

@Override
public boolean performClick() {
    if (actionNotAlreadyExecuted) {
        MotionEvent myEvent = MotionEvent.obtain(long downTime, long eventTime, int action, float x, float y, int metaState);
        onTouch(myView, myEvent);
    }
    return true; // register it has been handled
}

Plus d'informations sur l'accès aux événements tactiles via le code sont disponibles sur déclencher un événement tactile par programmation

2
Abandoned Cart