眾所周知,RecyclerView
是繼承自ViewGroup
的快骗,而不是像ListView
一樣繼承自AbsListview
.所以RecyclerView沒(méi)有OnItemClickListener
,沒(méi)有OnItemLongClickListener
,更沒(méi)有OnItemSelectedListener
.所以這都要我們自己實(shí)現(xiàn)统求。What3卮拧F骄健掉丽! Are you kidding me跌榔? 嚴(yán)肅臉,沒(méi)錯(cuò)捶障,就是要我們自己來(lái)實(shí)現(xiàn)僧须。實(shí)現(xiàn)的方法有很多種,我這里做了一下總結(jié)项炼,接下來(lái)我們一一列舉出來(lái)并且對(duì)比一下担平,當(dāng)然最后選哪一個(gè)還是看你自己喜歡咯。
- 修改RecyclerView的源碼锭部,在ViewHolder里面添加監(jiān)聽(tīng)暂论。
- 首先在RecyclerView里面添加
OnItemClickListenr
接口,并且添加OnItemClickListener的成員變量以及set方法如下:
private OnItemClickListener onItemClickListener;
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
public void setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
}
然后再RecyclerView
的抽象類ViewHolder里面的構(gòu)造方法里面添加如下代碼拌禾。
public ViewHolder(View itemView) {
if (itemView == null) {
throw new IllegalArgumentException("itemView may not be null");
}
this.itemView = itemView;
this.itemView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//getChildLayoutPosition(v),根據(jù)v來(lái)獲取v的位置取胎。 mOnItemClickListener.onItemClick(v, getChildLayoutPosition(v))
}
}); //添加這一句就可以添加onclick事件了。
}
這個(gè)方法雖然可行,但是需要修改RecyclerView的源碼闻蛀,在ViewHolder
的構(gòu)造函數(shù)這里直接添加onclicklistener只能對(duì)整個(gè)item設(shè)置click事件匪傍,不能對(duì)item里面的子布局設(shè)置click響應(yīng)事件。我不推薦這種做法觉痛,破壞了RecyclerView的封裝性役衡。只是在這里提一下,多提供一種思路秧饮。
- 不修改源碼映挂,在適配器設(shè)置OnItemClickListener
不多說(shuō),上代碼盗尸。
//ReclcyerView 的適配器
class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyHomeViewHolder> {
private List<String> mData;
public HomeAdapter(List<String> data) {
super();
mData = data;
}
@Override
public MyHomeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//加載布局
MyHomeViewHolder viewHolder = new MyHomeViewHolder(LayoutInflater.from(MainActivity.this).inflate(R.layout.view_item, parent, false));
return viewHolder;
}
@Override
public void onBindViewHolder(final MyHomeViewHolder holder, final int position) {
//onBindViewHolder 初始化布局
holder.mNum.setText(mData.get(position));
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "onclick" + position, Toast.LENGTH_LONG).show();
addData(holder.getLayoutPosition());
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener(){
@Override
public boolean onLongClick(View v) {
Toast.makeText(MainActivity.this, "on long click" + position, Toast.LENGTH_LONG).show();
removeData(holder.getLayoutPosition());
return true;
}
});
}
@Override
public int getItemCount() {
return mData.size();
}
class MyHomeViewHolder extends RecyclerView.ViewHolder {
TextView mNum;
public MyHomeViewHolder(View itemView) {
super(itemView);
//ViewHolder查找布局
mNum = (TextView) itemView.findViewById(R.id.txt_num);
}
}
}
如上代碼柑船,在onBindViewHolder
方法中,我們通過(guò)viewholder
獲取到item中的布局泼各,對(duì)item中的設(shè)置響應(yīng)的點(diǎn)擊事件鞍时。相對(duì)于修改源碼的來(lái)說(shuō),這個(gè)可以對(duì)item中的view的點(diǎn)擊事件進(jìn)行設(shè)置扣蜻。注意父布局搶占子布局焦點(diǎn)的問(wèn)題逆巍。記得設(shè)置mRecyclerView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS)
這樣父布局焦點(diǎn)在子布局獲取焦點(diǎn)之后。
同樣的你也可以給Apdater添加OnItemListener回調(diào)接口以及成員變量莽使,通過(guò)構(gòu)造函數(shù)或者set方法設(shè)置回調(diào)锐极,這樣就可以將onClick
的處理從adapter
里面抽離出去。
- 實(shí)現(xiàn) RecyclerView.OnItemTouchListener
實(shí)現(xiàn)RecyclerView.OnItemTouchListener
監(jiān)聽(tīng)RecyclerView的touch事件芳肌,通過(guò)捕獲touch事件灵再,根據(jù)event的x,y以及RecyclerView的findChildViewUnder(e.getX(),e.getY())
來(lái)獲取到當(dāng)前被觸摸的view。然后利用手勢(shì)來(lái)判斷是長(zhǎng)按還是點(diǎn)擊亿笤,從而回調(diào)相應(yīng)的回調(diào)函數(shù)翎迁。
示例代碼如下:
public class RecyclerViewClickListener implements RecyclerView.OnItemTouchListener {
private GestureDetector mGestureDetector;
private OnItemClickListener mListener;
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
public RecyclerViewClickListener(Context context, final RecyclerView recyclerView,OnItemClickListener listener){
mListener = listener;
mGestureDetector = new GestureDetector(context,
new GestureDetector.SimpleOnGestureListener(){
//click
@Override
public boolean onSingleTapUp(MotionEvent e) {
View childView = recyclerView.findChildViewUnder(e.getX(),e.getY());
if(childView != null && mListener != null){
mListener.onItemClick(childView,recyclerView.getChildLayoutPosition(childView));
return true;
}
return false;
}
//long click
@Override
public void onLongPress(MotionEvent e) {
View childView = recyclerView.findChildViewUnder(e.getX(),e.getY());
if(childView != null && mListener != null){
mListener.onItemLongClick(childView,recyclerView.getChildLayoutPosition(childView));
}
}
});
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
if(mGestureDetector.onTouchEvent(e)){
return true;
}else
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
Log.i("doris", "onTouchEvent");
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
Log.i("doris", "onRequestDisallowInterceptTouchEvent");
}
}
其實(shí)不通過(guò)手勢(shì)來(lái)判斷也是可以的,那就是監(jiān)聽(tīng)KEY_DOWN
以及KEY_MOVE
,KEY_UP
,根據(jù)動(dòng)作間隔以及移動(dòng)的具體來(lái)判斷是點(diǎn)擊還是滑動(dòng)還是長(zhǎng)按净薛。實(shí)現(xiàn)方法可參考揭開(kāi)RecyclerView的神秘面紗(二):處理RecyclerView的點(diǎn)擊事件.不過(guò)與其自己計(jì)算不如用google已經(jīng)封裝好的汪榔,這樣可以更精確的判斷點(diǎn)擊以及長(zhǎng)按事件,避免了由于頻繁快速的操作導(dǎo)致計(jì)算錯(cuò)誤的情況肃拜。
以為這就完了痴腌? 不不不,還有辦法呢燃领,而且我個(gè)人比較喜歡這種方法衷掷。因?yàn)楹?jiǎn)單,入侵性不大柿菩,不用修改源碼,只需設(shè)置回調(diào)接口雨涛,就可以方便的使用了枢舶。準(zhǔn)備好了嗎懦胞? 我要放代碼啦!A剐埂u镂尽!
- 重寫(xiě)RecyclerView的onChildAttachedToWindow方法
首先讓我們一起來(lái)看看onChildAttachedToWindow
方法:
/**
* Called when an item view is attached to this RecyclerView.
*
* <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
* of child views as they become attached. This will be called before a
* {@link LayoutManager} measures or lays out the view and is a good time to perform these
* changes.</p>
*
* @param child Child view that is now attached to this RecyclerView and its associated window
*/
public void onChildAttachedToWindow(View child) {
}
以我蹩腳的英語(yǔ)水平看了一下英文注釋后众,這個(gè)方法就是在itemView
要被attach
(關(guān)聯(lián))到RecylclerView
的時(shí)候調(diào)用胀糜,RecyclerView
的子布局可以重寫(xiě)這個(gè)方法,從而在View
要被``attach的時(shí)候?qū)?/code>itemView
做一些操作蒂誉。這個(gè)方法
LayoutManager`繪制view之前調(diào)用教藻,所以如果你想對(duì)view做特殊的處理,這個(gè)方法是一個(gè)很好的切入口右锨。果然GOOGLE的開(kāi)發(fā)人員還是很有愛(ài)的括堤,一早就給我們預(yù)留了方便我們拓展的方法,機(jī)智如你啦绍移。
好吧悄窃,看完了上面的這個(gè)方法的解說(shuō),你的大腦可以快速運(yùn)轉(zhuǎn)了蹂窖,這說(shuō)明了什么轧抗?我可以利用這個(gè)干什么? 好吧瞬测,謎底揭曉横媚,既然可以在這里操作到每一個(gè)view,那我們就可以在這里對(duì)view進(jìn)行事件監(jiān)聽(tīng)的設(shè)置啦涣楷,不是嗎不是嗎分唾? 對(duì)對(duì)對(duì),沒(méi)錯(cuò)狮斗。不過(guò)呢绽乔,這個(gè)方法也只能對(duì)整個(gè)item的view進(jìn)行設(shè)置,所以如果你的item沒(méi)有多個(gè)button的話碳褒,其實(shí)用這個(gè)方法是很不錯(cuò)的折砸。話不多說(shuō),又到了貼代碼的時(shí)候了沙峻。有沒(méi)有那么一丟丟的小期待睦授,haa!
//增加一個(gè)私有的ItemListener
private interface ItemListener extends OnClickListener, OnFocusChangeListener, OnKeyListener {
}
//在構(gòu)造函數(shù)里創(chuàng)建該對(duì)象摔寨,并重寫(xiě)方法如下
mItemListener = new ItemListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if(null != mOnItemOnKeyListener){
mOnItemOnKeyListener.onKey(v,keyCode,event);
}
return false;
}
/**
* 子控件的點(diǎn)擊事件
* @param itemView
*/
@Override
public void onClick(View itemView) {
if (null != mOnItemClickListener) {
mOnItemClickListener.onItemClick(RecyclerViewTV.this, itemView, getChildLayoutPosition(itemView));
}
}
/**
* 子控件的焦點(diǎn)變動(dòng)事件
* @param itemView
* @param hasFocus
*/
@Override
public void onFocusChange(View itemView, boolean hasFocus) {
if (null != mOnItemListener) {
if (null != itemView) {
mItemView = itemView; // 選中的item.
itemView.setSelected(hasFocus);
if (hasFocus) {
mCurrentSelectView = mItemView;
mOnItemListener.onItemSelected(RecyclerViewTV.this, itemView, getChildLayoutPosition(itemView));
} else {
mOnItemListener.onItemPreSelected(RecyclerViewTV.this, itemView, getChildLayoutPosition(itemView));
}
}
}
}
};
onChildAttachedToWindow
的重寫(xiě)方法如下:
@Override
public void onChildAttachedToWindow(View child) {
if (!child.hasOnClickListeners()) {
child.setOnClickListener(mItemListener);
}
if (child.getOnFocusChangeListener() == null) {
child.setOnFocusChangeListener(mItemListener);
}
child.setOnKeyListener(mItemListener);
}
這個(gè)代碼是引用自androidtvwidget
大家可以在github里面搜索去枷,里面有recyclerView的封裝,用起來(lái)還是不錯(cuò)的,而且是Android TV删顶,當(dāng)然如果你不是Android TV的開(kāi)發(fā)者竖螃,一樣也可以修改一下用于手機(jī)端的。
好了逗余,到這里我們的RecyclerView
如何設(shè)置OnItmeClick
事件的方法匯總就完了特咆。如果還有發(fā)現(xiàn)其他更好的辦法,我會(huì)更新進(jìn)來(lái)录粱。
喜歡我的匯總的可以點(diǎn)個(gè)贊腻格,你們的每一個(gè)點(diǎn)贊都是對(duì)我學(xué)習(xí)路上的莫大的鼓勵(lì),當(dāng)然歡迎大家留言交流啥繁,共同進(jìn)步菜职。
最后貼出參考鏈接,這幾篇都是我覺(jué)得還不錯(cuò)的資源:
揭開(kāi)RecyclerView的神秘面紗(二):處理RecyclerView的點(diǎn)擊事件
https://stackoverflow.com/questions/24885223/why-doesnt-recyclerview-have-onitemclicklistener
https://stackoverflow.com/questions/24471109/recyclerview-onclick/26196831#26196831