web-dev-qa-db-fra.com

Atteindre l'élément d'ondulation du menu de la barre d'outils

J'essaie d'utiliser padding pour augmenter la zone tactile d'un bouton. j'utilise

<ImageButton
    Android:paddingRight="32dp"
    Android:paddingEnd="32dp"
    Android:id="@+id/confirm_image_button"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_alignParentEnd="true"
    Android:layout_alignParentRight="true"
    Android:layout_centerVertical="true"
    Android:background="?selectableItemBackgroundBorderless"
    Android:src="?attr/confirmIcon" />

La zone de clic est agrandie. Mais, selectableItemBackgroundBorderless l'effet de clic ne s'affiche plus comme un cercle parfait.

enter image description here


J'essaie d'utiliser la technique duplicateParentState pour surmonter.

<FrameLayout
    Android:clickable="true"
    Android:paddingRight="32dp"
    Android:paddingEnd="32dp"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_alignParentEnd="true"
    Android:layout_alignParentRight="true"
    Android:layout_centerVertical="true">
    <ImageButton
        Android:duplicateParentState="true"
        Android:id="@+id/confirm_image_button"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:background="?selectableItemBackgroundBorderless"
        Android:src="?attr/confirmIcon" />
</FrameLayout>

enter image description here

Maintenant,

  1. La zone cliquée est agrandie.
  2. L'effet de cercle selectableItemBackgroundBorderless est un cercle parfait.

Cependant, il semble avoir un comportement étrange. Lorsque je clique sur la zone réelle du ImageButton, l'effet de pressage circulaire n'est pas affiché.

enter image description here

Puis-je savoir pourquoi il en est ainsi, et comment puis-je le surmonter? J'ai testé en utilisant l'API 26.

Remarque, j'essaie d'éviter d'utiliser la technique TouchDelegate, sauf si je suis obligé de le faire, car cela rend notre code plus compliqué.


Information additionnelle

Voici le comportement correct, présenté par le bouton pour Toolbar.

L'effet d'entraînement apparaît lorsque la zone de clic est en dehors du bouton

enter image description here

L'effet d'entraînement apparaît lorsque la zone de clic se trouve dans le bouton

enter image description here

Cependant, je n'ai aucune idée de la façon dont ils mettent en œuvre un tel comportement.

14
Cheok Yan Cheng

Après avoir passé un peu de temps, j'ai finalement trouvé comment fonctionne le "mystère de la barre d'outils" . C'est ActionMenuItemView, qui est affiché sur la barre d'outils. Et vous pouvez voir qu'à l'intérieur fichier xml il a style="?attr/actionButtonStyle" s'y est appliqué. ?attr/actionButtonStyle Correspond à Widget.Material.ActionButton et dans ce style, nous pouvons voir <item name="background">?attr/actionBarItemBackground</item>.

Si vous souhaitez appliquer le même effet à votre ImageButton, alors tout ce que vous avez à faire est d'appliquer Android:background="?attr/actionBarItemBackground" à elle. Ainsi, ayant la disposition xml suivante:

<ImageButton
    Android:id="@+id/confirm_image_button"
    Android:layout_width="50dp"
    Android:layout_height="50dp"
    Android:background="?attr/actionBarItemBackground"
    Android:src="@drawable/ic_check_black_24dp" />

Vous recevrez cette sortie:

enter image description here

Activé "Afficher les limites de la disposition" pour que les limites réelles de ImageButton soient visibles

Si vous êtes curieux de savoir ce que ?attr/actionBarItemBackground représente en fait, voici :

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:color="?attr/colorControlHighlight"
    Android:radius="20dp" />

Ainsi, vous pouvez créer votre dessinable et l'appliquer comme arrière-plan à ImageButton.

23
azizbekian

Les ondulations sur Android 5.0 commencent au centre de chaque vue. Selon les règles Material Design, les ondulations doivent commencer là où l'événement tactile se produit, de sorte qu'elles semblent s'écouler vers l'extérieur du doigt. Ce problème a été résolu dans Android 5.1, mais c'était un bogue dans Android 5.0

Pour résoudre ce problème, vous devez utiliser la méthode setHotspot(), ajoutée à Drawable dans l'API niveau 21. setHotspot() fournit au drawable un "point chaud ”, et RippleDrawable l'utilise apparemment comme point d'émanation pour l'effet d'entraînement. setHotspot() prend une paire de valeurs flottantes, probablement en vue d'utiliser setHotspot() à l'intérieur d'un OnTouchListener, comme le MotionEvent rapporte X/Y positions de l'événement tactile avec des valeurs flottantes.

Merci au guide du Busy Coder

Vous pouvez utiliser ce code pour corriger le bogue des ondulations:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
       btn.setOnTouchListener(new View.OnTouchListener() {
          @TargetApi(Build.VERSION_CODES.Lollipop)
          @Override
          public boolean onTouch(View v, MotionEvent event) {
              v.getBackground()
                .setHotspot(event.getX(), event.getY());
              return(false);
          }
     });
}

autre solution:

