Après avoir ajouté de la couleur à un bouton Android, il perd son effet d'entraînement qui donne à l'utilisateur l'impression qu'il y a un clic réactif. Comment résoudre ce problème? J'ai cherché dans de nombreuses solutions mais je ne pouvais pas n'en trouve pas un qui ne soit pas ambigu.
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:tools="http://schemas.Android.com/tools"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
tools:context=".ClockInOutFragment">
<AnalogClock
Android:id="@+id/clock"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_alignParentEnd="true"
Android:layout_alignParentRight="true"
Android:layout_alignParentTop="true"
Android:layout_toRightOf="@+id/date_and_time"/>
<RelativeLayout
Android:id="@+id/date_and_time"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_margin="10dp">
<TextView
Android:id="@+id/time_digits"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:text="12:10"
Android:textSize="45sp"/>
<TextView
Android:id="@+id/am_pm"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_alignBaseline="@+id/time_digits"
Android:layout_toRightOf="@+id/time_digits"
Android:paddingLeft="3dp"
Android:text="PM"
Android:textSize="20sp"/>
<TextView
Android:id="@+id/date"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_below="@+id/time_digits"
Android:text="Mon, July 11"
Android:textSize="22sp"/>
</RelativeLayout>
<!--Clock in and out buttons-->
<LinearLayout
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:layout_alignParentBottom="true"
Android:layout_centerHorizontal="true"
Android:orientation="horizontal">
<Button
Android:id="@+id/textView3"
Android:layout_width="0dp"
Android:layout_height="56dp"
Android:layout_weight="1"
Android:background="#4CAF50"
Android:gravity="center"
Android:text="Clock In"
Android:textAppearance="?android:attr/textAppearanceLarge"
Android:textColor="#FFFFFF"/>
<!--Divider between the clock in and out button-->
<View
Android:layout_width="1dp"
Android:layout_height="match_parent"
Android:background="#B6B6B6"/>
<Button
Android:id="@+id/textView4"
Android:layout_width="0dp"
Android:layout_height="56dp"
Android:layout_weight="1"
Android:background="#FF5252"
Android:gravity="center"
Android:text="Clock Out"
Android:textAppearance="?android:attr/textAppearanceLarge"
Android:textColor="#FFFFFF"/>
</LinearLayout>
</RelativeLayout>
Vous pouvez ajouter l'effet d'ondulation et la couleur d'arrière-plan avec une ondulation supplémentaire pouvant être dessinée:
votre mise en page:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:orientation="vertical" Android:layout_width="match_parent"
Android:layout_height="match_parent">
<Button
Android:id="@+id/button_connect"
Android:layout_width="fill_parent"
Android:layout_height="wrap_content"
Android:layout_marginTop="20dip"
Android:fontFamily="sans-serif"
Android:text="Connect"
Android:background="@drawable/ripple"
Android:textColor="#FFFFFF"
Android:textSize="18sp" />
</LinearLayout>
ripple.xml (c'est ici que vous pouvez ajouter une couleur d'arrière-plan en plus de l'effet d'entraînement):
<?xml version="1.0" encoding="utf-8"?>
<!-- in drawable folder-->
<ripple
xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:color="?android:colorControlHighlight">
<item Android:id="@Android:id/mask">
<shape Android:shape="rectangle">
<solid Android:color="?android:colorAccent" />
</shape>
</item>
<item>
<shape Android:shape="rectangle">
<!-- put your background color here-->
<solid Android:color="@color/default_color" />
</shape>
</item>
</ripple>
Une manière très simple et directe de procéder consiste à définir ?attr/selectableItemBackground
à Android:foreground
attribut de votre bouton. Le XML suivant est parfaitement valide et fonctionne
<Button
Android:id="@+id/btn"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:background="@Android:color/white"
Android:foreground="?attr/selectableItemBackground"/>
Ne changez pas l'arrière-plan de Button. Changez le thème.
<style name="ButtonGray">
<item name="colorButtonNormal">@color/gray</item>
</style>
et dans votre fichier xml
<Button
Android:id="@+id/accept_button"
Android:layout_height="wrap_content"
Android:layout_width="0dp"
Android:layout_weight="1"
Android:text="@string/button_accept_group"
Android:theme="@style/ButtonGray"/>
Ou vous pouvez l'ajouter dans votre thème d'application principal
<style name="AppTheme"
parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorButtonNormal">@color/primary_color</item>
</style>
Et pas besoin de changer l'arrière-plan du bouton.
Si vous voulez un arrière-plan totalement personnalisé, vous devez créer votre sélecteur. Et vous pouvez y définir un effet d'entraînement.
Utilisez simplement:
Android:backgroundTint="#4CAF50"
Au lieu de:
Android:background="#4CAF50"
N'oubliez pas de changer votre Button
en Android.support.v7.widget.AppCompatButton
En fait, vous pouvez utiliser <layer-list>
De dessinables pour combiner l'effet d'entraînement avec tout autre dessinable. C'est une solution universelle également pour le pré-lolipop: je l'ai testé dans de nombreuses configurations.
Le seul problème est que pré-lolipop se bloque lorsque ?selectableItemBackground
Apparaît à l'intérieur de <layer-list>
, Nous devons donc créer LayerDrawable par programmation.
Une solution simple très rapide ressemble à:
Spécifiez pour votre vue
Android:background="?selectableItemBackground"
Puis, n'importe où dans le code, créez mySpecialDrawable et faites l'astuce:
Drawable[] layers = {mySpecialDrawable, getBackground()};
setBackgroundDrawable(new LayerDrawable(layers).mutate());
Veuillez noter que .mutate()
pour LayeredDrawable est essentiel ici!
Une solution plus complexe pourrait être utile lorsque vous avez déjà votre vue personnalisée et que vous préférez étendre ses fonctionnalités et sa compatibilité plutôt que d'ajouter FrameLayout vide en tant que parent.
Dans attrs.xml, mettez:
<resources>
<declare-styleable name="MyView">
<attr name="selectableBackground" format="reference"/>
<attr name="backgroundDrawable" format="reference"/>
</declare-styleable>
</resources>
puis à l'intérieur de votre classe View-descendant:
private Drawable selectableBackground;
private Drawable backgroundDrawable;
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
try {
TypedArray attributeArray;
attributeArray = context.obtainStyledAttributes(attrs, R.styleable.MyView);
int id = attributeArray.getResourceId(R.styleable.MyView_selectableBackground, -1);
if (id != -1) {
selectableBackground = ResourcesCompat.getDrawable(getResources(), id, context.getTheme());
}
id = attributeArray.getResourceId(R.styleable.MyView_backgroundDrawable, -1);
if (id != -1) {
backgroundDrawable = ResourcesCompat.getDrawable(getResources(), id, context.getTheme());
}
constructBackground();
attributeArray.recycle();
} catch (Exception e) {
Log.e(this.toString(), "Attributes initialization error", e);
throw e;
}
}
void setSelectableBackground(Drawable drawable) {
selectableBackground = drawable;
constructBackground();
}
void setDrawable(Drawable drawable) {
backgroundDrawable = drawable;
constructBackground();
}
private void constructBackground() {
if (selectableBackground != null) {
if (backgroundDrawable != null) {
Drawable[] layers = {backgroundDrawable, selectableBackground};
setBackgroundDrawable(new LayerDrawable(layers).mutate()); // Both, using layers
} else setBackgroundDrawable(selectableBackground); // Selectable only
} else setBackgroundDrawable(backgroundDrawable); // Background only or null
}
Je préfère cette approche car elle n'a pas de problème comme l'attribut Android:foreground
Qui est 23+ ou une surcharge supplémentaire pour inclure des vues cliquables dans FrameLayout.