Android recyclerview拖拽自定义功能

Posted

技术标签:

【中文标题】Android recyclerview拖拽自定义功能【英文标题】:Android recyclerview drag and drop custom fetures 【发布时间】:2015-12-07 05:56:50 【问题描述】:

我正在尝试开发一个简单但自定义的 recyclerview 项目拖放功能。这个想法是将一个孩子拖到一个特定的视图,由 FrameLayout 表示,它不是 recyclerview 的孩子。 当一个孩子被扔到它上面时,它将被删除,否则它将简单地回到原来的位置。 我可以在没有任何动画效果的情况下轻松实现此行为,但我的目的是在 UP 拖动事件和元素的恢复上具有平滑的动画效果。现在,每当拖动一个孩子时,我都会为其创建一个 DragShadow,然后删除数据集中的孩子并使用 adapter.notifyItemRemoved(position) 通知删除。 最后一个允许我使用 Recyclerview 本身提供的简单删除动画。 守则比文字更能解释机制:

public boolean dragNdrop(View arg1, int position) 
    // ------------------------
    final RecyclerAdapter adapter= ListItemFragment.getAdapter();

   /*Keep track of the position and the object for restoring purpose*/
    ListItemFragment.indexAbruptedRemoved=position;
    ListItemFragment.itemAbruptedRemoved=adapter.getItem(position);

    ClipData clipData = ClipData.newPlainText("", "");
    View.DragShadowBuilder dsb = new View.DragShadowBuilder(arg1);
    arg1.startDrag(clipData, dsb, arg1, 0);


    adapter.remove(position);
    adapter.notifyItemRemoved(position);

    //--------------------

适配器的getter方法:

protected static RecyclerAdapter getAdapter() 
        return (RecyclerAdapter) recList.getAdapter();

dragNdrop 由自定义适配器中的拖动侦听器调用:

public void onBindViewHolder(MyHolder myHolder, int position) 
   //----------------------------------

    myHolder.rowCard.setLongClickable(true);
    myHolder.rowCard.setOnClickListener(new MyClickListener.ContainerListener(context,
            myHolder.getAdapterPosition(), rowObject, myHolder.imagePreView));
    myHolder.rowCard.setOnLongClickListener(new MyClickListener.DragListener(context,
            myHolder.getAdapterPosition(), myHolder.rowCard));

 //----------------------------------

LongCLickListener:

static class DragListener implements View.OnLongClickListener 
    private Context context;
    private int position;
    private View dragged;

    DragListener(Context context, int position, View dragged) 
        this.context = context;
        this.position = position;
        this.dragged = dragged;
    

    @Override
    public boolean onLongClick(View v) 
        return ((MainActivity) context).dragNdrop(dragged, position);
    

名为trashPanel的FrameLayout代码如下:

 trashPanel.setOnDragListener(new View.OnDragListener() 


        @Override
        public boolean onDrag(View view, DragEvent dragEvent) 
            int dragAction = dragEvent.getAction();
            View dragView = (View) dragEvent.getLocalState();//this
            //is properly the object we 've passed with startdrag() 

            switch (dragAction) 

                case DragEvent.ACTION_DRAG_EXITED:
                    imageView.setImageDrawable(getResources().getDrawable(R.drawable.bowl));
                    break;
                case DragEvent.ACTION_DRAG_ENTERED:
                    imageView.setImageDrawable(getActivity().getResources().getDrawable(R.drawable.waste));
                    break;
                case DragEvent.ACTION_DRAG_ENDED:
                    ended(view, dragEvent);
                    break;
                case DragEvent.ACTION_DROP:
                    Log.i(TAG,"HALOOOOO DROPPED");
                    drop();
                    break;

            

            return true;

        //[m] end on drag


        private void drop() 
            String itemToRemove = itemAbruptedRemoved.getSessionName();


            Toast.makeText(getActivity(), "DELETED: " + itemToRemove+
                            " list length:"+items.size()+" adapter length:"+mAdapter.size(),
                    Toast.LENGTH_SHORT).show();

            /**
             * HAPTIC FEEDBACK
             */
            Vibrator v = (Vibrator) getActivity().getSystemService(Service.VIBRATOR_SERVICE);
            long pattern[] = 25, 25, 50;
            v.vibrate(pattern, -1);// -1 to not repeat
        

        private void ended(View view, DragEvent dragEvent) 
            if ( dropEventNotHandled(dragEvent) ) 

                mAdapter.insert(itemAbruptedRemoved, indexAbruptedRemoved);
                mAdapter.notifyItemInserted(indexAbruptedRemoved);
      //********---------->mAdapter.notifyDataSetChanged();
                Log.i(TAG,"AGGIUNTO");
            
            //change the image inside trash_panel
            imageView.setImageDrawable(getActivity().getResources().getDrawable(R.drawable.bowl));

            //due to implementations detail it must be done in this way(otherwise mutithread exception is throwned)
            view.post(new Runnable() 
                public void run() 

                    /**
                     * Set animation for fabNew Button and trash_panel
                     * in order to make them smoothly disappear
                     * and place them in their original positions
                     *
                     */

                   ------------------------------------

                
            );

        


        private boolean dropEventNotHandled(DragEvent dragEvent) 
            return !dragEvent.getResult();
        


    );

问题是有时当我开始拖动一个项目时,我获得了正确的 DragShadow,但删除的项目是错误的。 在代码中,我使用 ***----> 调用 notifyDataSetChanged(),因为如果我调用它,一切正常,但没有动画。

我知道应该使用诸如 ItemTouchHelper.Callback 之类的实用程序类来操纵 recyclerlist 子项的移动和动画,但我不知道如何设法让他们做我想做的事。我看到了一些可用于实现此目的的方法,例如 onChildDrawOver 和 onChildDraw,但我仍然不知道如何使用它们来拦截 dragevent.DROP 。我也知道 LLM 的接口 ItemTouchHelper.ViewDropHandler 的存在,它具有抽象方法 prepareForDrop,但我仍然不知道如何正确使用它。

提前感谢所有帮助我的人!

【问题讨论】:

【参考方案1】:

看看ItemTouchHelper。它将允许您自定义拖动绘图。有关示例实现,请参阅 Support7Demos。

【讨论】:

以上是关于Android recyclerview拖拽自定义功能的主要内容,如果未能解决你的问题,请参考以下文章

Android recyclerview拖拽自定义功能

Android基础控件——RecyclerView实现拖拽排序侧滑删除效果

android开发游记:ItemTouchHelper 使用RecyclerView打造可拖拽的GridView

Android一步一步带你实现RecyclerView的拖拽和侧滑删除功能

向下拖拽展示更多之自定义 RecyclerView

Android10.5 滚动视图(RecyclerView)