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.
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>
Maintenant,
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é.
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é.
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
L'effet d'entraînement apparaît lorsque la zone de clic se trouve dans le bouton
Cependant, je n'ai aucune idée de la façon dont ils mettent en œuvre un tel comportement.
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:
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
.
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 ”, etRippleDrawable
l'utilise apparemment comme point d'émanation pour l'effet d'entraînement.setHotspot()
prend une paire de valeurs flottantes, probablement en vue d'utilisersetHotspot()
à l'intérieur d'unOnTouchListener
, comme leMotionEvent
rapporteX/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?
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);
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:
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.
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"
/>
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
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