三種方式實現(xiàn)RecyclerView的Item點擊事件

自從開始使用 RecyclerView 代替 ListView ,會發(fā)現(xiàn)有很多地方需要學(xué)習(xí).前一段時間的學(xué)習(xí)記錄有:

RecyclerView的滾動事件研究 - DevWiki

RecyclerView的ViewHolder和Adapter的封裝優(yōu)化 - DevWiki

RecyclerView問題記錄 - DevWiki

今天來學(xué)習(xí)一下如何實現(xiàn) RecyclerView 的Item的點擊事件.實現(xiàn)Item的點擊事件有三種方式:

通過 RecyclerView 已有的方法 addOnItemTouchListener() 實現(xiàn)

在創(chuàng)建 ItemView 時添加點擊監(jiān)聽

當(dāng) ItemView attach RecyclerView 時實現(xiàn)

  1. 通過 RecyclerView 的 addOnItemTouchListener() 實現(xiàn)

1.1 查看源碼

查看 RecyclerView 源碼可以看到, RecyclerView 預(yù)留了一個Item的觸摸事件方法:

/**

  • Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
  • to child views or this view's standard scrolling behavior.
  • <p>Client code may use listeners to implement item manipulation behavior. Once a listener
  • returns true from
  • {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
  • {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
  • for each incoming MotionEvent until the end of the gesture.</p>
  • @param listener Listener to add
  • @see SimpleOnItemTouchListener
    */
    public void addOnItemTouchListener(OnItemTouchListener listener) {
    mOnItemTouchListeners.add(listener);
    }
    通過注釋我們可知,此方法是在滾動事件之前調(diào)用.需要傳入一個 OnItemTouchListener 對象. OnItemTouchListener 的代碼如下:

public static interface OnItemTouchListener {

public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);

public void onTouchEvent(RecyclerView rv, MotionEvent e);

public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);

}
此接口還提供了一個實現(xiàn)類,且官方推薦使用該實現(xiàn)類 SimpleOnItemTouchListener

/**

  • An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies and
  • default return values.

<

p>

  • You may prefer to extend this class if you don't need to override all methods. Another

  • benefit of using this class is future compatibility. As the interface may change, we'll

  • always provide a default implementation on this class so that your code won't break when

  • you update to a new version of the support library.
    */
    public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
    return false;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    }
    }
    在觸摸接口中,當(dāng)觸摸時會回調(diào)一個 MotionEvent 對象,通過使用 GestureDetectorCompat 來解析用戶的操作.

1.2 實現(xiàn)點擊事件監(jiān)聽

寫一個 ItemClickListener 類繼承 SimpleOnItemTouchListener ,構(gòu)造時傳入 RecyclerView 對象和Item點擊回調(diào),并覆寫父類的 boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) 方法,具體代碼如下:

/**

  • 點擊事件
  • Created by DevWiki on 2016/7/16.
    */

public class ItemClickListener extends RecyclerView.SimpleOnItemTouchListener {

private OnItemClickListener clickListener;
private GestureDetectorCompat gestureDetector;

public interface OnItemClickListener {

    void onItemClick(View view, int position);

    void onItemLongClick(View view, int position);
}

public ItemClickListener(final RecyclerView recyclerView,
                         OnItemClickListener listener) {
    this.clickListener = listener;
    gestureDetector = new GestureDetectorCompat(recyclerView.getContext(),
            new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onSingleTapUp(MotionEvent e) {
                    View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());
                    if (childView != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
                        clickListener.onItemClick(childView, recyclerView.getChildAdapterPosition(childView));
                    }
                    return true;
                }

                @Override
                public void onLongPress(MotionEvent e) {
                    View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());
                    if (childView != null && clickListener != null) {
                        clickListener.onItemLongClick(childView,
                                recyclerView.getChildAdapterPosition(childView));
                    }
                }
            });
}

@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
    gestureDetector.onTouchEvent(e);
    return false;
}

}
在 GestureDetectorCompat 的手勢回調(diào)中我們覆寫:

