RecyclerView --onItemClick設(shè)置匯總

眾所周知,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)暂论。
  1. 首先在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

Android RecyclerView 使用完全解析 體驗(yàn)藝術(shù)般的控件

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末输虱,一起剝皮案震驚了整個(gè)濱河市些楣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宪睹,老刑警劉巖愁茁,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異亭病,居然都是意外死亡鹅很,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)罪帖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)促煮,“玉大人,你說(shuō)我怎么就攤上這事整袁〔こ荩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵坐昙,是天一觀的道長(zhǎng)绳匀。 經(jīng)常有香客問(wèn)我,道長(zhǎng)炸客,這世上最難降的妖魔是什么疾棵? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮痹仙,結(jié)果婚禮上是尔,老公的妹妹穿的比我還像新娘。我一直安慰自己开仰,他們只是感情好拟枚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布薪铜。 她就那樣靜靜地躺著,像睡著了一般梨州。 火紅的嫁衣襯著肌膚如雪痕囱。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天暴匠,我揣著相機(jī)與錄音,去河邊找鬼傻粘。 笑死每窖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的弦悉。 我是一名探鬼主播窒典,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼稽莉!你這毒婦竟也來(lái)了瀑志?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤污秆,失蹤者是張志新(化名)和其女友劉穎劈猪,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體良拼,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡战得,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了庸推。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片常侦。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖贬媒,靈堂內(nèi)的尸體忽然破棺而出聋亡,到底是詐尸還是另有隱情,我是刑警寧澤际乘,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布坡倔,位于F島的核電站,受9級(jí)特大地震影響蚓庭,放射性物質(zhì)發(fā)生泄漏致讥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一器赞、第九天 我趴在偏房一處隱蔽的房頂上張望垢袱。 院中可真熱鬧,春花似錦港柜、人聲如沸请契。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)爽锥。三九已至涌韩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間氯夷,已是汗流浹背臣樱。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腮考,地道東北人雇毫。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像踩蔚,于是被迫代替她去往敵國(guó)和親棚放。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,180評(píng)論 25 707
  • 特別聲明: 本文轉(zhuǎn)發(fā)自:【江清清的博客】http://blog.csdn.net/developer_jiangq...
    _猜火車(chē)_閱讀 37,443評(píng)論 11 70
  • 這篇文章分三個(gè)部分馅闽,簡(jiǎn)單跟大家講一下 RecyclerView 的常用方法與奇葩用法飘蚯;工作原理與ListView比...
    LucasAdam閱讀 4,391評(píng)論 0 27
  • 人找人難,話找話更難福也。 人這一生局骤,都在尋找那個(gè)能過(guò)心的人。 人群中拟杉,我們滔滔不絕庄涡,我們歡聲笑語(yǔ),我們有很多朋友搬设,看...
    花雅雅閱讀 551評(píng)論 1 5
  • 文/阿墨姑娘 天氣有點(diǎn)寒冷穴店,夜色不太溫柔。 僅有幾顆寂寥的星星百無(wú)聊賴地掛在天邊拿穴。 “真不幸泣洞。”穿梭在寒風(fēng)與星辰里...
    阿墨姑娘閱讀 414評(píng)論 19 8