Android 基于RecyclerView的Item側(cè)滑刪除

RecyclerView的強(qiáng)大之處就不用多說了茅逮,誰用誰知道哦憔购,本著學(xué)習(xí)的態(tài)度我們來給RecyclerView加上側(cè)滑刪除Item的功能焦匈,話不多說赘淮,先看圖:


ItemRemoveRecyclerView

Gif效果不夠理想辕录,嗚嗚......

其實(shí)核心思想很簡單,就是通過重寫RecyclerView的onTouchEvent()方法來檢測手勢的變化實(shí)現(xiàn)的梢卸,大致的流程如下:
1走诞、根據(jù)手指觸摸的坐標(biāo)點(diǎn)找到對應(yīng)Item的ViewHolder,進(jìn)而得到相應(yīng)的Item布局View蛤高。
2蚣旱、手指繼續(xù)移動,在條件滿足的情況下戴陡,通過scrollBy()使Item布局View內(nèi)容跟隨手指一起移動塞绿,當(dāng)然要注意邊界檢測。
3恤批、手指抬起時异吻,根據(jù)Item布局View內(nèi)容移動的距離以及手指的滑動速度,判斷是否顯示刪除按鈕喜庞,進(jìn)而通過startScroll()使Item布局View自動滑動到目標(biāo)位置诀浪。
4棋返、點(diǎn)擊刪除按鈕則刪除對應(yīng)Item,點(diǎn)擊其它區(qū)域則隱藏刪除按鈕笋妥。

由于Item的側(cè)滑刪除效果需要通過Scroller輔助實(shí)現(xiàn)的懊昨,還不了解Scroller的同學(xué)可以看下這篇文章:Android Scroller實(shí)現(xiàn)View彈性滑動完全解析

接下來看一下具體的實(shí)現(xiàn)過程:
先看一下onTouchEvent的MotionEvent.ACTION_DOWN事件處理:

    public boolean onTouchEvent(MotionEvent e) {
        mVelocityTracker.addMovement(e);

        int x = (int) e.getX();
        int y = (int) e.getY();
        switch (e.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (mDeleteBtnState == 0) {
                    View view = findChildViewUnder(x, y);
                    if (view == null) {
                        return false;
                    }

                    MyViewHolder viewHolder = (MyViewHolder) getChildViewHolder(view);

                    mItemLayout = viewHolder.layout;
                    mPosition = viewHolder.getAdapterPosition();

                    mDelete = (TextView) mItemLayout.findViewById(R.id.item_delete);
                    mMaxLength = mDelete.getWidth();
                    mDelete.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            mListener.onDeleteClick(mPosition);
                            mItemLayout.scrollTo(0, 0);
                            mDeleteBtnState = 0;
                        }
                    });
                } else if (mDeleteBtnState == 3) {
                    mScroller.startScroll(mItemLayout.getScrollX(), 0, -mMaxLength, 0, 200);
                    invalidate();
                    mDeleteBtnState = 0;
                    return false;
                } else {
                    return false;
                }

                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        mLastX = x;
        mLastY = y;
        return super.onTouchEvent(e);
    }

我們規(guī)定刪除按鈕有四個狀態(tài)(mDeleteBtnState):0:關(guān)閉春宣,1:將要關(guān)閉酵颁,2:將要打開,3:打開
當(dāng)刪除按鈕未展示時月帝,即if (mDeleteBtnState == 0)時躏惋,通過findChildViewUnder()方法得到觸摸點(diǎn)對應(yīng)的Item View,接下來通過getChildViewHolder()得到對應(yīng)的ViewHolder嚷辅,有了ViewHolder簿姨,我們就可以解析出Item的布局mItemLayout以及當(dāng)前Item的下標(biāo)mPosition,最后得到mMaxLength 簸搞,即刪除按鈕的寬度也就是Item的最大滑動距離扁位,同時給刪除按鈕綁定事件。
當(dāng)
else if (mDeleteBtnState == 3)
時趁俊,Item上的刪除按鈕完全展示域仇,如果點(diǎn)擊刪除按鈕外的任意區(qū)域則通過startScroll()方法使Item自動右滑直到刪除按鈕完全隱藏,并且onTouchEvent()方法返回flase寺擂,這樣此次事件結(jié)束暇务,不會繼續(xù)傳遞。
如果前兩個條件都不滿足怔软,表示上一次Item的滑動操作尚未結(jié)束垦细,則直接返回false,保證上一次的滑動操作順利完成挡逼。