boolean onSingleTapUp(MotionEvent e)
void onLongPress(MotionEvent e)
1.3 使用事件監(jiān)聽

在 RecyclerView 的對象中添加 addOnItemTouchListener() 方法,然后在回調(diào)中處理你需要的事件:

recyclerView.addOnItemTouchListener(new SingleItemClickListener(recyclerView,
new SingleItemClickListener.OnItemClickListener() {

        @Override
        public void onItemClick(View view, int position) {
            DevLog.i("touch click name:" + position);
            Toast.makeText(SingleActivity.this, "touch click:" + position, Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onItemLongClick(View view, int position) {
            DevLog.i("touch long click:" + position);
            Toast.makeText(SingleActivity.this, "touch long click:" + position, Toast.LENGTH_SHORT).show();
        }
    }));
  1. 在創(chuàng)建 ItemView 時添加點擊監(jiān)聽

這種方法和 ListView 一樣,在Adapter里面創(chuàng)建 View 時添加點擊事件.比如:

@Override
public void bindCustomViewHolder(SingleHolder holder, final int position) {
Person person = getItem(position);
holder.nameView.setText(person.getName());
holder.ageView.setText(String.valueOf(person.getAge()));

if (clickListener != null) {
    holder.nameView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            clickListener.onNameClick(position);
        }
    });
    holder.ageView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            clickListener.onAgeClick(position);
        }
    });
}

}
然后在Adapter對象上添加監(jiān)聽回調(diào):

singleAdapter.setClickListener(new SingleAdapter.OnSingleItemClickListener() {
@Override
public void onNameClick(int position) {
DevLog.i("adapter click name:" + position);
Toast.makeText(SingleActivity.this, "adapter click name:" + position, Toast.LENGTH_SHORT).show();
}

@Override
public void onAgeClick(int position) {
    DevLog.i("adapter click age:" + position);
    Toast.makeText(SingleActivity.this, "adapter click name:" + position, Toast.LENGTH_SHORT).show();
}

});

  1. 當(dāng) ItemView attach RecyclerView 時實現(xiàn)

該實現(xiàn)方法是在閱讀國外的一篇博客時發(fā)現(xiàn)的,原文鏈接如下: Getting your clicks on RecyclerView

實現(xiàn)的代碼如下:

public class ItemClickSupport {
private final RecyclerView mRecyclerView;
private OnItemClickListener mOnItemClickListener;
private OnItemLongClickListener mOnItemLongClickListener;

private View.OnClickListener mOnClickListener = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (mOnItemClickListener != null) {
            RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
            mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v);
        }
    }
};

private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        if (mOnItemLongClickListener != null) {
            RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
            return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v);
        }
        return false;
    }
};

private RecyclerView.OnChildAttachStateChangeListener mAttachListener
        = new RecyclerView.OnChildAttachStateChangeListener() {
    @Override
    public void onChildViewAttachedToWindow(View view) {
        if (mOnItemClickListener != null) {
            view.setOnClickListener(mOnClickListener);
        }
        if (mOnItemLongClickListener != null) {
            view.setOnLongClickListener(mOnLongClickListener);
        }
    }

    @Override
    public void onChildViewDetachedFromWindow(View view) {}
};

private ItemClickSupport(RecyclerView recyclerView) {
    mRecyclerView = recyclerView;
    mRecyclerView.setTag(R.id.item_click_support, this);
    mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
}

public static ItemClickSupport addTo(RecyclerView view) {
    ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
    if (support == null) {
        support = new ItemClickSupport(view);
    }
    return support;
}

public static ItemClickSupport removeFrom(RecyclerView view) {
    ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
    if (support != null) {
        support.detach(view);
    }
    return support;
}

public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
    mOnItemClickListener = listener;
    return this;
}

public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
    mOnItemLongClickListener = listener;
    return this;
}

private void detach(RecyclerView view) {
    view.removeOnChildAttachStateChangeListener(mAttachListener);
    view.setTag(R.id.item_click_support, null);
}

