RecyclerView使用(四)

這篇文章會(huì)講解RecyclerView的Item拖動(dòng)排序與滑動(dòng)刪除椭蹄。
ItemTouchHelper

This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.
ItemTouchHelper是一個(gè)支持RecyclerView的滑動(dòng)刪除和拖拽的工具類(lèi)杨何。也就是說(shuō)我們可以通過(guò)它來(lái)簡(jiǎn)單的完成RecyclerView的Item拖動(dòng)排序與滑動(dòng)刪除。

效果如圖

拖動(dòng)和滑動(dòng).gif

一哩陕、使用ItemTouchHelper.Callback

要使用ItemTouchHelper需要?jiǎng)?chuàng)建一個(gè)類(lèi)繼承 ItemTouchHelper.Callback 抽象類(lèi)平项,通過(guò)這個(gè)類(lèi)可以讓你監(jiān)聽(tīng)“move”與 “swipe”事件,而且還可以控制view被選中的狀態(tài)以及重寫(xiě)默認(rèn)動(dòng)畫(huà)的地方萌踱。如果你只是想要一個(gè)基本的實(shí)現(xiàn)葵礼,有一個(gè)系統(tǒng)封裝好的可以使用:SimpleCallback。但是為了了解其工作機(jī)制并鸵,我們還是自己實(shí)現(xiàn)鸳粉。
這是這個(gè)類(lèi)需要些的全部代碼

public class ItemTouchHelperCallback extends ItemTouchHelper.Callback{

    private ItemTouchHelperAdapter itemTouchHelperAdapter;//RecyclerView的適配器

    /**
     * 多參的構(gòu)造方法
     * @param itemTouchHelperAdapter 需要適配器來(lái)改變數(shù)據(jù)的位置,以及通知它來(lái)刷新
     */
    public ItemTouchHelperCallback(ItemTouchHelperAdapter itemTouchHelperAdapter) {
        this.itemTouchHelperAdapter = itemTouchHelperAdapter;

    }

    /**
     * RecyclerView item支持長(zhǎng)按進(jìn)入拖動(dòng)操作
     */
    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    /**
     * RecyclerView item任意位置觸發(fā)啟用滑動(dòng)操作
     */
    @Override
    public boolean isItemViewSwipeEnabled() {
        return true;
    }

    /**
     * 指定可以支持的拖動(dòng)和滑動(dòng)的方向园担,上下為拖動(dòng)(drag)届谈,左右為滑動(dòng)(swipe)
     */
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        //判斷布局管理器是否為GridLayoutManager
        if (recyclerView.getLayoutManager() instanceof GridLayoutManager || recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager) {
            final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
            //不需要滑動(dòng)
            final int swipeFlags = 0;
            return makeMovementFlags(dragFlags, swipeFlags);
        } else {
            final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
            return makeMovementFlags(dragFlags, swipeFlags);
        }
    }

    /**
     * 拖動(dòng)操作會(huì)執(zhí)行的方法
     */
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        //判斷如果viewHolder類(lèi)型不一樣,返回False
        if (viewHolder.getItemViewType() != target.getItemViewType()) {
            return false;
        }
        //通知適配器改變數(shù)據(jù) 以及刷新
        itemTouchHelperAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    /**
     * 滑動(dòng)操作會(huì)執(zhí)行的方法
     */
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        //通知適配器刪除數(shù)據(jù) 以及刷新
        itemTouchHelperAdapter.onItemDismiss(viewHolder.getAdapterPosition());
    }

    /**
     * 當(dāng)動(dòng)作開(kāi)始的時(shí)候調(diào)用
     */
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        //動(dòng)作不是空閑狀態(tài)調(diào)用
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            if (viewHolder instanceof ItemTouchHelperAdapter.ItemTouchHelperViewHolder) {
                //獲取執(zhí)行的item的ViewHolder
                ItemTouchHelperAdapter.ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperAdapter.ItemTouchHelperViewHolder) viewHolder;
                //選中狀態(tài)回調(diào)
                itemViewHolder.onItemSelected();
            }
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

    /**
     * 當(dāng)動(dòng)作之中的時(shí)候調(diào)用
     */
    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
            //自定義滑動(dòng)動(dòng)畫(huà)(讓它根據(jù)滑動(dòng)的距離與itemView的寬度的比例來(lái)改變透明度)
            final float alpha = 1.0f - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
            //設(shè)置透明度
            viewHolder.itemView.setAlpha(alpha);
            //設(shè)置平移動(dòng)畫(huà)
            viewHolder.itemView.setTranslationX(dX);
        } else {
            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        }
    }

    /**
     * 當(dāng)動(dòng)作結(jié)束的時(shí)候調(diào)用
     */
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        if (viewHolder instanceof ItemTouchHelperAdapter.ItemTouchHelperViewHolder) {
            //獲取執(zhí)行的item的ViewHolder
            ItemTouchHelperAdapter.ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperAdapter.ItemTouchHelperViewHolder) viewHolder;
            //未選中狀態(tài)回調(diào)
            itemViewHolder.onItemNormal();
        }
    }
}
  1. 繼承ItemTouchHelper.Callback的抽象類(lèi)之后會(huì)讓你實(shí)現(xiàn)這三個(gè)方法
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) 
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target)
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) 
  • getMovementFlags在這個(gè)方法里面寫(xiě)通過(guò)item的移動(dòng)方向來(lái)判斷是拖動(dòng)或滑動(dòng)操作弯汰。
  • onMove在這個(gè)方法里面寫(xiě)拖動(dòng)操作后的執(zhí)行方法艰山,通過(guò)適配器來(lái)改變數(shù)據(jù)位置,以及刷新
  • onSwiped在這個(gè)方法里面寫(xiě)滑動(dòng)操作后的執(zhí)行方法咏闪,通過(guò)適配器來(lái)刪除數(shù)據(jù)位置曙搬,以及刷新
  1. 之后還需要些兩個(gè)輔助動(dòng)作的方法
