Tout d’abord, j’ai vu cette question: Ajouter un fond coloré avec un texte/une icône sous la rangée balayée lorsqu’on utilise RecyclerView d’Android
Cependant, même si le titre indique "avec texte/icône", la réponse ne couvre que l'utilisation de l'objet Canvas
pour dessiner un rectangle.
Maintenant, j'ai dessiné un rectangle vert implémenté; Cependant, je veux rendre plus évident ce qui va se passer en ajoutant le bouton "Terminé", comme sur la capture d'écran suivante.
J'ai créé une vue personnalisée:
public class ChoosePlanView extends LinearLayout{
public ChoosePlanView(Context context) {
super(context);
init();
}
public ChoosePlanView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ChoosePlanView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init(){
View v = inflate(getContext(), R.layout.choose_plan_view, null);
addView(v);
}
}
Et la mise en page est très simple, composée d'un fond vert et de l'icône:
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:orientation="vertical"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:background="@color/choose_green">
<ImageView
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:id="@+id/doneIcon"
Android:layout_gravity="center_horizontal|right"
Android:src="@mipmap/done"/>
</LinearLayout>
J'ai essayé d'utiliser la méthode OnChildDraw
:
// ChoosePlanView choosePlanView; <- that was declared and initialized earlier, so I don't have to create a new ChoosePlanView everytime in OnChildDraw();
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
/*
onMove, onSwiped omitted
*/
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
View itemView = viewHolder.itemView;
if(dX < 0){
choosePlanView.invalidate();
//choosePlanView.setBackgroundResource(R.color.delete_red);
choosePlanView.measure(itemView.getWidth(), itemView.getHeight());
choosePlanView.layout(itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom());
c.save();
c.translate(choosePlanView.getRight() + (int) dX, viewHolder.getAdapterPosition()*itemView.getHeight());
choosePlanView.draw(c);
c.restore();
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
}
Et ça marche un peu (dessine ma ChoosePlanView
), mais il y a deux problèmes.
Tout d'abord, il ne dessine qu'un petit carré vert entourant mon icône "Terminé" (la partie rouge de la vue de la capture d'écran ci-dessous provient de la ligne commentée choosePlanView.setBackgroundResource(R.color.delete_red);
). La vue a la largeur correcte cependant, lorsqu'elle est cochée dans le débogueur.
La deuxième chose est l'endroit où l'icône est placée. J'ai précisé que je souhaitais qu'il soit centré horizontalement et toujours "collé" sur le côté droit de la vue. Quoi qu’il en soit, lorsque l’élément RecyclerView
est balayé, l’arrière-plan rouge indique qu’il n’est pas placé au centre non plus.
J'ai essayé d'ajouter la ligne choosePlanView.invalidate()
, parce que je pensais que cela se produisait, car la vue avait été créée plus tôt, mais il semble que le traçage ne change rien.
L'utilisation de l'interface ItemTouchUiUtil offre une solution robuste à cela. Il vous permet d’avoir une vue de premier plan et d’arrière-plan dans votre ViewHolder et de déléguer la gestion de tous les balayages à la vue de premier plan. Voici un exemple que j’utilise pour balayer juste à supprimer.
Dans votre ItemTouchHelper.Callback :
public class ClipItemTouchHelper extends ItemTouchHelper.SimpleCallback {
...
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (viewHolder != null){
final View foregroundView = ((ClipViewHolder) viewHolder).clipForeground;
getDefaultUIUtil().onSelected(foregroundView);
}
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder, float dX, float dY,
int actionState, boolean isCurrentlyActive) {
final View foregroundView = ((ClipViewHolder) viewHolder).clipForeground;
drawBackground(viewHolder, dX, actionState);
getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,
actionState, isCurrentlyActive);
}
@Override
public void onChildDrawOver(Canvas c, RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder, float dX, float dY,
int actionState, boolean isCurrentlyActive) {
final View foregroundView = ((ClipViewHolder) viewHolder).clipForeground;
drawBackground(viewHolder, dX, actionState);
getDefaultUIUtil().onDrawOver(c, recyclerView, foregroundView, dX, dY,
actionState, isCurrentlyActive);
}
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder){
final View backgroundView = ((ClipViewHolder) viewHolder).clipBackground;
final View foregroundView = ((ClipViewHolder) viewHolder).clipForeground;
// TODO: should animate out instead. how?
backgroundView.setRight(0);
getDefaultUIUtil().clearView(foregroundView);
}
private static void drawBackground(RecyclerView.ViewHolder viewHolder, float dX, int actionState) {
final View backgroundView = ((ClipViewHolder) viewHolder).clipBackground;
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
//noinspection NumericCastThatLosesPrecision
backgroundView.setRight((int) Math.max(dX, 0));
}
}
}
Et dans votre fichier de présentation, définissez les vues de premier plan et d’arrière-plan:
<LinearLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:tools="http://schemas.Android.com/tools"
Android:id="@+id/clipRow"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:orientation="vertical">
<View style="@style/Divider"/>
<FrameLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:tools="http://schemas.Android.com/tools"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:background="@drawable/row_selector"
>
<RelativeLayout
Android:id="@+id/clipBackground"
Android:layout_width="0dp"
Android:layout_height="match_parent"
Android:background="@color/swipe_bg"
tools:layout_width="match_parent">
<ImageView
Android:layout_width="32dp"
Android:layout_height="32dp"
Android:layout_alignParentLeft="true"
Android:layout_alignParentStart="true"
Android:layout_centerVertical="true"
Android:layout_marginLeft="12dp"
Android:layout_marginStart="12dp"
Android:focusable="false"
Android:src="@drawable/ic_delete_24dp"
tools:ignore="ContentDescription"/>
</RelativeLayout>
<RelativeLayout
Android:id="@+id/clipForeground"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:paddingBottom="@dimen/clip_vertical_margin"
Android:paddingLeft="@dimen/clip_horizontal_margin"
Android:paddingRight="@dimen/clip_horizontal_margin"
Android:paddingTop="@dimen/clip_vertical_margin" >
<CheckBox
Android:id="@+id/favCheckBox"
Android:layout_width="@dimen/view_image_size"
Android:layout_height="@dimen/view_image_size"
Android:layout_alignParentLeft="true"
Android:layout_alignParentStart="true"
Android:background="@Android:color/transparent"
Android:button="@drawable/ic_star_outline_24dp"
Android:clickable="true"
Android:contentDescription="@string/content_favorite"
/>
...
</RelativeLayout>
</FrameLayout>
</LinearLayout>
Au lieu de tirer dessus, je pense que vous devriez utiliser viewType
Implémente getItemViewType(int position)
dans l'adaptateur, en renvoyant différents types, par exemple. 0 pour la normale, 1 pour swipped
et changez onCreateViewHolder etc. pour retourner une mise en page différente sur différents viewType
J'ai réussi à faire en sorte que cela fonctionne avec la couleur et une image en étendant la réponse fournie par Sanvywell à l'adresse: Ajout d'un fond coloré avec du texte/une icône sous la rangée balayée lors de l'utilisation de RecyclerView d'Android
Je ne recréerai pas l'intégralité du post (vous pouvez suivre le lien). En résumé, je n’ai pas créé de vue complète à rendre lors du balayage, mais j’ai simplement ajouté une image bitmap au canevas, positionnée de manière appropriée sur l’élément RecyclerView.