Android RecyclerView侧滑返回后删除按钮无法清除的问题求助
侧滑归档后删除按钮残留问题解决方案
我来帮你搞定这个侧滑后删除按钮残留的问题~你遇到的情况是:实现类似Gmail的侧滑归档操作时,滑动过程中能正常绘制背景和删除按钮,但侧滑返回原位后,按钮图像却留在界面上没有消失。这是因为ItemTouchHelper的绘制逻辑没有在滑动结束后正确清除之前的绘制内容。
先看一下你提供的代码:
class SwipeController extends Callback { private Paint p = new Paint(); private Context context; public SwipeController(Context context) { this.context = context; } @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { return makeMovementFlags(0, LEFT); } @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { return false; } @Override public int convertToAbsoluteDirection(int flags, int layoutDirection) { Log.d("convertToDirection","convertToAbsoluteDirection Executed"); return super.convertToAbsoluteDirection(flags, layoutDirection); } @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { int position = viewHolder.getAdapterPosition(); if (direction == ItemTouchHelper.LEFT){ //adapter.removeItem(position);remove item from adapter } } @Override public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { Bitmap icon; if(actionState == ItemTouchHelper.ACTION_STATE_SWIPE){ View itemView = viewHolder.itemView; float height = (float) itemView.getBottom() - (float) itemView.getTop(); float width = height / 3; try{ if(dX > 0){ p.setColor(Color.parseColor("#388E3C")); RectF background = new RectF((float) itemView.getLeft(), (float) itemView.getTop(), dX,(float) itemView.getBottom()); c.drawRect(background,p); icon = BitmapFactory.decodeResource(context.getResources(), R.drawable.voicemaildelte); RectF icon_dest = new RectF((float) itemView.getLeft() + width ,(float) itemView.getTop() + width,(float) itemView.getLeft()+ 2*width,(float)itemView.getBottom() - width); c.drawBitmap(icon,null,icon_dest,p); } else { p.setColor(Color.parseColor("#D32F2F")); RectF background = new RectF((float) itemView.getRight() + dX, (float) itemView.getTop(),(float) itemView.getRight(), (float) itemView.getBottom()); c.drawRect(background,p); icon = BitmapFactory.decodeResource(context.getResources(), R.drawable.voicemaildelte); RectF icon_dest = new RectF((float) itemView.getRight() - 2*width ,(float) itemView.getTop() + width,(float) itemView.getRight() - width,(float)itemView.getBottom() - width); c.drawBitmap(icon,null,icon_dest,p); } }catch(Exception e){ e.printStackTrace(); } } super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } }
问题原因分析
- 绘制逻辑不完整:你的
onChildDraw只在ACTION_STATE_SWIPE状态下处理绘制,但滑动结束后(比如手指松开让Item回到原位),这个方法仍会被调用,但此时actionState不再是SWIPE,你没有做任何清除操作,导致之前绘制的按钮残留。 - 缺少滑动结束后的视图重置:没有重写
clearView方法,这个方法是滑动操作完全结束后的回调,专门用来恢复视图状态,正好可以用来清除残留。 - 重复解码Bitmap:每次绘制都重新解码图标资源,不仅性能差,还可能导致绘制对象无法正确回收。
解决方案(修改后的代码)
class SwipeController extends Callback { private Paint p = new Paint(); private Context context; private Bitmap deleteIcon; // 提前初始化图标,避免重复解码 public SwipeController(Context context) { this.context = context; // 提前初始化删除图标,开启画笔抗锯齿 deleteIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.voicemaildelte); p.setAntiAlias(true); } @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { return makeMovementFlags(0, LEFT); } @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { return false; } @Override public int convertToAbsoluteDirection(int flags, int layoutDirection) { Log.d("convertToDirection","convertToAbsoluteDirection Executed"); return super.convertToAbsoluteDirection(flags, layoutDirection); } @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { int position = viewHolder.getAdapterPosition(); if (direction == ItemTouchHelper.LEFT){ //adapter.removeItem(position);remove item from adapter } } @Override public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { // 先调用super,让系统先绘制默认滑动效果,再叠加自定义内容 super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); if(actionState == ItemTouchHelper.ACTION_STATE_SWIPE && isCurrentlyActive){ View itemView = viewHolder.itemView; float height = (float) itemView.getBottom() - (float) itemView.getTop(); float width = height / 3; // 只处理左滑逻辑(因为你的movementFlags只允许LEFT) if(dX < 0){ p.setColor(Color.parseColor("#D32F2F")); RectF background = new RectF((float) itemView.getRight() + dX, (float) itemView.getTop(),(float) itemView.getRight(), (float) itemView.getBottom()); c.drawRect(background,p); RectF icon_dest = new RectF((float) itemView.getRight() - 2*width ,(float) itemView.getTop() + width,(float) itemView.getRight() - width,(float)itemView.getBottom() - width); c.drawBitmap(deleteIcon,null,icon_dest,p); } } } // 重写clearView,滑动结束后强制视图重绘,清除残留 @Override public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); viewHolder.itemView.invalidate(); } }
关键修改点说明
- 提前初始化图标:把Bitmap解码移到构造方法中,避免每次绘制都重复解码,提升性能。
- 调整绘制顺序:先调用
super.onChildDraw,让系统先绘制默认的滑动背景,再叠加我们的自定义按钮,这样滑动结束时系统的绘制会覆盖残留内容。 - 重写clearView:在滑动完全结束后调用
itemView.invalidate(),强制视图重绘,彻底清除所有残留的绘制内容。 - 精简逻辑:去掉了无用的右滑处理,因为你的
getMovementFlags只允许左滑操作,dX>0的情况不会出现。
内容的提问来源于stack exchange,提问作者Jeeva
相关产品推荐
相关产品推荐

