類似支付寶應(yīng)用管理界面——RecycleView+ItemTouchHelper實現(xiàn)拖拽滑動

要實現(xiàn)RecycleView中的拖拽滑動,在以往的經(jīng)驗中經(jīng)常要依賴GestureDetectors、onInterceptTouchEvent等來實現(xiàn),然而在RecyclerView上添加拖動特性有一個非常簡單的方法它就是:ItemTouchHelper。

一、效果圖

以下就是通過RecycleView+ItemTouchHelper實現(xiàn)拖拽滑動的效果圖,看起來有沒有很炫酷。其實實現(xiàn)起來很簡單,我們接下來就開始介紹。


二、ItemTouchHelper的介紹

ItemTouchHelper是一個強大的工具,它處理好了關(guān)于在RecyclerView上添加拖動排序與滑動刪除的所有事情。它是RecyclerView.ItemDecoration的子類,也就是說它可以輕易的添加到幾乎所有的LayoutManager和Adapter中。它還可以和現(xiàn)有的item動畫一起工作,提供受類型限制的拖放動畫等等。

1.添加依賴
compile 'com.android.support:recyclerview-v7:25.1.0'

因為要使用RecycleView,同時ItemTouchHelper也是RecycleView中的類。

2.自定義ItemTouchHelper.Callback類

為了使用ItemTouchHelper,你需要實現(xiàn)ItemTouchHelper.Callback接口,通過這個接口,你可以監(jiān)聽“move”和 “swipe”事件,在這里你也可以控制View的選擇狀態(tài)和重寫默認動畫。

必須實現(xiàn)主要的回調(diào)方法:

getMovementFlags(RecyclerView, ViewHolder)
onMove(RecyclerView, ViewHolder, ViewHolder)
onSwiped(ViewHolder, int)

具體解釋這三個方法:

public int  getMovementFlags(RecyclerView recyclerView, 
RecyclerView.ViewHolder viewHolder) {
          int dragFlags = ItemTouchHelper.UP| ItemTouchHelper.DOWN;
          int swipeFlags = ItemTouchHelper.START| ItemTouchHelper.END;
          return makeMovementFlags(dragFlags, swipeFlags);
}

ItemTouchHelper允許你判斷事件方向。但你必須覆寫getMovementFlags()方法去指定支持哪些方向。使用ItemTouchHelper.makeMovementFlags(int, int)創(chuàng)建代表方向的Flag。這里我們同時支持drag和swipe。實現(xiàn)這個方法,ItemTouchHelper可以只能drag而不能swipe(反之亦然),總之根據(jù)自己的需求指定。

onMove(RecyclerView, ViewHolder, ViewHolder)
onSwiped(ViewHolder, int)

當(dāng)Item移動或者滑動時,會回調(diào)這兩個方法,然后可以在這兩個方法內(nèi)部設(shè)置回調(diào)通知更新適配器或者頁面顯示的數(shù)據(jù)。

我們還將使用2個幫助方法:

@Override
public boolean isLongPressDragEnabled() {
      return true;
}

實現(xiàn)isLongPressDragEnabled()方法返回true去支持長按RecyclerView的item時的drag事件?;蛘?,也可以調(diào)用ItemTouchHelper.startDrag(RecyclerView.ViewHolder) 方法來開始一個拖動。

@Override
public boolean isItemViewSwipeEnabled() {
        return true;
}

實現(xiàn)isItemViewSwipeEnabled()方法返回true開啟觸摸視圖時的swipe功能。另外ItemTouchHelper.startSwipe(RecyclerView.ViewHolder)也開始swipe事件。

設(shè)置給RecycleView:
實現(xiàn)了以上的方法后,就會監(jiān)聽到拖拽和滑動的手勢,并會處理相關(guān)操作。
接下來需要做的就是把實現(xiàn)的自定義ItemTouchHelper.Callback類設(shè)置給RecycleView。

ItemDragHelperCallback callback = new ItemDragHelperCallback(mMineAdapter);
ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(mNewsChannelMineRv);

