1. 程式人生 > >RecyclerView的item拖動排序效果實現和它的ItemTouchHelper詳解

RecyclerView的item拖動排序效果實現和它的ItemTouchHelper詳解

recyclerview這個控制元件太強大了,自帶各種特效,很多高逼格效果需要我們自己簡單組合一下就呈現出來,比如拖動排序!它對拓展簡直是非常open(OCP原則),廢話不扯了,先看效果:
這裡寫圖片描述

說明:
1.實現這個效果的核心類是:ItemTouchHelper和ItemTouchHelper.Callbck.
2.mainActivity的佈局就是一個recyclerview,item的佈局cardview套著三個控制元件。

AAA: 首先看MAinActivity的程式碼吧

public class MainActivity extends AppCompatActivity {
List<String> list; private MyAdapter adapter; RecyclerView recyView; //初始化集合資料 { list = new ArrayList<>(); for (int i = 0; i < 10; i++) { list.add("我的序號是:" + i); } } @Override protected void onCreate(Bundle savedInstanceState) { super
.onCreate(savedInstanceState); setContentView(R.layout.activity_main); recyView = (RecyclerView) findViewById(R.id.recyclerview); adapter = new MyAdapter(); recyView.setLayoutManager(new LinearLayoutManager(this)); recyView.setAdapter(adapter); //1.建立item helper
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback); //2.繫結到recyclerview上面去 itemTouchHelper.attachToRecyclerView(recyView); //3.在ItemHelper的介面回撥中過濾開啟長按拖動,拓展其他操作 }

BBB: 下面是adapter的程式碼

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {

    List<String> list;
    public MyAdapter(List<String> list) {
        this.list=list;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
        MyViewHolder holder = new MyViewHolder(itemView);
        return holder;
    }

    @Override
    public int getItemCount() {
        return list == null ? 0 : list.size();
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.textNumber.setText(list.get(position));
    }


    class MyViewHolder extends RecyclerView.ViewHolder {

        TextView textNumber;

        public MyViewHolder(View itemView) {
            super(itemView);
            textNumber = (TextView) itemView.findViewById(R.id.textNumber);
        }

    }
}

