首页 文章

如何在没有解雇的情况下在android中的recyclerview上滑动

提问于
浏览
3

我有一个项目,我需要以两种方式在一行上滑动以启动操作 . 向左滑动应从特定列表中删除有问题的项目(不将其从原始列表中删除,因此将其保留在recyclerview数据集中),向右滑动应将相关项目添加到另一个列表(同样不是原始列表) . 我实现了一个ItemTouchHelperAdapter和一个ItemTouchHelperCallback,我可以检测到左/右滑动,但视图会从屏幕上移开,我留下一个空白矩形 . 有任何想法吗?

public interface ItemTouchHelperAdapter {

/**
 * Called when an item has been dismissed by a swipe.
*
* Implementations should call {@link RecyclerView.Adapter#notifyItemRemoved(int)} after * adjusting the underlying data to reflect this removal. * * @param position The position of the item dismissed. * @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder) * @see RecyclerView.ViewHolder#getAdapterPosition() */ void onItemLeftSwipe(int position); void onItemRightSwipe(int position);

}

公共类ItemTouchHelperCallback扩展ItemTouchHelper.Callback {

private final ItemTouchHelperAdapter mAdapter;

public ItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
    mAdapter = adapter;
}

@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    // int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
    int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
    return makeMovementFlags(0, swipeFlags);
}

@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
    return false;
}

@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    if (direction == ItemTouchHelper.START)
        mAdapter.onItemLeftSwipe(viewHolder.getAdapterPosition());
    else if (direction == ItemTouchHelper.END)
        mAdapter.onItemRightSwipe(viewHolder.getAdapterPosition());
    else
        System.out.println("direction: " + direction);
}

@Override
public boolean isLongPressDragEnabled() {
    return false;
}

@Override
public boolean isItemViewSwipeEnabled() {
    return true;
}

}

这是在我的适配器类中:

@Override
public void onItemLeftSwipe(int position) {

    System.out.println("swiped left on " + mDataset.get(position).getName());

}

@Override
public void onItemRightSwipe(int position) {

    System.out.println("swiped right on " + mDataset.get(position).getName());

}

