zoukankan      html  css  js  c++  java
  • Android 高级UI设计笔记20:RecyclerView 的详解之RecyclerView添加Item点击事件

    1. 引言:

    RecyclerView侧重的是布局的灵活性,虽说可以替代ListView但是连基本的点击事件都没有,这篇文章就来详细讲解如何为RecyclerView的item添加点击事件,顺便复习一下观察者模式。

    2. 最终目的

    模拟ListView的setOnItemClickListener()方法,调用者只须调用类似于setOnItemClickListener的东西就能获得被点击item的相关数据。

     

    3. 原理

    为RecyclerView的每个子item设置setOnClickListener,然后在onClick中再调用一次对外封装的接口,将这个事件传递给外面的调用者。而"为RecyclerView的每个子item设置setOnClickListener"在Adapter中设置。其实直接在onClick中也能完全处理item的点击事件,但是这样会破坏代码的逻辑。

    4. 具体步骤如下:

    在自定义MyAdapter之中(继承自RecyclerView.Adapter

    (1)在MyAdapter中定义如下接口,模拟ListView的OnItemClickListener:

     //define interface
        public static interface OnRecyclerViewItemClickListener {
            void onItemClick(View view , String data);
        }

    (2)声明一个这个接口的变量

    private OnRecyclerViewItemClickListener mOnItemClickListener = null;

    (3)在onCreateViewHolder()中为每个item添加点击事件

     @Override
        public ViewHolder onCreateViewHolder(ViewGroup viewGroup,  int viewType) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
            ViewHolder vh = new ViewHolder(view);
            //将创建的View注册点击事件
            view.setOnClickListener(this);
            return vh;
        }

    (4)将点击事件转移给外面的调用者:

     @Override
        public void onClick(View v) {
            if (mOnItemClickListener != null) {
                //注意这里使用getTag方法获取数据
                mOnItemClickListener.onItemClick(v,(String)v.getTag());
            }
        }

    (5)注意上面调用接口的onItemClick()中的v.getTag()方法,这需要在onBindViewHolder()方法中设置和item相关的数据

    @Override
        public void onBindViewHolder(ViewHolder viewHolder,  int position) {
            viewHolder.mTextView.setText(datas[position]);
            //将数据保存在itemView的Tag中,以便点击时进行获取
            viewHolder.itemView.setTag(datas[position]);
        }

    (6)最后暴露给外面的调用者,定义一个设置Listener的方法():

     public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
            this.mOnItemClickListener = listener;
        }

     以上所有步骤都发生在自定义的adapter中,典型的观察者模式,有点绕的地方在于,这里涉及到两个观察者模式的使用,view的setOnClickListener本来就是观察者模式,我们将这个观察者模式的事件监听传递给了我们自己的观察者模式。

    (7)接下来当然是在Activity中使用,如下:

     1 mRecyclerView = (RecyclerView)findViewById(R.id.my_recycler_view);
     2         //创建默认的线性LayoutManager
     3         mLayoutManager = new LinearLayoutManager(this);
     4         mRecyclerView.setLayoutManager(mLayoutManager);
     5         //如果可以确定每个item的高度是固定的,设置这个选项可以提高性能
     6         mRecyclerView.setHasFixedSize(true);
     7         //创建并设置Adapter
     8         mAdapter = new MyAdapter(data);
     9         mRecyclerView.setAdapter(mAdapter);
    10         mAdapter.setOnItemClickListener(new OnRecyclerViewItemClickListener(){
    11             @Override    
    12             public void onItemClick(View view , String data){
    13                 Toast.makeText(MainActivity.this, data, 600).show();
    14             }
    15         });

    5. 案例演示:(结合上面的步骤理解)

    (1)使用Eclipse创建一个工程,如下:

    同时注意API要使用API21

    (2)首先我们来到主布局activity_main.xml,如下:

     1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent" >
     5 
     6     <android.support.v7.widget.RecyclerView
     7         android:id="@+id/recyclerview"
     8         android:layout_width="match_parent"
     9         android:layout_height="match_parent"
    10          />
    11 
    12 </RelativeLayout>

    (3)接下来,我们来到RecyclerView的item布局item.xml,如下:

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     xmlns:tools="http://schemas.android.com/tools"
     4     android:layout_width="match_parent"
     5     android:layout_height="50dip" >
     6 
     7     <TextView
     8         android:id="@+id/text"
     9         android:text="默认"
    10         android:layout_marginTop="5dp"
    11         android:gravity="center_horizontal"
    12         android:layout_width="match_parent"
    13         android:layout_height="wrap_content"
    14         android:textSize="20sp" 
    15         android:textColor="@android:color/holo_red_dark"/>
    16 
    17 </RelativeLayout>

    (4)接下来我们自定义MyAdapter,如下:

     1 package com.himi.recyclerviewdemo;
     2 
     3 import android.support.v7.widget.RecyclerView;
     4 import android.view.LayoutInflater;
     5 import android.view.View;
     6 import android.view.ViewGroup;
     7 import android.widget.TextView;
     8 
     9 public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> 
    10                                 implements View.OnClickListener {
    11 
    12     private String[] datas;
    13 
    14     public MyAdapter(String[] datas) {
    15         this.datas = datas;
    16 
    17     }
    18 
    19 
    20     /**
    21      * 1.定义接口
    22      */
    23     public static interface OnRecyclerViewItemClickListener {
    24         
    25         void onItemClick(View view, String data);
    26         
    27     }
    28 
    29     /**
    30      * 3.在onCreateViewHolder()中为每个item添加点击事件
    31      */
    32     @Override
    33     public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
    34         View view = LayoutInflater.from(viewGroup.getContext())
    35                 .inflate(R.layout.item, viewGroup, false);
    36         
    37         ViewHolder vh = new ViewHolder(view);
    38         // 将创建的View注册点击事件
    39         view.setOnClickListener(this);
    40         
    41         return vh;
    42     }
    43     
    44     /**
    45      * 5.注意上面调用接口的onItemClick()中的v.getTag()方法,
    46      * 这需要在onBindViewHolder()方法中设置和item相关的数据
    47      */
    48     @Override
    49     public void onBindViewHolder(ViewHolder viewHolder, int position) {
    50         
    51         viewHolder.mTextView.setText(datas[position]);
    52         
    53         // 将数据保存在itemView的Tag中,以便点击时进行获取
    54         viewHolder.itemView.setTag(datas[position]);
    55     }
    56 
    57     /**
    58      * 4.将点击事件转移给外面的调用者
    59      */
    60     public void onClick(View v) {
    61         if (mOnItemClickListener != null) {
    62             // 注意这里使用getTag方法获取数据
    63             mOnItemClickListener.onItemClick(v, (String) v.getTag());
    64         }
    65     }
    66 
    67     /**
    68      * 2.声明一个这个接口的变量
    69      */
    70     private OnRecyclerViewItemClickListener mOnItemClickListener = null;
    71     
    72     
    73     /**
    74      *6.最后暴露给外面的调用者,定义一个设置Listener的方法()
    75      */
    76     
    77     public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
    78         this.mOnItemClickListener = listener;
    79     }
    80     
    81     
    82 
    83     // 获取数据的数量
    84     @Override
    85     public int getItemCount() {
    86         return datas.length;
    87     }
    88 
    89     // 自定义的ViewHolder,持有每个Item的的所有界面元素
    90     public static class ViewHolder extends RecyclerView.ViewHolder {
    91         public TextView mTextView;
    92 
    93         public ViewHolder(View view) {
    94             super(view);
    95             mTextView = (TextView) view.findViewById(R.id.text);
    96         }
    97     }
    98 
    99 }

    (5)接下来来到MainActivity,如下:

     1 package com.himi.recyclerviewdemo;
     2 
     3 import com.himi.recyclerviewdemo.MyAdapter.OnRecyclerViewItemClickListener;
     4 
     5 import android.app.Activity;
     6 import android.os.Bundle;
     7 import android.support.v7.widget.LinearLayoutManager;
     8 import android.support.v7.widget.RecyclerView;
     9 import android.view.View;
    10 import android.widget.Toast;
    11 
    12 public class MainActivity extends Activity {
    13 
    14     private RecyclerView mRecyclerView;
    15     private LinearLayoutManager mLayoutManager;
    16     private MyAdapter mAdapter;
    17     private String[] data = new String[] { 
    18                 "刘德华", "周杰伦", "梁朝伟", "郭富城", "黎明", "张学友", 
    19                 "成龙", "午马", "洪金宝", "林正英", "元彪", "林志颖", 
    20                 "吴奇隆", "苏有朋", "赵薇", "陈坤", "周润发", "范冰冰", 
    21                 "贾静雯", "周星驰" 
    22             };
    23 
    24     @Override
    25     protected void onCreate(Bundle savedInstanceState) {
    26         super.onCreate(savedInstanceState);
    27         setContentView(R.layout.activity_main);
    28         mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
    29 
    30         // 创建默认的线性LayoutManager
    31         mLayoutManager = new LinearLayoutManager(this);
    32         mRecyclerView.setLayoutManager(mLayoutManager);
    33 
    34         // 如果可以确定每个item的高度是固定的,设置这个选项可以提高性能
    35         mRecyclerView.setHasFixedSize(true);
    36         
    37         //添加item间的分割线
    38         mRecyclerView.addItemDecoration(new RecycleViewDivider(this, LinearLayoutManager.HORIZONTAL));
    39 
    40         // 创建并设置Adapter
    41         mAdapter = new MyAdapter(data);
    42         mRecyclerView.setAdapter(mAdapter);
    43         mAdapter.setOnItemClickListener(new OnRecyclerViewItemClickListener() {
    44             @Override
    45             public void onItemClick(View view, String data) {
    46                 Toast.makeText(MainActivity.this, data, 1).show();
    47             }
    48         });
    49     }
    50 
    51 }

    这里使用到的自定义分割线类RecycleViewDivider是别人写的,我直接拿来用了,在此我表示感谢,如下:

      1 package com.himi.recyclerviewdemo;
      2 
      3 import android.content.Context;
      4 import android.content.res.TypedArray;
      5 import android.graphics.Canvas;
      6 import android.graphics.Paint;
      7 import android.graphics.Rect;
      8 import android.graphics.drawable.Drawable;
      9 import android.support.v4.content.ContextCompat;
     10 import android.support.v7.widget.LinearLayoutManager;
     11 import android.support.v7.widget.RecyclerView;
     12 import android.support.v7.widget.RecyclerView.ItemDecoration;
     13 import android.view.View;
     14 
     15 public class RecycleViewDivider extends ItemDecoration {
     16       private Paint mPaint;
     17         private Drawable mDivider;
     18         private int mDividerHeight = 2;//分割线高度,默认为1px
     19         private int mOrientation;//列表的方向:LinearLayoutManager.VERTICAL或LinearLayoutManager.HORIZONTAL
     20         private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
     21 
     22         /**
     23          * 默认分割线:高度为2px,颜色为灰色
     24          *
     25          * @param context
     26          * @param orientation 列表方向
     27          */
     28         public RecycleViewDivider(Context context, int orientation) {
     29             if (orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL) {
     30                 throw new IllegalArgumentException("请输入正确的参数!");
     31             }
     32             mOrientation = orientation;
     33 
     34             final TypedArray a = context.obtainStyledAttributes(ATTRS);
     35             mDivider = a.getDrawable(0);
     36             a.recycle();
     37         }
     38 
     39         /**
     40          * 自定义分割线
     41          *
     42          * @param context
     43          * @param orientation 列表方向
     44          * @param drawableId  分割线图片
     45          */
     46         public RecycleViewDivider(Context context, int orientation, int drawableId) {
     47             this(context, orientation);
     48             mDivider = ContextCompat.getDrawable(context, drawableId);
     49             mDividerHeight = mDivider.getIntrinsicHeight();
     50         }
     51 
     52         /**
     53          * 自定义分割线
     54          *
     55          * @param context
     56          * @param orientation   列表方向
     57          * @param dividerHeight 分割线高度
     58          * @param dividerColor  分割线颜色
     59          */
     60         public RecycleViewDivider(Context context, int orientation, int dividerHeight, int dividerColor) {
     61             this(context, orientation);
     62             mDividerHeight = dividerHeight;
     63             mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     64             mPaint.setColor(dividerColor);
     65             mPaint.setStyle(Paint.Style.FILL);
     66         }
     67 
     68 
     69         //获取分割线尺寸
     70         @Override
     71         public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
     72             super.getItemOffsets(outRect, view, parent, state);
     73             outRect.set(0, 0, 0, mDividerHeight);
     74         }
     75 
     76         //绘制分割线
     77         @Override
     78         public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
     79             super.onDraw(c, parent, state);
     80             if (mOrientation == LinearLayoutManager.VERTICAL) {
     81                 drawVertical(c, parent);
     82             } else {
     83                 drawHorizontal(c, parent);
     84             }
     85         }
     86 
     87         //绘制横向 item 分割线
     88         private void drawHorizontal(Canvas canvas, RecyclerView parent) {
     89             final int left = parent.getPaddingLeft();
     90             final int right = parent.getMeasuredWidth() - parent.getPaddingRight();
     91             final int childSize = parent.getChildCount();
     92             for (int i = 0; i < childSize; i++) {
     93                 final View child = parent.getChildAt(i);
     94                 RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
     95                 final int top = child.getBottom() + layoutParams.bottomMargin;
     96                 final int bottom = top + mDividerHeight;
     97                 if (mDivider != null) {
     98                     mDivider.setBounds(left, top, right, bottom);
     99                     mDivider.draw(canvas);
    100                 }
    101                 if (mPaint != null) {
    102                     canvas.drawRect(left, top, right, bottom, mPaint);
    103                 }
    104             }
    105         }
    106 
    107         //绘制纵向 item 分割线
    108         private void drawVertical(Canvas canvas, RecyclerView parent) {
    109             final int top = parent.getPaddingTop();
    110             final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom();
    111             final int childSize = parent.getChildCount();
    112             for (int i = 0; i < childSize; i++) {
    113                 final View child = parent.getChildAt(i);
    114                 RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
    115                 final int left = child.getRight() + layoutParams.rightMargin;
    116                 final int right = left + mDividerHeight;
    117                 if (mDivider != null) {
    118                     mDivider.setBounds(left, top, right, bottom);
    119                     mDivider.draw(canvas);
    120                 }
    121                 if (mPaint != null) {
    122                     canvas.drawRect(left, top, right, bottom, mPaint);
    123                 }
    124             }
    125         }
    126     }

    (6)部署程序到手机上,如下:

    6. 总结:

    在ListView中我们是调用ListView的setOnItemClickListener:

    1 mListView.setOnItemClickListener(new OnItemClickListener() {
    2             public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
    3                  
    4                     ...           
    5                 
    6             }
    7         });

    而在我们这里是调用mAdapter的setOnItemClickListener。且回调方法public void onItemClick()的参数也不一致,ListView中有被点击item的position参数,而我们这里直接是被点击item的相关数据(这里只是一个字符串)。

     

  • 相关阅读:
    netty解决TCP的拆包和粘包的解决办法
    HTML图片热区map area的用法
    JWT应用
    React-router4简约教程
    axios中文文档
    如何区分Babel中的stage-0,stage-1,stage-2以及stage-3(一)
    python datetime offset-aware与offset-navie相互转换
    DJango跨域中间键
    Javacript实现倒计时
    CAN协议,系统结构和帧结构
  • 原文地址:https://www.cnblogs.com/hebao0514/p/5643225.html
Copyright © 2011-2022 走看看