RecyclerView Swipe with a view below it

前端 未结 6 1015
囚心锁ツ
囚心锁ツ 2020-12-07 11:24

I have been doing some research and I have yet to find an example or implementation that allows you to put a view (Example Image Below) underneath the RecyclerView when you

相关标签:
6条回答
  • 2020-12-07 11:39

    I like the @erik approach but I would recommend to drawing what you want via passed Canvas to onChildDraw() function. e.g. item background or icon.

    @Override 
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        final ColorDrawable background = new ColorDrawable(Color.RED);
        background.setBounds(0, itemView.getTop(),   itemView.getLeft() + dX, itemView.getBottom());
        background.draw(c);
    
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
    }
    

    In this approach, you will draw what needed on demand and no need to inflate unused views

    0 讨论(0)
  • 2020-12-07 11:44

    My understanding of how this is done is that one would put two views in the xml that would be displayed per line in your recyclerview.

    So for example, this would be my adapter:

    public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
        public static class ExampleViewHolder extends RecyclerView.ViewHolder {
            public TextView background;
            public TextView foreground;
    
            public ExampleViewHolder(View v) {
                super(v);
                background = (TextView) v.findViewById(R.id.background);
                foreground = (TextView) v.findViewById(R.id.foreground);
            }
    
            @Override
            public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
                if (holder instanceof ExampleViewHolder) {
                    ((ExampleViewHolder) holder).background.setBackgroundColor(); // do your manipulation of background and foreground here.
                }
            }
    
            @Override
            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                              int viewType) {
    
                View v = LayoutInflater.from(parent.getContext())
                        .inflate(R.layout.example, parent, false);
                return new ExampleViewHolder(v);
            }
    
    
        }
    }
    

    Each line in the recyclerview is pulling the xml layout from R.layout.example. Therefore, to create a view underneath, you can just use relativelayout or framelayout to create the views on top of one another:

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/background"
            />
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/foreground"/>
    </RelativeLayout>
    

    Then if you do not want to use a library for the swipe, you can copy this class from google and subsequently modified by Bruno Romeu Nunes:

    https://github.com/heruoxin/Clip-Stack/blob/master/app/src/main/java/com/catchingnow/tinyclipboardmanager/SwipeableRecyclerViewTouchListener.java

    The class will require you to create a swipe listener:

    swipeTouchListener =
            new SwipeableRecyclerViewTouchListener(mRecyclerView,
                    new SwipeableRecyclerViewTouchListener.SwipeListener() {
                        @Override
                        public boolean canSwipe(int position) {
                            if (position == totalPost.size() - 1 && !connected) {
                                return false;
                            }
                            return true;
                        }
    
                        @Override
                        public void onDismissedBySwipeLeft(RecyclerView recyclerView, int[] reverseSortedPositions) {
                            for (int position : reverseSortedPositions) {
                                //change some data if you swipe left
                            }
                            myAdapter.notifyDataSetChanged();
                        }
    
                        @Override
                        public void onDismissedBySwipeRight(RecyclerView recyclerView, int[] reverseSortedPositions) {
                            for (int position : reverseSortedPositions) {
                                //change some data if you swipe right
                            }
                            myAdapter.notifyDataSetChanged();
                        }
                    });
    

    Then simply link it with your recyclerview:

        mRecyclerView.addOnItemTouchListener(swipeTouchListener);
    
    0 讨论(0)
  • 2020-12-07 11:44

    Changing elevation works for me to bring one item above another one.

    0 讨论(0)
  • 2020-12-07 11:45

    Simple solution without allocation or drawing on canvas. SomeAdapter.SomeVH should contain upper view and under view. And with this approach we will be able to swipe only upper view (container), exposing under view (with label, icon whatever you want)

    class SomeTouchHelper extends ItemTouchHelper.Callback {
    ...
        @Override
        public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
                float dX, float dY, int actionState, boolean isCurrentlyActive) {
            if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
                if (viewHolder instanceof SomeAdapter.SomeVH) {
                    SomeAdapter.SomeVH someViewHolder
                            = (SomeAdapter.SomeVH) viewHolder;
                    ViewCompat.setTranslationX(someViewHolder.mContainer, dX);
                }
            } else {
                super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            }
        }
    ...
    }
    

    Attach it

    new ItemTouchHelper(SomeTouchHelper).attachToRecyclerView(recyclerView);
    

    don't forget to restore initial view state of SomeVH in adapter onViewRecycled()

    public void onViewRecycled(final SomeVH holder) {
            if (holder.mContainer != null) {
                holder.mContainer.setTranslationX(0);//restore position
            }
    }
    
    0 讨论(0)
  • 2020-12-07 11:49

    I was investigating the same issue.
    What i ended up doing was, in the layout that contained the recyclerView, I added a simple FrameLayout called 'swipe_bg' of the same height as one of my RecyclerView.ViewHolder items. I set its visibility to "gone", and placed it under the RecyclerView

    Then in my activity where i set the ItemTouchHelper, I override the onChildDraw like so..

    ItemTouchHelper.SimpleCallback swipeCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT) {
    
        @Override
        public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
             View itemView = viewHolder.itemView;
             Imageview swipe_bg = itemView.findViewById(R.id.imageview_swipe_background);
             swipe_bg.setY(itemView.getTop());
             if (isCurrentlyActive) {
                 swipe_bg.setVisibility(View.VISIBLE);
             } else {
                 swipe_bg.setVisibility(View.GONE);
             }
             super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        }
    };
    

    Don't know if that is the best way, but seemed like the simplest way.

    0 讨论(0)
  • 2020-12-07 11:51

    You can use RvTools Library. It will help you easily implement what you want.

    Just create your SwipeContextMenuDrawer with desired look

    public class YourSwipeContextMenuDrawer extends SwipeContextMenuDrawer {
    
    private final Paint mRightPaint;
    private final Paint mIconPaint;
    private final Bitmap mRightIconBitmap;
    
    public YourSwipeContextMenuDrawer(@NonNull Context context) {
        mRightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mRightPaint.setColor(ContextCompat.getColor(context, R.color.deep_orange_400));
        mRightIconBitmap = GraphicUtils.getBitmap(context, R.drawable.ic_delete, 100, 100);
        mIconPaint = new Paint();
        mIconPaint.setColorFilter(new PorterDuffColorFilter(ContextCompat.getColor(context, R.color.backgroundColor), PorterDuff.Mode.SRC_IN));
    }
    
    @Override
    public void drawRight(@NonNull Canvas canvas, @NonNull View view) {
        canvas.drawRect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom(), mRightPaint);
        canvas.drawBitmap(mRightIconBitmap, view.getLeft() + mRightIconBitmap.getWidth() - 20, (view.getBottom() + view.getTop() - mRightIconBitmap.getHeight()) >> 1, mIconPaint);
    }
    
    @Override
    public void drawLeft(@NonNull Canvas canvas, @NonNull View view) {
    }
    }
    

    And create RvTools instance with swipe action and swipe context menu drawer like this

     new RvTools.Builder(recyclerView)
                .withSwipeRightAction(this)
                .withSwipeContextMenuDrawer(new YourSwipeContextMenuDrawer(getContext()))
                .buildAndApplyToRecyclerView();
    
    0 讨论(0)
提交回复
热议问题