CCC : 下面是itemTouchHelper的重寫程式碼,並附有大量的註釋,我是翻看了原始碼和谷歌官方文件,寫得應該還是比較清楚的了,這個寫好的itemTouchHelper程式碼直接複製過去是可以用的。
注意:目前實現的是item的長按拖動啊!!!

 //itemHelper的回撥
    ItemTouchHelper.Callback callback = new ItemTouchHelper.Callback() {

        /**
         * 官方文件的說明如下:
         * o control which actions user can take on each view, you should override getMovementFlags(RecyclerView, ViewHolder)
         * and return appropriate set of direction flags. (LEFT, RIGHT, START, END, UP, DOWN).
         * 返回我們要監控的方向,上下左右,我們做的是上下拖動,要返回都是UP和DOWN
         * 關鍵坑爹的是下面方法返回值只有1個,也就是說只能監控一個方向。
         * 不過點入到原始碼裡面有驚喜。原始碼標記方向如下:
         *  public static final int UP = 1     0001
         *  public static final int DOWN = 1 << 1; (位運算:值其實就是2)0010
         *  public static final int LEFT = 1 << 2   左 值是3
         *  public static final int RIGHT = 1 << 3  右 值是8
         */
        @Override
        public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
            //也就是說返回值是組合式的
            //makeMovementFlags (int dragFlags, int swipeFlags),看下面的解釋說明
            int swipFlag=0;
            //如果也監控左右方向的話,swipFlag=ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT;
            int dragflag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            //等價於:0001&0010;多點觸控標記觸屏手指的順序和個數也是這樣標記哦
            return  makeMovementFlags(dragflag,swipFlag);

            /**
             * 備註:由getMovementFlags可以聯想到setMovementFlags,不過文件麼有這個方法,但是:
             * 有 makeMovementFlags (int dragFlags, int swipeFlags)
             * Convenience method to create movement flags.便捷方法建立moveMentFlag
             * For instance, if you want to let your items be drag & dropped vertically and swiped left to be dismissed,
             * you can call this method with: makeMovementFlags(UP | DOWN, LEFT);
             * 這個recyclerview的文件寫的簡直完美,示例程式碼都弄好了!!!
             * 如果你想讓item上下拖動和左邊滑動刪除,應該這樣用: makeMovementFlags(UP | DOWN, LEFT)
             */

            //拓展一下:如果只想上下的話:makeMovementFlags(UP | DOWN, 0),標記方向的最小值1
        }



        /**
         * 官方文件的說明如下
         * If user drags an item, ItemTouchHelper will call onMove(recyclerView, dragged, target). Upon receiving this callback,
         * you should move the item from the old position (dragged.getAdapterPosition()) to new position (target.getAdapterPosition())
         * in your adapter and also call notifyItemMoved(int, int).
         * 拖動某個item的回撥,在return前要更新item位置,呼叫notifyItemMoved(draggedPosition,targetPosition)
         * viewHolde:正在拖動item
         * target:要拖到的目標
         * @return true 表示消費事件
         */
        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
            //直接按照文件來操作啊,這文件寫得太給力了,簡直完美!
            adapter.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
            //注意這裡有個坑的,itemView 都移動了,對應的資料也要移動
            Collections.swap(list, viewHolder.getAdapterPosition(), target.getAdapterPosition());
            return true;
        }



        /**
         * 谷歌官方文件說明如下:
         * 這個看了一下主要是做左右拖動的回撥
         * When a View is swiped, ItemTouchHelper animates it until it goes out of bounds, then calls onSwiped(ViewHolder, int).
         * At this point, you should update your adapter (e.g. remove the item) and call related Adapter#notify event.
         * @param viewHolder
         * @param direction
         */
        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
           //暫不處理
        }



        /**
         * 官方文件如下:返回true 當前tiem可以被拖動到目標位置後,直接”落“在target上,其他的上面的tiem跟著“落”,
         * 所以要重寫這個方法,不然只是拖動的tiem在動,target tiem不動,靜止的
         * Return true if the current ViewHolder can be dropped over the the target ViewHolder.
         * @param recyclerView
         * @param current
         * @param target
         * @return
         */
        @Override
        public boolean canDropOver(RecyclerView recyclerView, RecyclerView.ViewHolder current, RecyclerView.ViewHolder target) {
            return true;
        }


        /**
         * 官方文件說明如下:
         * Returns whether ItemTouchHelper should start a drag and drop operation if an item is long pressed.
         * 是否開啟長按 拖動
         * @return
         */
        @Override
        public boolean isLongPressDragEnabled() {
            //return true後,可以實現長按拖動排序和拖動動畫了
            return true;
        }
    };

======================================================================================

上面實現item的長按拖動,有很多需求可能觸控item的某個控制元件就進行拖動。
先看改進後的效果圖如下:
這裡寫圖片描述

這個時候去找官方文件了。recyclerview真強大,要啥有啥。
itemTouchHelper有個方法 如下:

        //谷歌官方文件如下:
        //開啟拖動我們給他的holder,但是預設
        // 是長按拖動,直接程式碼呼叫拖動要先禁止長按拖動
        //Starts dragging the provided ViewHolder. By default,
        // ItemTouchHelper starts a drag when a View is long pressed.
        // You can disable that behavior by overriding isLongPressDragEnabled().
        itemTouchHelper.startDrag(holder);

由於adapter和activity是分離的兩個類,所以用了介面回撥,更新了adatper的程式碼和mainActivity的程式碼。

DDD: 介面的程式碼如下:

public interface startDragListener {
    //觸控imageview,開啟拖動的介面
    void startDragItem(RecyclerView.ViewHolder holder);
}

