You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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); 
    } 
}

问题原因分析

  1. 绘制逻辑不完整:你的onChildDraw只在ACTION_STATE_SWIPE状态下处理绘制,但滑动结束后(比如手指松开让Item回到原位),这个方法仍会被调用,但此时actionState不再是SWIPE,你没有做任何清除操作,导致之前绘制的按钮残留。
  2. 缺少滑动结束后的视图重置:没有重写clearView方法,这个方法是滑动操作完全结束后的回调,专门用来恢复视图状态,正好可以用来清除残留。
  3. 重复解码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();
    } 
}

关键修改点说明

  1. 提前初始化图标:把Bitmap解码移到构造方法中,避免每次绘制都重复解码,提升性能。
  2. 调整绘制顺序:先调用super.onChildDraw,让系统先绘制默认的滑动背景,再叠加我们的自定义按钮,这样滑动结束时系统的绘制会覆盖残留内容。
  3. 重写clearView:在滑动完全结束后调用itemView.invalidate(),强制视图重绘,彻底清除所有残留的绘制内容。
  4. 精简逻辑:去掉了无用的右滑处理,因为你的getMovementFlags只允许左滑操作,dX>0的情况不会出现。

内容的提问来源于stack exchange,提问作者Jeeva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:11:48