onTouchEvent的MotionEvent.ACTION_MOVE事件處理代碼如下:

    public boolean onTouchEvent(MotionEvent e) {
        mVelocityTracker.addMovement(e);

        int x = (int) e.getX();
        int y = (int) e.getY();
        switch (e.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_MOVE:
                int dx = mLastX - x;
                int dy = mLastY - y;

                int scrollX = mItemLayout.getScrollX();
                if (Math.abs(dx) > Math.abs(dy)) {
                    isItemMoving = true;
                    if (scrollX + dx <= 0) {//左邊界檢測
                        mItemLayout.scrollTo(0, 0);
                        return true;
                    } else if (scrollX + dx >= mMaxLength) {//右邊界檢測
                        mItemLayout.scrollTo(mMaxLength, 0);
                        return true;
                    }
                    mItemLayout.scrollBy(dx, 0);//item跟隨手指滑動
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }

        mLastX = x;
        mLastY = y;
        return super.onTouchEvent(e);
    }

當(dāng)手指滑動的時候括改,如果水平滑動距離大于垂直滑動距離,則通過scrollBy()方法使Item可跟隨手指左右滑動家坎,當(dāng)然我們進(jìn)行了滑動的邊界檢測嘱能,并不會出現(xiàn)滑動越界的情況哦!

最后看一下onTouchEvent的MotionEvent.ACTION_UP事件處理:

    public boolean onTouchEvent(MotionEvent e) {
        mVelocityTracker.addMovement(e);

        int x = (int) e.getX();
        int y = (int) e.getY();
        switch (e.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:
                if (!isItemMoving && !isDragging && mListener != null) {
                    mListener.onItemClick(mItemLayout, mPosition);
                }
                isItemMoving = false;

                mVelocityTracker.computeCurrentVelocity(1000);//計(jì)算手指滑動的速度
                float xVelocity = mVelocityTracker.getXVelocity();//水平方向速度(向左為負(fù))
                float yVelocity = mVelocityTracker.getYVelocity();//垂直方向速度

                int deltaX = 0;
                int upScrollX = mItemLayout.getScrollX();

                if (Math.abs(xVelocity) > 100 && Math.abs(xVelocity) > Math.abs(yVelocity)) {
                    if (xVelocity <= -100) {//左滑速度大于100乘盖,則刪除按鈕顯示
                        deltaX = mMaxLength - upScrollX;
                        mDeleteBtnState = 2;
                    } else if (xVelocity > 100) {//右滑速度大于100,則刪除按鈕隱藏
                        deltaX = -upScrollX;
                        mDeleteBtnState = 1;
                    }
                } else {
                    if (upScrollX >= mMaxLength / 2) {//item的左滑動距離大于刪除按鈕寬度的一半憔涉,則則顯示刪除按鈕
                        deltaX = mMaxLength - upScrollX;
                        mDeleteBtnState = 2;
                    } else if (upScrollX < mMaxLength / 2) {//否則隱藏
                        deltaX = -upScrollX;
                        mDeleteBtnState = 1;
                    }
                }

                //item自動滑動到指定位置
                mScroller.startScroll(upScrollX, 0, deltaX, 0, 200);
                isStartScroll = true;
                invalidate();

                mVelocityTracker.clear();
                break;
        }

        mLastX = x;
        mLastY = y;
        return super.onTouchEvent(e);
    }

當(dāng)手指抬起時订框,如果之前沒有發(fā)生Item水平滑動、上下滑動列表兜叨、回調(diào)接口不為空穿扳,則認(rèn)為是Item的點(diǎn)擊事件衩侥,執(zhí)行回調(diào)接口里的方法mListener.onItemClick(mItemLayout, mPosition);。接下來計(jì)算出手指在水平以及垂直方向的滑動速度**xVelocity 矛物、yVelocity 茫死,如果if (Math.abs(xVelocity) > 100 && Math.abs(xVelocity) > Math.abs(yVelocity)),則根據(jù)速度判斷手指抬起后Item的滑動情況履羞,if (xVelocity <= -100)代表左滑速度大于等于100峦萎,則將mDeleteBtnState值改為2,代表刪除按鈕將要打開(展示)忆首,同理如果右滑速度大于100則刪除按鈕將要關(guān)閉(隱藏)爱榔,同時計(jì)算出相應(yīng)的滑動距離deltaX **。如果不滿足通過速度的判斷條件則根據(jù)Item的滑動距離來判斷糙及,如果if (upScrollX >= mMaxLength / 2)详幽,即Item左滑的距離大于等于刪除按鈕寬度的一半,則將mDeleteBtnState值改為2浸锨,否則將mDeleteBtnState值改為1唇聘,同時不要忘了計(jì)算deltaX的值。最后通過mScroller.startScroll(upScrollX, 0, deltaX, 0, 200);使Item滑動到指定位置柱搜。

到這里我們的onTouchEvent()實(shí)現(xiàn)原理就分析完了迟郎。

再貼一下computeScroll()的代碼:

public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            mItemLayout.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
        } else if (isStartScroll) {
            isStartScroll = false;
            if (mDeleteBtnState == 1) {
                mDeleteBtnState = 0;
            }
            if (mDeleteBtnState == 2) {
                mDeleteBtnState = 3;
            }
        }
    }