EEE: 更新後的adapter的程式碼如下:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {

    startDragListener draglistener;
    //set介面回撥
    public void setDraglistener(startDragListener draglistener) {
        this.draglistener = draglistener;
    }

    List<String> list;
    public MyAdapter(List<String> list) {
        this.list=list;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
        MyViewHolder holder = new MyViewHolder(itemView);
        return holder;
    }

    @Override
    public int getItemCount() {
        return list == null ? 0 : list.size();
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, int position) {
        holder.textNumber.setText(list.get(position));
        holder.image.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                //注意:這裡down和up都會回撥該方法
                if (event.getAction()== MotionEvent.ACTION_DOWN) {
                    draglistener.startDragItem(holder);
                }
                return false;
            }
        });
    }


    class MyViewHolder extends RecyclerView.ViewHolder {

        TextView textNumber;
        ImageView image;

        public MyViewHolder(View itemView) {
            super(itemView);
            textNumber = (TextView) itemView.findViewById(R.id.textNumber);
            image= (ImageView) itemView.findViewById(R.id.image);
        }

    }
}

FFF:更新後的mainactivity的程式碼如下:

public class MainActivity extends AppCompatActivity implements startDragListener{


    List<String> list;
    private MyAdapter adapter;
    RecyclerView recyView;
    private ItemTouchHelper itemTouchHelper;

    //初始化集合資料
    {
        list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add("我的序號是:" + i);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyView = (RecyclerView) findViewById(R.id.recyclerview);
        adapter = new MyAdapter(list);
        adapter.setDraglistener(this);
        recyView.setLayoutManager(new LinearLayoutManager(this));
        recyView.setAdapter(adapter);
        //1.建立item helper
        itemTouchHelper = new ItemTouchHelper(callback);
        //2.繫結到recyclerview上面去
        itemTouchHelper.attachToRecyclerView(recyView);
        //3.在ItemHelper的介面回撥中過濾開啟長按拖動,拓展其他操作
    }

    //itemHelper的回撥
    ItemTouchHelper.Callback callback = new ItemTouchHelper.Callback() {

        /**
         * 官方文件的說明如下:
         * o control which actions user can take on each view, you should override getMovementFlags(RecyclerView, ViewHolder)
         * and return appropriate set of direction flags. (LEFT, RIGHT, START, END, UP, DOWN).
         * 返回我們要監控的方向,上下左右,我們做的是上下拖動,要返回都是UP和DOWN
         * 關鍵坑爹的是下面方法返回值只有1個,也就是說只能監控一個方向。
         * 不過點入到原始碼裡面有驚喜。原始碼標記方向如下:
         *  public static final int UP = 1     0001
         *  public static final int DOWN = 1 << 1; (位運算:值其實就是2)0010
         *  public static final int LEFT = 1 << 2   左 值是3
         *  public static final int RIGHT = 1 << 3  右 值是8
         */
        @Override
        public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
            //也就是說返回值是組合式的
            //makeMovementFlags (int dragFlags, int swipeFlags),看下面的解釋說明
            int swipFlag=0;
            //如果也監控左右方向的話,swipFlag=ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT;
            int dragflag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            //等價於:0001&0010;多點觸控標記觸屏手指的順序和個數也是這樣標記哦
            return  makeMovementFlags(dragflag,swipFlag);

            /**
             * 備註:由getMovementFlags可以聯想到setMovementFlags,不過文件麼有這個方法,但是:
             * 有 makeMovementFlags (int dragFlags, int swipeFlags)
             * Convenience method to create movement flags.便捷方法建立moveMentFlag
             * For instance, if you want to let your items be drag & dropped vertically and swiped left to be dismissed,
             * you can call this method with: makeMovementFlags(UP | DOWN, LEFT);
             * 這個recyclerview的文件寫的簡直完美,示例程式碼都弄好了!!!
             * 如果你想讓item上下拖動和左邊滑動刪除,應該這樣用: makeMovementFlags(UP | DOWN, LEFT)
             */

            //拓展一下:如果只想上下的話:makeMovementFlags(UP | DOWN, 0),標記方向的最小值1
        }



