[英]How to handle swipe-to-remove on recyclerView correctly?
I'm trying to allow to swipe to remove items of the recycler view, but for some reason it doesn't always play nicely, showing empty spaces instead of the cards. 我试图允许滑动以删除回收站视图中的项目,但是由于某些原因,它并不总是能很好地播放,显示了空白而不是纸牌。
I've made the code handle both flinging and moving the item, to trigger the animation of the swiping, and when the swiping animation ends, the item is removed from the dataset and notifies the adapter too. 我已经使代码能够同时拖动和移动该项目,以触发滑动动画,并且当滑动动画结束时,该项目也会从数据集中删除,并且还会通知适配器。
Maybe it's because I'm new to RecyclerView, but I can't find what's missing. 也许是因为我是RecyclerView的新手,但我找不到丢失的内容。
public class MainActivity extends ActionBarActivity
{
private RecyclerView mRecyclerView;
private LinearLayoutManager mLayoutManager;
private MyAdapter mAdapter;
private static final int DATA_COUNT=100;
private ArrayList<String> mDataSet;
@Override
protected void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView=(RecyclerView)findViewById(R.id.my_recycler_view);
mRecyclerView.setHasFixedSize(true);
mLayoutManager=new LinearLayoutManager(this);
// TODO in case we use GridLayoutManager, consider using this: http://stackoverflow.com/q/26869312/878126
mRecyclerView.setLayoutManager(mLayoutManager);
mDataSet=new ArrayList<String>(DATA_COUNT);
for(int i=0;i<DATA_COUNT;++i)
mDataSet.add(Integer.toString(i));
mAdapter=new MyAdapter(mDataSet);
mRecyclerView.setAdapter(mAdapter);
}
// ///////////////////////////////////////////////////////////////
// MyAdapter//
// ///////////
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
{
private final ArrayList<String> mDataset;
public class ItemViewType
{
private static final int HEADER=0, ITEM=1;
}
public MyAdapter(final ArrayList<String> myDataset)
{
mDataset=myDataset;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent,final int viewType)
{
final RecyclerView.ViewHolder holder;
final View rootView;
switch(viewType)
{
case ItemViewType.HEADER:
rootView=LayoutInflater.from(parent.getContext()).inflate(R.layout.header,parent,false);
holder=new HeaderViewHoler(rootView);
break;
case ItemViewType.ITEM:
rootView=LayoutInflater.from(parent.getContext()).inflate(R.layout.card,parent,false);
holder=new ItemViewHolder(rootView);
rootView.setAlpha(1);
rootView.setTranslationX(0);
rootView.setTranslationY(0);
handleSwiping(rootView,holder);
break;
default:
holder=null;
break;
}
return holder;
}
private void handleSwiping(final View rootView,final RecyclerView.ViewHolder holder)
{
final GestureDetectorCompat gestureDetector=new GestureDetectorCompat(rootView.getContext(),
new GestureDetector.OnGestureListener()
{
...
@Override
public boolean onFling(final MotionEvent e1,final MotionEvent e2,final float velocityX,
final float velocityY)
{
final int viewSwipeThreshold=rootView.getWidth()/4;
if(velocityX<-viewSwipeThreshold)
{
onSwipe(rootView,holder.getPosition(),false);
return true;
}
else if(velocityX>viewSwipeThreshold)
{
onSwipe(rootView,holder.getPosition(),true);
return true;
}
return false;
}
});
rootView.setOnTouchListener(new View.OnTouchListener()
{
private final float originalX=0;
private final float originalY=0;
private float startMoveX=0;
private float startMoveY=0;
@Override
public boolean onTouch(final View view,final MotionEvent event)
{
final int viewSwipeHorizontalThreshold=rootView.getWidth()/3;
final int viewSwipeVerticalThreshold=view.getContext().getResources()
.getDimensionPixelSize(R.dimen.vertical_swipe_threshold);
if(gestureDetector.onTouchEvent(event))
return true;
final float x=event.getRawX(), y=event.getRawY();
final float deltaX=x-startMoveX, deltaY=y-startMoveY;
switch(event.getAction()&MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
startMoveX=x;
startMoveY=y;
break;
case MotionEvent.ACTION_UP:
if(Math.abs(deltaX)<viewSwipeHorizontalThreshold)
{
rootView.animate().translationX(originalX).translationY(originalY).alpha(1).start();
if(Math.abs(deltaY)<viewSwipeHorizontalThreshold)
rootView.performClick();
}
else if(deltaX<0)
onSwipe(rootView,holder.getPosition(),true);
else
onSwipe(rootView,holder.getPosition(),false);
break;
case MotionEvent.ACTION_CANCEL:
if(Math.abs(deltaX)<viewSwipeHorizontalThreshold
||Math.abs(deltaY)<viewSwipeVerticalThreshold)
rootView.animate().translationX(originalX).translationY(originalY).alpha(1).start();
else if(deltaX<0)
onSwipe(rootView,holder.getPosition(),true);
else
onSwipe(rootView,holder.getPosition(),false);
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
rootView.setAlpha(Math.max(Math.min((255-Math.abs(deltaX))/255f,1.0f),0.1f));
rootView.setTranslationX(deltaX);
break;
}
return true;
}
});
}
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder,final int position)
{
final int itemViewType=getItemViewType(position);
final View rootView=holder.itemView;
rootView.setAlpha(1);
rootView.setTranslationX(0);
rootView.setTranslationY(0);
}
private void onSwipe(final View rootView,final int position,final boolean isToLeft)
{
ViewPropertyAnimator animator;
if(isToLeft)
animator=rootView.animate().translationX(-rootView.getWidth());
else
animator=rootView.animate().translationX(rootView.getWidth());
animator.setListener(new Animator.AnimatorListener()
{
@Override
public void onAnimationStart(Animator animation)
{
}
@Override
public void onAnimationEnd(Animator animation)
{
rootView.setAlpha(1);
mDataset.remove(position);
notifyItemRemoved(position);
}
@Override
public void onAnimationCancel(Animator animation)
{
}
@Override
public void onAnimationRepeat(Animator animation)
{
}
});
animator.start();
}
@Override
public int getItemCount()
{
return mDataset.size()+1;
}
@Override
public int getItemViewType(final int position)
{
return position==0?ItemViewType.HEADER:ItemViewType.ITEM;
}
}
// ///////////////////////////////////////
// HeaderViewHoler //
// //////////////////
public static class HeaderViewHoler extends RecyclerView.ViewHolder
{
public TextView mTextView;
public HeaderViewHoler(final View v)
{
super(v);
}
}
// ///////////////////////////////////////
// ItemViewHolder //
// /////////////////
public static class ItemViewHolder extends RecyclerView.ViewHolder
{
public ItemViewHolder(final View rootView)
{
super(rootView);
rootView.setAlpha(1);
rootView.setTranslationX(0);
rootView.setTranslationY(0);
}
}
}
What is wrong in what I did? 我做错了什么? How come it sometimes works well and sometimes doesn't? 为什么有时效果很好,有时却效果不好?
Is there maybe a better solution for the swipe-to-remove handling? 滑动删除操作是否可能有更好的解决方案?
You cannot access the position
parameter in the callback because RecyclerView will not rebind a ViewHolder just because its position has changed. 您无法访问回调中的position
参数,因为RecyclerView不会仅仅因为ViewHolder的位置发生变化而重新绑定ViewHolder。 Removing an item changes the position of all items below it so all of your position references for those items will be obsolete. 删除项目会更改其下方所有项目的位置,因此这些项目的所有位置参考都将过时。
Instead, you can use ViewHolder#getPosition
to get the up to date position at the time of the user action. 相反,您可以使用ViewHolder#getPosition
来获取用户操作时的最新位置。
In addition to that, do not add the gesture listener and touch listener in onBind, instead, add them when you create the ViewHolder. 除此之外,不要在onBind中添加手势侦听器和触摸侦听器,而是在创建ViewHolder时添加它们。 This way, you'll avoid creating a new object each time an item is rebound. 这样,您将避免在每次反弹物品时创建一个新对象。
Update for the comment. 更新评论。 Suggested changes: 建议的更改:
@Override
public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
RecyclerView.ViewHolder holder = null;
View rootView;
switch (viewType) {
case ItemViewType.HEADER:
rootView = LayoutInflater.from(parent.getContext()).inflate(R.layout.header, parent, false);
holder = new HeaderViewHoler(rootView);
break;
case ItemViewType.ITEM:
rootView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card, parent, false);
holder = new ItemViewHolder(rootView);
//initialize gesture detector and touch listener, replace position with getPosiiton
}
return holder;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.