Как обрабатывать Swipe-To-Remove на RecyClerview правильно? -- android поле с участием swipe поле с участием android-recyclerview пол Связанный проблема

How to handle swipe-to-remove on recyclerView correctly?


2
vote

проблема

русский

фон

Я пытаюсь позволить удалить элементы вида рецирллеров, но по какой-то причине он не всегда всегда играет, показывая пустые пробелы вместо карт.

Я сделал обработку кода оба бросать и перемещать элемент, чтобы заставить анимацию прослушивания, а когда концы анимации, элемент удаляется из набора данных и уведомляет адаптер.

Может быть, это потому, что я новичок в 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);         }       }     }   

Вопрос

Что не так в том, что я сделал? Как это иногда работает хорошо, а иногда нет?

Есть, может быть, лучшее решение для обработки промахов к удалению?

Английский оригинал

Background

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.

The code

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

The question

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?

</div
        

Список ответов

3
 
vote

Вы не можете получить доступ к position параметра в обратном вызове, потому что RecyClerview не будет воспрещаться просмотром только потому, что его позиция изменилась. Удаление элемента меняет позицию всех элементов под ним, поэтому все ваши позиции ссылок на эти элементы будут устареть.

Вместо этого вы можете использовать exec /usr/bin/ssh -o User=user1 -o IdentityFile=/path/to/ssh/key -o HostName=repo.com -o StrictHostKeyChecking=no -o UserKnownHostsFile=/path/to/knownhosts -p <port> repo.com git-receive-pack '/repo/path' 0 , чтобы получить место в современной позиции во время действия пользователя.

В дополнение к этому, не добавляйте жест жест и прикосноведим прослушиватель в onbind, вместо этого добавьте их при создании просмотра. Таким образом, вы избежите создания нового объекта каждый раз, когда элемент отскок.

<Сильное> Обновление для комментариев. Предлагаемые изменения:

 <код> exec /usr/bin/ssh -o User=user1 -o IdentityFile=/path/to/ssh/key -o HostName=repo.com -o StrictHostKeyChecking=no -o UserKnownHostsFile=/path/to/knownhosts -p <port> repo.com git-receive-pack '/repo/path' 1  
 

You cannot access the position parameter in the callback because RecyclerView will not rebind a ViewHolder just because its position has changed. 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.

In addition to that, do not add the gesture listener and touch listener in onBind, instead, add them when you create the 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;     } 
</div
 
 
         
         

Связанный проблема

4  Проблемы о переполнении размера кучи со звуковыми клипами  ( Heap size overflow issues with sound clips ) 
Я создаю приложение для Android, которое включает несколько коротких звуковых зажимов. На данный момент я использую SoundPool для загрузки моих звуковых кли...

0  Случайные 7-значные номера на andich android  ( Random 7 digit numbers on click android ) 
Я хотел бы, чтобы вы помогли мне в этом коде. Я могу генерировать случайные числа, но не в точном количестве. Вопрос: Как я могу генерировать 7 случайных ци...

64  Uninstall adb не удалось  ( Adb uninstall failed ) 
Я пишу некоторые образцы приложений. После отладка этих приложений я не вижу кнопку удаления в управлении приложениями моего устройства. Когда я делаю ADB у...

1  Установка поля _ID с использованием SQLite & ContentProvider в Android  ( Setting the id field using sqlite contentprovider in android ) 
Я пытаюсь настроить ContentProvider в приложении Android, чтобы держать информацию о домах. Я хотел бы иметь возможность установить поле _id, чтобы быть таким...

0  Android полноэкранный  ( Android fullscreen ) 
Я разрабатываю приложение Android, используя Eclipse. Я заметил одно и то это приложение не показывает на полном экране. Есть пустые черные пространства вокру...

0  Запуск Android-тестового проекта от Launcher  ( Running android test project from launcher ) 
У меня есть некоторые требования, где я должен запускать тестовый проект в любое время на моем устройстве, не подключая его к системе и запущена через Eclipse...

0  Swiperefresh ListView, обновления, но не то, как вы ожидаете  ( Swiperefresh listview updates but not how you would expect ) 
У меня есть список, который использует функцию SwipeFresh, и я столкнулся с странной проблемой. Когда я проводлю вниз, я вижу анимацию и мою информацию (обнов...

1  Можно ли построить ящики Edittext, как ListView?  ( Is it possible to build edittext boxes like a listview ) 
Так что в основном то, что я хотел бы сделать, это уметь создать список редактируемых полей, используя ящики Edittext. Однако вместо того, чтобы войти в мой X...

0  Новое развитие приложений с использованием Cordova и Ionic  ( Native app development using cordova and ionic ) 
Как сохранить процесс или сервис всегда запущены даже после того, как приложение будет убито пользователем, похожее на секундомер, который будет запущен даже ...

0  Async HTTP-запрос и Arduino  ( Async http request and arduino ) 
Я разрабатываю приложение для Android, которое будет общаться с Arduino Uno, хотя WiFi. Arduino уже запрограммирован, чтобы включить цифровой порт 12, когда я...

16  Обычная задача Android (эквивалент Cronjob)  ( Android regular task cronjob equivalent ) 
Последний раз, когда этот вопрос был задан (другим пользователем), ответ ответа был: Если это работает в беговой активности, вы можете использовать таймер ...

-3  Как прекратить воспроизведение сервиса в Android  ( How to stop playing service in android ) 
У меня есть вопрос о сервисе и трансляции в Android.I Создайте страницу будильника, которые используя класс тревоги Reciver (он расширяет класс трансляции) дл...

76  Как настроить пункт и элемент текстового цвета внутри навигацииВизнес?  ( How to customize item background and item text color inside navigationview ) 
Я хочу достичь чего-то вроде этого показано в Документы дизайна материалов < / a>. <Код> colorControlHighlight используется для фона на проверенных элемен...

0  Как я могу прочитать все контакты в Android 1.5 на мобильном?  ( How can i read all contacts in android 1 5 mobile ) 
Я хочу быть в состоянии прочитать все контакты в Android Mobile версии 1.5. Пожалуйста, помогите мне. Список считывать контент и пример, пожалуйста, спасибо...

0  Как добавить анимацию на фоне в живых обоях в андроид?  ( How to add animation on background in live wallpaper in android ) 
Я хочу добавить прикосновение эффект с анимацией в живых обоях: <код> Bitmap img = BitmapFactory.decodeResource(getResources(),R.drawable.heart); float x = ...