        /**
         * 官方文件的說明如下
         * If user drags an item, ItemTouchHelper will call onMove(recyclerView, dragged, target). Upon receiving this callback,
         * you should move the item from the old position (dragged.getAdapterPosition()) to new position (target.getAdapterPosition())
         * in your adapter and also call notifyItemMoved(int, int).
         * 拖動某個item的回撥,在return前要更新item位置,呼叫notifyItemMoved(draggedPosition,targetPosition)
         * viewHolde:正在拖動item
         * target:要拖到的目標
         * @return true 表示消費事件
         */
        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
            //直接按照文件來操作啊,這文件寫得太給力了,簡直完美!
            adapter.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
            //注意這裡有個坑的,itemView 都移動了,對應的資料也要移動
            Collections.swap(list, viewHolder.getAdapterPosition(), target.getAdapterPosition());
            return true;
        }



        /**
         * 谷歌官方文件說明如下:
         * 這個看了一下主要是做左右拖動的回撥
         * When a View is swiped, ItemTouchHelper animates it until it goes out of bounds, then calls onSwiped(ViewHolder, int).
         * At this point, you should update your adapter (e.g. remove the item) and call related Adapter#notify event.
         * @param viewHolder
         * @param direction
         */
        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
           //暫不處理
        }



        /**
         * 官方文件如下:返回true 當前tiem可以被拖動到目標位置後,直接”落“在target上,其他的上面的tiem跟著“落”,
         * 所以要重寫這個方法,不然只是拖動的tiem在動,target tiem不動,靜止的
         * Return true if the current ViewHolder can be dropped over the the target ViewHolder.
         * @param recyclerView
         * @param current
         * @param target
         * @return
         */
        @Override
        public boolean canDropOver(RecyclerView recyclerView, RecyclerView.ViewHolder current, RecyclerView.ViewHolder target) {
            return true;
        }


        /**
         * 官方文件說明如下:
         * Returns whether ItemTouchHelper should start a drag and drop operation if an item is long pressed.
         * 是否開啟長按 拖動
         * @return
         */
        @Override
        public boolean isLongPressDragEnabled() {
            //return true後,可以實現長按拖動排序和拖動動畫了
            return false;
        }
    };

    @Override
    public void startDragItem(RecyclerView.ViewHolder holder) {
        //谷歌官方文件如下:
        //開啟拖動我們給他的holder,但是預設
        // 是長按拖動,直接程式碼呼叫拖動要先禁止長按拖動
        //Starts dragging the provided ViewHolder. By default,
        // ItemTouchHelper starts a drag when a View is long pressed.
        // You can disable that behavior by overriding isLongPressDragEnabled().
        itemTouchHelper.startDrag(holder);
    }

}

備註:1.看完這篇文章就可以重寫側滑刪除,並有刪除動畫功能
2.寫部落格簡直太耗費時間了
3.demo原始碼的下載連結

相關推薦

RecyclerView的item排序效果實現ItemTouchHelper

recyclerview這個控制元件太強大了,自帶各種特效,很多高逼格效果需要我們自己簡單組合一下就呈現出來,比如拖動排序!它對拓展簡直是非常open(OCP原則),廢話不扯了,先看效果: 說明: 1.實現這個效果的核心類是:ItemTouchHelpe

RecyclerView 實現側滑刪除排序

在使用ListView的時候實現拖動排序,主要靠繼承別人第三方的,在網上有很多例子, 這裡不介紹,主要是介紹一下recycleview的拖動排序和滑動刪除 一、主要知識點:             1、 ItemTouchHelper.Callback的使用        

WPF 實現工具箱效果

get adding n) rgs 拖拽 value src 點擊 stroke 原文:WPF 實現拖動工具箱效果 1.效果 點擊左邊的矩形拖動到右邊canvas面板,右邊面板添加矩形 2.布局 左邊是個StockPanel,上面有個矩形,右

jq實現排序