3 回答

  • 3
    public class MainActivity extends AppCompatActivity {
        RecyclerView mRecyclerView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);
            mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
            setUpRecyclerView();
    
        }
        private void setUpRecyclerView() {
            mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
            mRecyclerView.setAdapter(new TestAdapter());
            mRecyclerView.setHasFixedSize(true);
            setUpItemTouchHelper();
            setUpAnimationDecoratorHelper();
        }
        private void setUpItemTouchHelper() {
    
            ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
                Drawable background;
                Drawable xMark;
                int xMarkMargin;
                boolean initiated;
                private void init() {
                    background = new ColorDrawable(Color.WHITE);
                    xMark = ContextCompat.getDrawable(MainActivity.this, R.drawable.ic_clear_24dp);
                    xMark.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP);
                    xMarkMargin = (int) MainActivity.this.getResources().getDimension(R.dimen.ic_clear_margin);
                    initiated = true;
                }
                @Override
                public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
                    return false;
                }
    
                @Override
                public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)
                {
                    int position = viewHolder.getAdapterPosition();
                    TestAdapter testAdapter = (TestAdapter)recyclerView.getAdapter();
                    if (testAdapter.isUndoOn() && testAdapter.isPendingRemoval(position))
                    {
                        return 0;
                    }
                    return super.getSwipeDirs(recyclerView, viewHolder);
                }
    
                @Override
                public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
                    int swipedPosition = viewHolder.getAdapterPosition();
                    TestAdapter adapter = (TestAdapter)mRecyclerView.getAdapter();
                    boolean undoOn = adapter.isUndoOn();
                    if (undoOn) {
                        adapter.pendingRemoval(swipedPosition);
                    } else {
                        adapter.remove(swipedPosition);
                    }
                }
    
                @Override
                public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
                    View itemView = viewHolder.itemView;
                    if (viewHolder.getAdapterPosition() == -1) {
                        return;
                    }
                    if (!initiated) {
                        init();
                    }
                    background.setBounds(itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom());
                    background.draw(c);
    
                    int itemHeight = itemView.getBottom() - itemView.getTop();
                    int intrinsicWidth = xMark.getIntrinsicWidth();
                    int intrinsicHeight = xMark.getIntrinsicWidth();
                    int xMarkLeft = itemView.getRight() - xMarkMargin - intrinsicWidth;
                    int xMarkRight = itemView.getRight() - xMarkMargin;
                    int xMarkTop = itemView.getTop() + (itemHeight - intrinsicHeight)/2;
                    int xMarkBottom = xMarkTop + intrinsicHeight;
                    xMark.setBounds(xMarkLeft, xMarkTop, xMarkRight, xMarkBottom);
                    xMark.draw(c);
                    super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
                }
    
            };
            ItemTouchHelper mItemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
            mItemTouchHelper.attachToRecyclerView(mRecyclerView);
        }
        private void setUpAnimationDecoratorHelper() {
            mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
                Drawable background;
                boolean initiated;
                private void init() {
                    background = new ColorDrawable(Color.RED);
                    initiated = true;
                }
                @Override
                public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    
                    if (!initiated) {
                        init();
                    }
                    if (parent.getItemAnimator().isRunning())
                    {
                        View lastViewComingDown = null;
                        View firstViewComingUp = null;
                        int left = parent.getHeight();
                        int right = parent.getWidth();
                        int top = 0;
                        int bottom = 0;
                        int childCount = parent.getLayoutManager().getChildCount();
                        for (int i = 0; i < childCount; i++) {
                            View child = parent.getLayoutManager().getChildAt(i);
                            if (child.getTranslationY() < 0) {
                                // view is coming down
                                lastViewComingDown = child;
                            } else if (child.getTranslationY() > 0) {
                                // view is coming up
                                if (firstViewComingUp == null) {
                                    firstViewComingUp = child;
                                }
                            }
                        }
    
                        if (lastViewComingDown != null && firstViewComingUp != null) {
                            // views are coming down AND going up to fill the void
                            top = lastViewComingDown.getBottom() + (int) lastViewComingDown.getTranslationY();
                            bottom = firstViewComingUp.getTop() + (int) firstViewComingUp.getTranslationY();
                        } else if (lastViewComingDown != null) {
                            // views are going down to fill the void
                            top = lastViewComingDown.getBottom() + (int) lastViewComingDown.getTranslationY();
                            bottom = lastViewComingDown.getBottom();
                        } else if (firstViewComingUp != null) {
                            // views are coming up to fill the void
                            top = firstViewComingUp.getTop();
                            bottom = firstViewComingUp.getTop() + (int) firstViewComingUp.getTranslationY();
                        }
    
                        background.setBounds(left, top, right, bottom);
                        background.draw(c);
    
                    }
                    super.onDraw(c, parent, state);
                }
    
            });
        }
    
        class TestAdapter extends RecyclerView.Adapter {
    
            private static final int PENDING_REMOVAL_TIMEOUT = 3000; // 3sec
    
            List<String> items;
            List<String> itemsPendingRemoval;
            int lastInsertedIndex; // so we can add some more items for testing purposes
            boolean undoOn; // is undo on, you can turn it on from the toolbar menu
    
            private Handler handler = new Handler(); // hanlder for running delayed runnables
            HashMap<String, Runnable> pendingRunnables = new HashMap<>(); // map of items to pending runnables, so we can cancel a removal if need be
    
            public TestAdapter() {
                items = new ArrayList<>();
                itemsPendingRemoval = new ArrayList<>();
                // let's generate some items
                lastInsertedIndex = 15;
                // this should give us a couple of screens worth
                for (int i=1; i<= lastInsertedIndex; i++) {
                    items.add("Item " + i);
                }
            }
    
            @Override
            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                return new TestViewHolder(parent);
            }
    
            @Override
            public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
                TestViewHolder viewHolder = (TestViewHolder)holder;
                final String item = items.get(position);
    
                if (itemsPendingRemoval.contains(item)) {
                    // we need to show the "undo" state of the row
                    //viewHolder.itemView.setBackgroundColor(Color.RED);
                    viewHolder.itemView.setBackgroundColor(Color.WHITE);
                    viewHolder.titleTextView.setVisibility(View.GONE);
                    //viewHolder.undoButton.setVisibility(View.VISIBLE);
                    viewHolder.undoButton.setVisibility(View.GONE);
                    viewHolder.undoButton.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            // user wants to undo the removal, let's cancel the pending task
                            Runnable pendingRemovalRunnable = pendingRunnables.get(item);
                            pendingRunnables.remove(item);
                            if (pendingRemovalRunnable != null) handler.removeCallbacks(pendingRemovalRunnable);
                            itemsPendingRemoval.remove(item);
                            // this will rebind the row in "normal" state
                            notifyItemChanged(items.indexOf(item));
                        }
                    });
                } else {
                    // we need to show the "normal" state
                    viewHolder.itemView.setBackgroundColor(Color.WHITE);
                    viewHolder.titleTextView.setVisibility(View.VISIBLE);
                    viewHolder.titleTextView.setText(item);
                    viewHolder.undoButton.setVisibility(View.GONE);
                    viewHolder.undoButton.setOnClickListener(null);
                }
            }
    
            @Override
            public int getItemCount() {
                return items.size();
            }
    
            public void addItems(int howMany){
                if (howMany > 0) {
                    for (int i = lastInsertedIndex + 1; i <= lastInsertedIndex + howMany; i++) {
                        items.add("Item " + i);
                        notifyItemInserted(items.size() - 1);
                    }
                    lastInsertedIndex = lastInsertedIndex + howMany;
                }
            }
    
            public void setUndoOn(boolean undoOn) {
                this.undoOn = undoOn;
            }
    
            public boolean isUndoOn() {
                return undoOn;
            }
    
            public void pendingRemoval(int position) {
                final String item = items.get(position);
                if (!itemsPendingRemoval.contains(item)) {
                    itemsPendingRemoval.add(item);
                    // this will redraw row in "undo" state
                    notifyItemChanged(position);
                    // let's create, store and post a runnable to remove the item
                    Runnable pendingRemovalRunnable = new Runnable() {
                        @Override
                        public void run() {
                            remove(items.indexOf(item));
                        }
                    };
                    handler.postDelayed(pendingRemovalRunnable, PENDING_REMOVAL_TIMEOUT);
                    pendingRunnables.put(item, pendingRemovalRunnable);
                }
            }
            public void remove(int position) {
                String item = items.get(position);
                if (itemsPendingRemoval.contains(item)) {
                    itemsPendingRemoval.remove(item);
                }
                if (items.contains(item)) {
                    items.remove(position);
                    notifyItemRemoved(position);
                }
            }
    
            public boolean isPendingRemoval(int position) {
                String item = items.get(position);
                return itemsPendingRemoval.contains(item);
            }
        }
        static class TestViewHolder extends RecyclerView.ViewHolder
        {
            TextView titleTextView;
            Button undoButton;
            public TestViewHolder(ViewGroup parent)
            {
                super(LayoutInflater.from(parent.getContext()).inflate(R.layout.row_view, parent, false));
                titleTextView = (TextView) itemView.findViewById(R.id.title_text_view);
                undoButton = (Button) itemView.findViewById(R.id.undo_button);
            }
        }
    }
    
  • 1

    我不确定这是否是最好的解决方案和最有效的解决方案,但我刚刚删除了该项目并再次添加:

    @Override
    public void onItemLeftSwipe(int position) {
    
        // modify your item as you wish
    
        mDataset.add(position, mDataset.get(position));
        notifyItemInserted(position);
    
        mDataset.remove(position + 1);
        notifyItemRemoved(position + 1);
    }
    
    @Override
    public void onItemRightSwipe(int position) {
    
        // modify your item as you wish
    
        mDataset.add(position, mDataset.get(position));
        notifyItemInserted(position);
    
        mDataset.remove(position + 1);
        notifyItemRemoved(position + 1);
    }
    
  • 1

    使用此类为适配器类中的每个项实现滑动侦听器 .

    public class OnSwipeTouchListener implements OnTouchListener {
    
        private final GestureDetector gestureDetector;
    
        public OnSwipeTouchListener (Context ctx){
            gestureDetector = new GestureDetector(ctx, new GestureListener());
        }
    
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            return gestureDetector.onTouchEvent(event);
        }
    
        private final class GestureListener extends SimpleOnGestureListener {
    
            private static final int SWIPE_THRESHOLD = 100;
            private static final int SWIPE_VELOCITY_THRESHOLD = 100;
    
            @Override
            public boolean onDown(MotionEvent e) {
                return true;
            }
    
            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                boolean result = false;
                try {
                    float diffY = e2.getY() - e1.getY();
                    float diffX = e2.getX() - e1.getX();
                    if (Math.abs(diffX) > Math.abs(diffY)) {
                        if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                            if (diffX > 0) {
                                onSwipeRight();
                            } else {
                                onSwipeLeft();
                            }
                        }
                        result = true;
                    } 
                    else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                            if (diffY > 0) {
                                onSwipeBottom();
                            } else {
                                onSwipeTop();
                            }
                        }
                        result = true;
    
                } catch (Exception exception) {
                    exception.printStackTrace();
                }
                return result;
            }
        }
    
        public void onSwipeRight() {
        }
    
        public void onSwipeLeft() {
        }
    
        public void onSwipeTop() {
        }
    
        public void onSwipeBottom() {
        }
    }
    

    在适配器类中:

    itemView.setOnTouchListener(new OnSwipeTouchListener(getActivity()) {
                public void onSwipeTop() {
                }
    
                public void onSwipeRight() {
    
                }
    
                public void onSwipeLeft() {
    
                }
    
                public void onSwipeBottom() {
                }
    
            });
    

相关问题