2016-05-23 72 views
1

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

public interface ItemTouchHelperAdapter { 

/** 
* Called when an item has been dismissed by a swipe.<br/> 
* <br/> 
* 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()); 

} 

回答

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() { 
      } 

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

可能要参考代码库的代码是从:https://github.com/nemanja-kovacevic/recycler-view- swipe-to-delete/blob/master/app/src/main/java/net/nemanjakovacevic/recyclerviewswipetodelete/MainActivity.java或者反之亦然 –