首先我們要實現一個拖動的js這裡我們就不廢話了直接上拖動js的原始碼 $.fn.dropIng=function (type,Obj) { this.each(function (a, b){ b.typeDrop=0; if(typeof type!="u

Android 自定義View(繼承原生元件)實現移位效果

自定義View實現拖拽移位效果 通過繼承GridLayout實現的拖拽移位效果 首先建立Class類繼承GridLayout並重寫前三個構造方法 public class MyGridlayout extends GridLayout implement

jQuery UI sortable()實現排序

Query UI的強大不用多說,今天來看下它的拖動排序功能,jQuery UI使用sortable()實現對元素拖動排序,首先來看下面這個簡單的例子: <!DOCTYPE html> <html> <head> <title>

Android 自定義View(繼承原生元件)實現移位效果

自定義View實現拖拽移位效果 通過繼承GridLayout實現的拖拽移位效果 首先建立Class類繼承GridLayout並重寫前三個構造方法 public class MyGridlayout extends GridLayout implements

Android實現(一個流暢的排序DragSortGridView,自動滾屏)

/** * Copyright (C), 2008-2015, Huawei Tech. Co., Ltd. * <p/> * Description : 拖動排序佈局 * * @version V100R001 * @since V100R001 */ @SuppressLint(

基於Metronic的Bootstrap開發框架經驗總結(13)--頁面連結收藏夾功能的實現2(利用Sortable進行排序

在上篇隨筆《基於Metronic的Bootstrap開發框架經驗總結(12)--頁面連結收藏夾功能的實現》上,我介紹了連結收藏夾功能的實現,以及對收藏記錄的排序處理。該篇隨筆主要使用功能按鈕的方式移動收藏記錄,功能雖然實現的還算不錯,不過文章出來後,有讀者同行指出可以利用直接拖動的方式實現排序更方便,因此對其

實現排序的ListView-DragListView

專案 中要用到拖動排序的效果,於是百度到網上的做法,github上開源框架被我pass, 為了一個小功能匯入一庫太不划算。然後看到這篇 http://blog.csdn.net/jj120522/article/details/8240407,可能是博主原始

ios中建立可以的view原理實現(含程式碼)

有時候我們會需要在介面上拖動view;uiview是繼承於uiresponder的,所以可以響應觸控相關的事件。 重點是以下一組方法: - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event  -

Android實現GridView的item長按刪除完美實現(帶動畫效果

領導這幾天讓做一個專案,就是可以實現像支付寶首頁一樣的可以長按拖動,刪除的介面,以前沒做過,領導讓我做的時候覺得簡直是老虎吃天,無從下手啊,可是領導的任務還是要實現的,沒辦法,就自己網上找咯,但是網上的demo五花八門無法滿足我的需求,而且bug還很多,所以最後

sortable結合angularjs實現排序

cti 排序 n) upper dir object exist test getitem 記錄拖動排序 <!DOCTYPE html> <html lang="en"> <head> <meta charset

記一個react排序中的坑:key

map 一個 解決方案 () 不重復 tab 需求 unique nbsp 在做一個基於react的應用的時候遇到了對列表拖動排序的需求。當使用sortable對列表添加排序支持後發現一個問題:數據正確排序了,但是dom的順序卻亂了,找了一會兒原因後發現是因為在渲染數據的時

vue drag 對表格的列進行排序

literal val html class 狀態 epc 索引 一次 data 用drag實現拖動表格列進行列排序 以下是用到的主要方法 1.dragstart 拖動開始返回目標對象 2.dragenter 拖動過程中經過的對象 3.dragend 拖動結束返回目

前端div 效果

col urn als mouseup use document overflow 前端 author /** * author levi * url http://levi.cg.am */ $(function() { $(document).mouse

轉載 * jQuery實現動態分割div—通過分隔欄實現上下、左右動態改變左右、上下兩個相鄰div的大小

鼠標 動態改變 win 上下 key mousedown pre console ati 由jQuery實現上下、左右動態改變左右、上下兩個div的大小,需要自己引入jquery1.8.0.min.js包 可用於頁面布局。 //======================

選擇排序簡單實現複雜度估計

選擇排序 1.每一次從待排序的資料元素中選出最小(或最大)的一個元素,存放在 序列的起始位置,直到全部待排序的資料元素排完。 簡單實現 public class SelectionSort { /** * * @param arr 傳入需要排序的陣列

氣泡排序實現優化及其與插入,選擇排序的比較

備註:這裡使用的插入排序和選擇排序都是經過優化後的詳細優化請檢視上一條部落格,編譯器使用DEV-C++ 氣泡排序演算法的運作如下:(從後往前) 1.比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。 2.對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數

ng2-dragula 排序

1、使用元件模式 html檔案 <ion-header> <ion-navbar> <ion-title>Home</ion-title> </ion-navbar> </ion-h