行业资讯
Android RecyclerView拖拽排序与侧滑删除实现详解
1. RecyclerView拖拽与侧滑删除的实现原理ItemTouchHelper是Android Support库中一个强大的工具类专门用于处理RecyclerView的触摸事件。它通过ItemTouchHelper.Callback抽象类来定义各种触摸行为的响应逻辑主要包括三个核心机制拖拽排序通过重写getMovementFlags()和onMove()方法实现侧滑删除通过重写getMovementFlags()和onSwiped()方法实现触摸反馈通过重写onSelectedChanged()和clearView()方法实现视觉反馈注意ItemTouchHelper需要AndroidX库支持最低兼容到API 7(Android 2.1)2. 基础实现步骤2.1 布局文件配置首先需要准备RecyclerView及其item的布局文件!-- activity_main.xml -- androidx.recyclerview.widget.RecyclerView android:idid/recyclerView android:layout_widthmatch_parent android:layout_heightmatch_parent/ !-- item_layout.xml -- FrameLayout xmlns:androidhttp://schemas.android.com/apk/res/android android:layout_widthmatch_parent android:layout_height56dp android:background?attr/selectableItemBackground TextView android:idid/item_text android:layout_widthmatch_parent android:layout_heightmatch_parent android:gravitycenter_vertical android:paddingStart16dp android:paddingEnd16dp/ /FrameLayout2.2 实现ItemTouchHelper.Callback创建自定义Callback类是实现功能的核心public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback { private final ItemTouchHelperAdapter mAdapter; public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) { mAdapter adapter; } Override public boolean isLongPressDragEnabled() { return true; // 启用长按拖拽 } Override public boolean isItemViewSwipeEnabled() { return true; // 启用侧滑删除 } Override public int getMovementFlags(NonNull RecyclerView recyclerView, NonNull RecyclerView.ViewHolder viewHolder) { // 设置支持的拖拽和滑动方向 int dragFlags ItemTouchHelper.UP | ItemTouchHelper.DOWN; // 垂直拖拽 int swipeFlags ItemTouchHelper.START | ItemTouchHelper.END; // 左右滑动 return makeMovementFlags(dragFlags, swipeFlags); } Override public boolean onMove(NonNull RecyclerView recyclerView, NonNull RecyclerView.ViewHolder source, NonNull RecyclerView.ViewHolder target) { mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition()); return true; } Override public void onSwiped(NonNull RecyclerView.ViewHolder viewHolder, int direction) { mAdapter.onItemDismiss(viewHolder.getAdapterPosition()); } Override public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { // 当item被选中时改变背景色 if (actionState ! ItemTouchHelper.ACTION_STATE_IDLE) { viewHolder.itemView.setBackgroundColor(Color.LTGRAY); } super.onSelectedChanged(viewHolder, actionState); } Override public void clearView(NonNull RecyclerView recyclerView, NonNull RecyclerView.ViewHolder viewHolder) { // 操作完成后恢复默认背景 viewHolder.itemView.setBackgroundColor(Color.WHITE); super.clearView(recyclerView, viewHolder); } }2.3 适配器接口定义定义适配器接口确保数据操作的一致性public interface ItemTouchHelperAdapter { // 移动item void onItemMove(int fromPosition, int toPosition); // 删除item void onItemDismiss(int position); }2.4 RecyclerView适配器实现实现带有拖拽和删除功能的适配器public class MyAdapter extends RecyclerView.AdapterMyAdapter.ViewHolder implements ItemTouchHelperAdapter { private ListString mItems; public MyAdapter(ListString items) { mItems items; } Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_layout, parent, false); return new ViewHolder(view); } Override public void onBindViewHolder(ViewHolder holder, int position) { holder.textView.setText(mItems.get(position)); } Override public int getItemCount() { return mItems.size(); } Override public void onItemMove(int fromPosition, int toPosition) { // 交换数据位置 Collections.swap(mItems, fromPosition, toPosition); // 通知适配器item移动 notifyItemMoved(fromPosition, toPosition); } Override public void onItemDismiss(int position) { // 移除数据 mItems.remove(position); // 通知适配器item移除 notifyItemRemoved(position); } public static class ViewHolder extends RecyclerView.ViewHolder { public TextView textView; public ViewHolder(View itemView) { super(itemView); textView itemView.findViewById(R.id.item_text); } } }2.5 绑定到RecyclerView最后将ItemTouchHelper绑定到RecyclerViewRecyclerView recyclerView findViewById(R.id.recyclerView); MyAdapter adapter new MyAdapter(dataList); recyclerView.setAdapter(adapter); recyclerView.setLayoutManager(new LinearLayoutManager(this)); ItemTouchHelper.Callback callback new SimpleItemTouchHelperCallback(adapter); ItemTouchHelper touchHelper new ItemTouchHelper(callback); touchHelper.attachToRecyclerView(recyclerView);3. 高级功能实现3.1 自定义拖拽和滑动行为通过修改Callback类可以实现更复杂的行为控制Override public float getSwipeThreshold(NonNull RecyclerView.ViewHolder viewHolder) { // 滑动阈值默认0.5f return 0.8f; // 需要滑动更远距离才会触发删除 } Override public float getMoveThreshold(NonNull RecyclerView.ViewHolder viewHolder) { // 移动阈值默认0.5f return 0.3f; // 更容易触发位置交换 } Override public long getAnimationDuration(NonNull RecyclerView recyclerView, int animationType, float animateDx, float animateDy) { // 自定义动画时间 return animationType ItemTouchHelper.ANIMATION_TYPE_DRAG ? 300 : 250; }3.2 实现网格布局拖拽对于GridLayoutManager需要调整移动标志Override public int getMovementFlags(NonNull RecyclerView recyclerView, NonNull RecyclerView.ViewHolder viewHolder) { if (recyclerView.getLayoutManager() instanceof GridLayoutManager) { int dragFlags ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; int swipeFlags 0; // 网格布局通常禁用滑动删除 return makeMovementFlags(dragFlags, swipeFlags); } else { // 线性布局保持原有设置 int dragFlags ItemTouchHelper.UP | ItemTouchHelper.DOWN; int swipeFlags ItemTouchHelper.START | ItemTouchHelper.END; return makeMovementFlags(dragFlags, swipeFlags); } }3.3 添加滑动删除确认提示可以在onSwiped方法中添加删除确认逻辑Override public void onSwiped(NonNull final RecyclerView.ViewHolder viewHolder, int direction) { final int position viewHolder.getAdapterPosition(); new AlertDialog.Builder(viewHolder.itemView.getContext()) .setTitle(确认删除) .setMessage(确定要删除此项吗) .setPositiveButton(确定, (dialog, which) - { mAdapter.onItemDismiss(position); }) .setNegativeButton(取消, (dialog, which) - { notifyItemChanged(position); // 恢复item状态 }) .setOnCancelListener(dialog - { notifyItemChanged(position); // 恢复item状态 }) .show(); }4. 性能优化与问题排查4.1 常见问题解决方案拖拽不灵敏问题检查item布局中是否有控件拦截了触摸事件确保item的根布局没有设置clickabletrue尝试调整getMoveThreshold()返回值滑动删除后数据错乱确保在删除数据后调用notifyItemRemoved()避免在删除操作中使用notifyDataSetChanged()检查数据源操作是否正确动画卡顿问题简化item布局层级避免在item中使用复杂自定义View重写getAnimationDuration()调整动画时间4.2 性能优化建议数据操作优化// 使用更高效的数据结构 private ListString mItems new ArrayList(); // 批量操作时使用DiffUtil public void updateItems(ListString newItems) { DiffUtil.DiffResult result DiffUtil.calculateDiff(new MyDiffCallback(mItems, newItems)); mItems.clear(); mItems.addAll(newItems); result.dispatchUpdatesTo(this); }视图复用优化Override public void onViewRecycled(NonNull ViewHolder holder) { // 清理视图状态 holder.itemView.setBackgroundColor(Color.WHITE); super.onViewRecycled(holder); }内存泄漏预防// 在Activity/Fragment销毁时解绑 Override protected void onDestroy() { if (touchHelper ! null) { touchHelper.attachToRecyclerView(null); } super.onDestroy(); }5. 实际应用中的经验分享拖拽手柄实现 很多应用不希望整个item都可拖拽而是通过特定区域如拖动图标触发Override public boolean isLongPressDragEnabled() { return false; // 禁用默认长按拖拽 } // 在ViewHolder中添加拖拽图标点击事件 holder.dragHandle.setOnTouchListener((v, event) - { if (event.getAction() MotionEvent.ACTION_DOWN) { touchHelper.startDrag(holder); } return false; });多类型item处理 当RecyclerView有多种item类型时需要区别处理Override public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) { if (viewHolder.getItemViewType() TYPE_HEADER) { return makeMovementFlags(0, 0); // 不可拖拽和滑动 } else { return makeMovementFlags(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.END); } }自定义滑动背景 实现类似邮件应用的滑动露出背景效果Override public void onChildDraw(NonNull Canvas c, NonNull RecyclerView recyclerView, NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { if (actionState ItemTouchHelper.ACTION_STATE_SWIPE) { View itemView viewHolder.itemView; Paint paint new Paint(); if (dX 0) { // 向右滑动显示绿色背景 paint.setColor(Color.GREEN); c.drawRect(itemView.getLeft(), itemView.getTop(), dX, itemView.getBottom(), paint); } else { // 向左滑动显示红色背景 paint.setColor(Color.RED); c.drawRect(itemView.getRight() dX, itemView.getTop(), itemView.getRight(), itemView.getBottom(), paint); } } super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); }拖拽限制优化 某些场景需要限制拖拽范围或目标位置Override public boolean canDropOver(NonNull RecyclerView recyclerView, NonNull RecyclerView.ViewHolder current, NonNull RecyclerView.ViewHolder target) { // 只允许同类型item之间拖拽 return current.getItemViewType() target.getItemViewType(); } Override public boolean onMove(NonNull RecyclerView recyclerView, NonNull RecyclerView.ViewHolder source, NonNull RecyclerView.ViewHolder target) { // 检查目标位置是否允许放置 if (isPositionValid(target.getAdapterPosition())) { mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition()); return true; } return false; }
郑州网站建设
网页设计
企业官网