public interface OnItemClickListener {
    void onItemClicked(RecyclerView recyclerView, int position, View v);
}

public interface OnItemLongClickListener {
    boolean onItemLongClicked(RecyclerView recyclerView, int position, View v);
}

}
上面的代碼中給 RecyclerView 設(shè)置了 OnChildAttachStateChangeListener 事件監(jiān)聽,當(dāng)子 View attach RecyclerView 時設(shè)置事件監(jiān)聽.

private RecyclerView.OnChildAttachStateChangeListener mAttachListener
= new RecyclerView.OnChildAttachStateChangeListener() {
@Override
public void onChildViewAttachedToWindow(View view) {
if (mOnItemClickListener != null) {
view.setOnClickListener(mOnClickListener);
}
if (mOnItemLongClickListener != null) {
view.setOnLongClickListener(mOnLongClickListener);
}
}

    @Override
    public void onChildViewDetachedFromWindow(View view) {}
};
  1. 三種方式對比

以上三種方式分別是:

通過 RecyclerView 已有的方法 addOnItemTouchListener() 實現(xiàn)

在創(chuàng)建 ItemView 時添加點擊監(jiān)聽

當(dāng) ItemView attach RecyclerView 時實現(xiàn)

從以上三種方式的實現(xiàn)過程可知:

三種均可實現(xiàn) ItemView 的點擊事件和長按事件的監(jiān)聽.

第一種方式可以很方便獲取用戶點擊的坐標(biāo).

第二種和第三種方式可以很方便對 ItemView 中的子 View 進(jìn)行監(jiān)聽.

第一種方式和第三種方式可以寫在單獨的類中,相對于第二種寫在 Adapter 的方式可使代碼更獨立整潔

綜上所述:

如果你只想監(jiān)聽 ItemView 的點擊事件或長按事件,三種方式均可.

如果你想監(jiān)聽 ItemView 中每個子 View 的點擊事件,采用第二種或者第三種比較方面.

原文地址:http://blog.devwiki.net/index.php/2016/07/24/three-ways-click-recyclerview-item.html?utm_source=tuicool&utm_medium=referral

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末祟身,一起剝皮案震驚了整個濱河市答恶,隨后出現(xiàn)的幾起案子吃粒,更是在濱河造成了極大的恐慌震肮,老刑警劉巖头朱,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異垢粮,居然都是意外死亡蘸炸,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門聊疲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茬底,“玉大人,你說我怎么就攤上這事获洲≮灞恚” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵贡珊,是天一觀的道長最爬。 經(jīng)常有香客問我,道長门岔,這世上最難降的妖魔是什么爱致? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮寒随,結(jié)果婚禮上糠悯,老公的妹妹穿的比我還像新娘帮坚。我一直安慰自己,他們只是感情好互艾,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布试和。 她就那樣靜靜地躺著,像睡著了一般忘朝。 火紅的嫁衣襯著肌膚如雪灰署。 梳的紋絲不亂的頭發(fā)上判帮,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天局嘁,我揣著相機(jī)與錄音,去河邊找鬼晦墙。 笑死悦昵,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的晌畅。 我是一名探鬼主播但指,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼抗楔!你這毒婦竟也來了棋凳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤连躏,失蹤者是張志新(化名)和其女友劉穎剩岳,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體入热,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡拍棕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了勺良。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绰播。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖尚困,靈堂內(nèi)的尸體忽然破棺而出蠢箩,到底是詐尸還是另有隱情,我是刑警寧澤事甜,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布忙芒,位于F島的核電站,受9級特大地震影響讳侨,放射性物質(zhì)發(fā)生泄漏呵萨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一跨跨、第九天 我趴在偏房一處隱蔽的房頂上張望潮峦。 院中可真熱鬧囱皿,春花似錦、人聲如沸忱嘹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拘悦。三九已至齿兔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間础米,已是汗流浹背分苇。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留屁桑,地道東北人医寿。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像蘑斧,于是被迫代替她去往敵國和親靖秩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

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