@Override
public boolean isLongPressDragEnabled()
@Override
public boolean isItemViewSwipeEnabled()
  • isLongPressDragEnabled這個(gè)方法默認(rèn)返回ture,支持長(zhǎng)按進(jìn)入拖動(dòng)操作
  • isItemViewSwipeEnabled這個(gè)方法指的是在view任意位置觸摸事件發(fā)生時(shí)啟用滑動(dòng)操作
  1. 如果寫(xiě)一些在有關(guān)操作的動(dòng)畫(huà)鸽嫂,復(fù)寫(xiě)下面三個(gè)方法
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState)
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) 
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)
  • onSelectedChanged指的是你在開(kāi)始操作(也就是說(shuō)選中item)的時(shí)候希望做的對(duì)item的改變纵装,比如說(shuō)改變item的背景顏色,還可以通過(guò)參數(shù)actionState來(lái)判斷什么操作
  • onChildDraw指的是你在操作(一般指的是滑動(dòng))途中希望做的對(duì)item的改變据某,比如寫(xiě)了一個(gè)透明度改變的動(dòng)畫(huà)
  • clearView操作結(jié)束后的做的對(duì)item的改變橡娄,讓它恢復(fù)原有狀態(tài)

二、需要在Adapter寫(xiě)的方法

全部的代碼

public class ItemTouchHelperAdapter extends RecyclerView.Adapter<ItemTouchHelperAdapter.ItemTouchHelperViewHolder> {

    private ArrayList<String> list;//要顯示的數(shù)據(jù)
    private Context context;//上下文
    private ImgViewTouchListener imgViewTouchListener;//需要外面實(shí)現(xiàn)的接口

    /*
    多參的構(gòu)造方法
     */
    public ItemTouchHelperAdapter(ArrayList<String> list, Context context) {
        this.list = list;
        this.context = context;
    }