其中isStartScroll代表手指抬起后Item自動滑動的狀態(tài),在MotionEvent.ACTION_DOWN我們將其賦值為true冯凹,代表開始自動滑動谎亩,如果自動滑動結(jié)束則會執(zhí)行else if中的邏輯,重置isStartScroll宇姚、修改mDeleteBtnState最終的狀態(tài)值(打開或者關(guān)閉)匈庭。

手指上下滑動列表時,我們通過onScrollStateChanged()方法浑劳,監(jiān)聽列表滑動的狀態(tài):

public void onScrollStateChanged(int state) {
        super.onScrollStateChanged(state);
        isDragging = state == SCROLL_STATE_DRAGGING;
    }

以判斷是否正在上下滑動列表阱持。

到此,我們把大致的實(shí)現(xiàn)方法就分析完了魔熏,如有不合理的地方歡迎指出V匝省!蒜绽!如有興趣可下載源碼看看:點(diǎn)我下載哦

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末镶骗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子躲雅,更是在濱河造成了極大的恐慌鼎姊,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異相寇,居然都是意外死亡慰于,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門唤衫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來婆赠,“玉大人,你說我怎么就攤上這事佳励⌒堇铮” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵植兰,是天一觀的道長份帐。 經(jīng)常有香客問我,道長楣导,這世上最難降的妖魔是什么废境? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮筒繁,結(jié)果婚禮上噩凹,老公的妹妹穿的比我還像新娘。我一直安慰自己毡咏,他們只是感情好驮宴,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著呕缭,像睡著了一般堵泽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上恢总,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天迎罗,我揣著相機(jī)與錄音,去河邊找鬼片仿。 笑死纹安,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的砂豌。 我是一名探鬼主播厢岂,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼阳距!你這毒婦竟也來了塔粒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤筐摘,失蹤者是張志新(化名)和其女友劉穎卒茬,沒想到半個月后映跟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扬虚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了球恤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辜昵。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖咽斧,靈堂內(nèi)的尸體忽然破棺而出堪置,到底是詐尸還是另有隱情,我是刑警寧澤张惹,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布舀锨,位于F島的核電站,受9級特大地震影響宛逗,放射性物質(zhì)發(fā)生泄漏坎匿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一雷激、第九天 我趴在偏房一處隱蔽的房頂上張望替蔬。 院中可真熱鬧,春花似錦屎暇、人聲如沸承桥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凶异。三九已至,卻和暖如春挤巡,著一層夾襖步出監(jiān)牢的瞬間剩彬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工玄柏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留襟衰,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓粪摘,卻偏偏與公主長得像瀑晒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子徘意,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,072評論 25 707
  • 什么是View View 是 Android 中所有控件的基類苔悦。 View的位置參數(shù) View 的位置由它的四個頂...
    acc8226閱讀 1,170評論 0 7
  • 12.24深圳南山區(qū)聚橙劇院趙雷我們的時光劇場巡回演唱會 剛開始知道雷子要來深圳的時候我并不準(zhǔn)備去看玖详,原因是因?yàn)闆]...
    初念于你閱讀 382評論 0 0
  • 青草綠樹紅花把介。 臺榭高樓民家。 洛城熱浪人煞蟋座。 殘陽西斜拗踢, 好男兒懷天下。
    江郎舞風(fēng)閱讀 229評論 0 0
  • 無淚空等倪紅亂情向臀, 曠野無痕花草凋零巢墅。 天冷清清卻無悲風(fēng), 你不等因緣盡數(shù)斷券膀。 你不聽前后因果冥君纫。 颼颼颯颯起了風(fēng)。
    姓梁心不涼閱讀 351評論 0 0