Comment réaliser une animation d'ondulation en utilisant la bibliothèque de support?

RippleDrawable


une autre solution:

lors de votre premier essai:

J'essaie d'utiliser le rembourrage pour augmenter la zone tactile d'un bouton.

vous pouvez utiliser ce code pour mettre le centre de l'ondulation au centre de ImageButton:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
           btn.setOnTouchListener(new View.OnTouchListener() {
              @TargetApi(Build.VERSION_CODES.Lollipop)
              @Override
              public boolean onTouch(View v, MotionEvent event) {
                  v.getBackground()
                    .setHotspot(v.getWidth()/2f, v.getHeight()/2f);//setting hot spot at center of ImageButton
                  return(false);
              }
         });
    }

peut-être une autre solution lorsque FrameLayout encapsule ImageButton:

comme tu dis:

Cependant, il semble avoir un comportement étrange. Lorsque je clique sur la zone réelle de l'ImageButton, l'effet de presse circulaire n'est pas affiché.

une solution à ce problème peut être d'ajouter frameLayout.performClick() dans la méthode onClick() de ImageButton

ou vous pouvez simuler une touche sur frameLayout comme ce code lorsque ImageButton est cliqué:

// Obtain MotionEvent object
long downTime = SystemClock.uptimeMillis();
long eventTime = SystemClock.uptimeMillis() + 100;
float x = 0.0f;// or x = frameLayout.getWidth()/2f
float y = 0.0f;// or y = frameLayout.getHeight()/2f
// List of meta states found here: developer.Android.com/reference/Android/view/KeyEvent.html#getMetaState()
int metaState = 0;
MotionEvent motionEvent = MotionEvent.obtain(
    downTime, 
    eventTime, 
    MotionEvent.ACTION_UP, 
    x, 
    y, 
    metaState
);

// Dispatch touch event to view
frameLayout.dispatchTouchEvent(motionEvent);
2
Bahman

Je soupçonne que votre problème réside dans le fait que vous avez un ImageButton et pas seulement un ImageView. Le style par défaut pour le ImageButton est:

<style name="Widget.ImageButton">
    <item name="Android:focusable">true</item>
    <item name="Android:clickable">true</item>
    <item name="Android:scaleType">center</item>
    <item name="Android:background">@Android:drawable/btn_default</item>
</style>

L'arrière-plan a été remplacé, mais vous avez quand même laissé l'élément cliquable et focalisable. Dans votre exemple, l'élément réel actif est le conteneur, vous pouvez donc soit utiliser un ImageView normal, soit définir l'élément de manière à ce qu'il ne soit ni cliquable ni focalisable manuellement. Il est également important de définir l'écouteur de clics sur le conteneur, et non le bouton/l'image lui-même, car il deviendra cliquable.

Ceci est une variante:

<FrameLayout
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:paddingEnd="48dp"
    Android:focusable="true"
    Android:clickable="true"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    >

    <ImageButton
        Android:duplicateParentState="true"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:background="?selectableItemBackgroundBorderless"
        Android:duplicateParentState="true"
        Android:src="@drawable/ic_check_black_24dp"
        Android:clickable="false"
        Android:focusable="false"
        />
</FrameLayout>

L'autre est le même, sauf que la vue à l'intérieur du FrameLayout est:

<ImageView
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:background="?selectableItemBackgroundBorderless"
    Android:duplicateParentState="true"
    Android:src="@drawable/ic_check_black_24dp"
    />

Voici la preuve visuelle:

screen capture

Cela a été capturé sur un émulateur exécutant Android 5.0.2. J'ai essayé cela sur mon téléphone 8.0, ça marche aussi.

1
Malcolm

Si vous utilisez Android:duplicateParentState="true", Définissez simplement Android:clickable="false" Sur votre ImageButton mettra votre Button en surbrillance lorsque vous cliquez dessus et son parent

<ImageButton
       ...
       Android:duplicateParentState="true"
       Android:clickable="false"
  />

Explication

Depuis le document

Lorsque la duplication est activée, cette vue tire son état de dessin de son parent plutôt que de ses propres propriétés internes

=> Afficher la base de changement d'état sur l'état parent. La valeur par défaut clickable de ImageButton est vraie => lorsque vous cliquez sur ImageButton, l'état de parent n'est pas modifié => ImageButton n'est pas mis en surbrillance

=> Définir clickable=false Résoudra le problème

QUELQUE NOTE

1) N'oubliez pas de définir clickable=true Sur son parent
2) Pour TextView, ImageView (ou une vue qui a cliquable = false par défaut), vous n'avez pas besoin de définir clickable=false
3) Soyez prudent lorsque vous utilisez setOnClickListener

public void setOnClickListener(@Nullable OnClickListener l) {
    if (!isClickable()) {
        setClickable(true);
    }
    getListenerInfo().mOnClickListener = l;
}

Lorsque vous utilisez setOnClickListener, il sera également setClickable(true) dans ce cas, vous devez uniquement définir le setOnClickListener pour sa disposition parent

1
Phan Van Linh