    /**
     * 創(chuàng)建視圖
     */
    @Override
    public ItemTouchHelperViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //創(chuàng)建視圖
        View view = LayoutInflater.from(context).inflate(R.layout.item_touch_helper_item,parent,false);
        //實(shí)例化MainViewHolder---- 傳View過(guò)去
        ItemTouchHelperViewHolder holder = new ItemTouchHelperViewHolder(view);
        return holder;
    }

    /**
     * 初始化控件
     */
    public class ItemTouchHelperViewHolder extends RecyclerView.ViewHolder{
        public TextView textView;
        public ImageView imageView;
        public ItemTouchHelperViewHolder(View itemView) {
            super(itemView);
            //初始化控件
            textView = (TextView) itemView.findViewById(R.id.text);
            imageView = (ImageView) itemView.findViewById(R.id.imgView);
        }

        /**
         * 操作開(kāi)始時(shí)調(diào)用的方法
         */
        public void onItemSelected() {
            itemView.setBackgroundColor(Color.BLACK);
        }
        /**
         * 操作完成后調(diào)用的方法
         */
        public void onItemNormal() {
            itemView.setBackgroundColor(0);
        }
    }



    /**
     * 填充數(shù)據(jù)
     */
    @Override
    public void onBindViewHolder(final ItemTouchHelperViewHolder holder, int position) {
        //獲取要填充的值
        String content = list.get(position);
        //控件中設(shè)置值
        holder.textView.setText(content);
        holder.imageView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                imgViewTouchListener.imgViewOnTouch(event,holder);
                return false;
            }
        });

    }

    /**
     * 獲取item的總個(gè)數(shù)
     */
    @Override
    public int getItemCount() {
        return list.size();
    }

    /**
     * 拖動(dòng)方法
     */
    public boolean onItemMove(int fromPosition, int toPosition) {
        //互換列表中指定位置的數(shù)據(jù)
        Collections.swap(list, fromPosition, toPosition);
        //通知改變
        notifyItemMoved(fromPosition, toPosition);
        return true;
    }

    /**
     * 滑動(dòng)刪除的方法
     */
    public void onItemDismiss(int position) {
        list.remove(position);
        notifyItemRemoved(position);
    }

    /*
    暴露出去的點(diǎn)擊事件的方法
     */
    public void setImgViewTouchListener(ImgViewTouchListener imgViewTouchListener){
        this.imgViewTouchListener = imgViewTouchListener;
    }

    /*
    點(diǎn)擊要實(shí)現(xiàn)的接口
     */
    public interface ImgViewTouchListener{
        public void imgViewOnTouch(MotionEvent event,ItemTouchHelperViewHolder holder);
    }

}
  • 寫(xiě)一個(gè)拖動(dòng)操作調(diào)用的方法和一個(gè)滑動(dòng)刪除調(diào)用的方法癣籽,在上一個(gè)類(lèi)的onMove和onSwiped方法中調(diào)用挽唉。
  • 在ViewHolder內(nèi)部類(lèi)寫(xiě)開(kāi)始操作和結(jié)束操作的方法滤祖,在上一個(gè)類(lèi)的onSelectedChanged和clearView方法中調(diào)用
  • 最后我希望在按下圖片才開(kāi)始拖拽操作,所有需要寫(xiě)一個(gè)點(diǎn)擊方法(通過(guò)上一篇文章的方式)瓶籽。

三匠童、在Activity中

//實(shí)例化ItemTouchHelper對(duì)象
final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelperCallback(adapter));
//itemTouchHelper與RecyclerView關(guān)聯(lián)
itemTouchHelper.attachToRecyclerView(rcTouch);
//設(shè)置ImgView的觸摸監(jiān)聽(tīng)事件
adapter.setImgViewTouchListener(new ItemTouchHelperAdapter.ImgViewTouchListener() {
    @Override
    public void imgViewOnTouch(MotionEvent event, ItemTouchHelperAdapter.ItemTouchHelperViewHolder holder) {
        //判斷是否按下圖片
        if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
            //按下則開(kāi)始拖動(dòng)
            itemTouchHelper.startDrag(holder);
        }
    }
});
  • 首先實(shí)例化一個(gè)ItemTouchHelper類(lèi),并且在參數(shù)之前寫(xiě)的ItemTouchHelperCallback類(lèi)的對(duì)象
  • 然后將其與RecyclerView關(guān)聯(lián)
  • 設(shè)置ImgView的觸摸監(jiān)聽(tīng)棘劣,在里面通過(guò)ItemTouchHelper類(lèi)來(lái)調(diào)用開(kāi)始拖動(dòng)的方法

RecyclerView的系列的文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市茬暇,隨后出現(xiàn)的幾起案子首昔,更是在濱河造成了極大的恐慌,老刑警劉巖糙俗,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勒奇,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡巧骚,警方通過(guò)查閱死者的電腦和手機(jī)赊颠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)劈彪,“玉大人竣蹦,你說(shuō)我怎么就攤上這事〔着” “怎么了痘括?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)滔吠。 經(jīng)常有香客問(wèn)我纲菌,道長(zhǎng),這世上最難降的妖魔是什么疮绷? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任翰舌,我火速辦了婚禮,結(jié)果婚禮上冬骚,老公的妹妹穿的比我還像新娘椅贱。我一直安慰自己,他們只是感情好只冻,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布夜涕。 她就那樣靜靜地躺著,像睡著了一般属愤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上酸役,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天住诸,我揣著相機(jī)與錄音驾胆,去河邊找鬼。 笑死贱呐,一個(gè)胖子當(dāng)著我的面吹牛丧诺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播奄薇,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼驳阎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了馁蒂?” 一聲冷哼從身側(cè)響起呵晚,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沫屡,沒(méi)想到半個(gè)月后饵隙,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沮脖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年金矛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片勺届。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡驶俊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出免姿,到底是詐尸還是另有隱情饼酿,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布养泡,位于F島的核電站嗜湃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏澜掩。R本人自食惡果不足惜购披,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肩榕。 院中可真熱鬧刚陡,春花似錦、人聲如沸株汉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)乔妈。三九已至蝙云,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間路召,已是汗流浹背勃刨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工波材, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人身隐。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓廷区,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親贾铝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子隙轻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容