以上就是關(guān)于RecycleView+ItemTouchHelper實現(xiàn)拖拽滑動的簡單介紹,下面為實現(xiàn)上述效果圖,具體講解其實現(xiàn)過程。

三、RecycleView+ItemTouchHelper實現(xiàn)拖拽的實例應(yīng)用

1.布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:background="@color/white"
              android:clipToPadding="true"
              android:fitsSystemWindows="true"
              android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        style="@style/action_bar"
        android:background="@color/colorPrimary"
        app:navigationIcon="@drawable/ic_arrow_back"
        app:theme="@style/AppTheme.PopupOverlay"
        app:title="@string/channel_manage" />

    <TextView
        style="@style/news_channel_sort_title"
        android:text="我的頻道  長按并拖拽可排序" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/news_channel_mine_rv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:overScrollMode="never"></android.support.v7.widget.RecyclerView>

    <TextView
        style="@style/news_channel_sort_title"
        android:text="@string/更多頻道  點擊添加" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/news_channel_more_rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:overScrollMode="never"></android.support.v7.widget.RecyclerView>
</LinearLayout>

一共用了兩個RecyclerView來分別實現(xiàn)我的頻道和更多頻道的內(nèi)容,其實可以使用一個RecyclerView通過判斷類型來實現(xiàn)多布局類型,效果會更好,后續(xù)會嘗試,暫時先這樣了。感興趣的可以參考我的另一篇文章來自行實現(xiàn)。

2.點擊Item增刪效果的實現(xiàn)

先來個簡單的,就是點擊Item后,我的頻道和更多頻道中,一個頻道刪除點擊的頻道,另一個頻道增加該頻道。

思路:設(shè)置Item的點擊事件的監(jiān)聽

在Adaper類中,設(shè)置監(jiān)聽接口,并提供傳入接口對象的方法讓Activity調(diào)用

 //Item點擊事件的監(jiān)聽接口
    public interface OnItemClickListener {
        void onItemClick(View view, int position);
    }
 public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        mOnItemClickListener = onItemClickListener;
    }

當(dāng)點擊Item后,出發(fā)監(jiān)聽,產(chǎn)生回調(diào)

 if (mOnItemClickListener != null) {
            holder.mLayout.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {

                    if (!table.getNewsChannelFixed()) {
                        //對項目點擊后增刪操作的監(jiān)聽
                        mOnItemClickListener.onItemClick(view, holder.getLayoutPosition());
                    }
                }
            });
        }

在Activity中根據(jù)傳入的數(shù)據(jù)進行操作

我的頻道所對應(yīng)的RecyclerView的操作

mMineAdapter.setOnItemClickListener(new NewsChannelAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                NewsChannelTable newsChannel = mMineAdapter.getAdapterData().get(position);
                mMoreAdapter.getAdapterData().add(newsChannel);
                mMoreAdapter.notifyDataSetChanged();
                mMineAdapter.getAdapterData().remove(position);
                mMineAdapter.notifyDataSetChanged();
                //進行添加或刪除操作后,要更新的列表 進行存儲
                mPresenter.onItemAddOrRemove((ArrayList<NewsChannelTable>) mMineAdapter.getAdapterData(), (ArrayList<NewsChannelTable>) mMoreAdapter.getAdapterData());

            }
        });

更多頻道所對應(yīng)的RecyclerView的操作

mMoreAdapter.setOnItemClickListener(new NewsChannelAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                if(mMineAdapter.getAdapterData().size()==7){
                    ToastUitl.showShort("最多只能添加7個");
                }else{
                NewsChannelTable newsChannel = mMoreAdapter.getAdapterData().get(position);
                mMoreAdapter.getAdapterData().remove(position);
                mMoreAdapter.notifyDataSetChanged();
                mMineAdapter.getAdapterData().add(newsChannel);
                mMineAdapter.notifyDataSetChanged();
                List<NewsChannelTable> data = mMineAdapter.getAdapterData();
                for (NewsChannelTable table : data) {
                    System.out.println(table);
                }
                //進行添加或刪除操作后,要更新的列表 進行存儲
                mPresenter.onItemAddOrRemove((ArrayList<NewsChannelTable>) mMineAdapter.getAdapterData(), (ArrayList<NewsChannelTable>) mMoreAdapter.getAdapterData());
            }

主要實現(xiàn)兩個內(nèi)容:
第一:RecyclerView中內(nèi)容數(shù)據(jù)的修改更新,呈現(xiàn)點擊后增刪的效果。
第二:調(diào)用方法通知進行緩存處理,記錄修改后的效果。同時通知新聞首頁中頻道的更新顯示。關(guān)于第二部分的內(nèi)容不詳細介紹了,可以看最下方的源碼地址。

3.自定義ItemTouchHelper.Callback類實現(xiàn)拖拽效果
@Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        //根據(jù)recyclerView的布局,進行設(shè)置拖拽的方向
        int dragFlags = setDragFlags(recyclerView);
        //不允許進行滑動
        int swipeFlags = 0;
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    private int setDragFlags(RecyclerView recyclerView) {
        int dragFlags;
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager || layoutManager instanceof StaggeredGridLayoutManager) {
            dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
        } else {
            dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        }
        return dragFlags;
    }

先根據(jù)布局判斷支持的拖拽的方向,如果是GridLayoutManager 和StaggeredGridLayoutManager支持上下左右拖拽,如果是LinearLayoutManager支持上下拖拽,本例中不支持滑動操作。

 @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        return mOnItemMoveListener.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());

    }

監(jiān)聽移動事件,其中mOnItemMoveListener.onItemMove是對移動的監(jiān)聽的回調(diào),判斷是否可以移動并通知Adapter類數(shù)據(jù)更新。

 @Override
    public boolean onItemMove(int fromPosition, int toPosition) {
        if (isChannelFixed(fromPosition, toPosition)) {
            return false;
        }
        //在我的頻道中進行子頻道的移動
        Collections.swap(getAdapterData(), fromPosition, toPosition);
        notifyItemMoved(fromPosition, toPosition);
        //通知順序變換,存儲,設(shè)置頻道順序,以及顯示的順序
        System.out.println("發(fā)送移動的消息");
        EventBus.getDefault().post(new ChannelBean(getAdapterData()));
        return true;
    }
//不能移動頭條
    private boolean isChannelFixed(int fromPosition, int toPosition) {
        return fromPosition == 0 || toPosition == 0;
    }

兩種情況:
第一:如果移動的是“頭條”頻道或者移動到“頭條”頻道,返回false,則不能進行移動。
第二:不是上面的情況。更新我的頻道欄目中頻道的顯示順序,同時通知數(shù)據(jù)緩存并通知新聞首頁頻道順序的更新。關(guān)于這部分的詳細內(nèi)容,可以看最下方的源碼。

 //返回true 允許拖拽
    @Override
    public boolean isLongPressDragEnabled() {
        return mIsLongPressEnabled;
    }

是否允許拖拽,通過外部傳入來開啟。

public void setLongPressEnabled(boolean longPressEnabled) {
        mIsLongPressEnabled = longPressEnabled;
    }

在Adapter類中,根據(jù)觸摸的Item的類型來判斷是否開啟長按拖拽。

if (mItemDragHelperCallback != null) {
            holder.mLayout.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    mItemDragHelperCallback.setLongPressEnabled(table.getNewsChannelIndex() == 0 ? false : true);
                    return false;
                }
            });
        }

如果觸摸的對象是“頭條”頻道,則不開啟拖拽,其他情況就會開啟長按。

4.設(shè)置給RecycleView
       //Adapter類實現(xiàn)了OnItemMoveListener的接口,將其傳入ItemDragHelperCallback方便接口回調(diào)
        ItemDragHelperCallback callback = new ItemDragHelperCallback(mMineAdapter);
       //將自定義的ItemDragHelperCallback類傳給ItemTouchHelper
        ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
       //將ItemTouchHelper設(shè)置給RecyclerView
        touchHelper.attachToRecyclerView(mNewsChannelMineRv);

通過以上步驟就可以實現(xiàn)效果圖中的效果,真實效果還是不錯的。源碼地址,感興趣的看一下,給個Star支持下,看項目中的NewsChannelActivity部分即可。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容