欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

00-Unit_Common综述-RecyclerView封装

程序员文章站 2022-04-24 16:51:46
自学安卓也有一年的时间了,与代码相伴的日子里,苦乐共存。能坚持到现在确实已见到了“往日所未曾见证的风采”。今2018年4月2日,决定用一个案例:Unit_Common,把安卓基础的知识进行串联,形成模块化的总结性测试案例,一方面是对自己的总结,在总结中温故知新。另一方面通过博客,和众多开发爱好者进行... ......
自学安卓也有一年的时间了,与代码相伴的日子里,苦乐共存。能坚持到现在确实已见到了“往日所未曾见证的风采”。今2018年4月24日,决定用一个案例:Unit_Common,把安卓基础的知识进行串联,形成模块化的总结性测试案例,一方面是对自己的总结,在总结中温故知新。另一方面通过博客,和众多开发爱好者进行知识分享,希望可以帮助到一些新入安卓的人。
本项目通过一个RecyclerView进行模块的划分,点击item进入相应模块。所以本篇先用RecyclerView将整个案例的框架搭建出来,这篇对新手可能比较难以理解,但并不影响学习后面的简单知识。
 
为了方便查看将每个模块的名称和图片展示在RecyclerView的item上:
 1 package top.toly.www.unit_common.bean;
 2 
 3 /**
 4  * 作者:张风捷特烈
 5  * 时间:2018/4/10:14:55
 6  * 邮箱:1981462002@qq.com
 7  * 说明:每个界面的bean对象 图片+名称
 8  */
 9 public class ItemBean {
10 
11     private String name;
12     private int ResId;
13 
14     public ItemBean(String name, int resId) {
15         this.name = name;
16         ResId = resId;
17     }
18 
19     public String getName() {
20         return name;
21     }
22 
23     public void setName(String name) {
24         this.name = name;
25     }
26 
27     public int getResId() {
28         return ResId;
29     }
30 
31     public void setResId(int resId) {
32         ResId = resId;
33     }
34 }
Activity_Home_RV 我们需要关注的是OnRvItemClick方法:通过位置打开模块。 setItemsData方法设置数据
 1 注:笔者为避免寻找id的麻烦,使用了ButterKnife
 2    依赖:implementation 'com.jakewharton:butterknife:7.0.1'
 3    混淆:#butterknife
 4   -keep class butterknife.** { *; }
 5   -dontwarn butterknife.internal.**
 6   -keep class **$$ViewBinder { *; }
 7   -keepclasseswithmembernames class * {
 8       @butterknife.* <fields>;
 9   }
10   -keepclasseswithmembernames class * {
11       @butterknife.* <methods>;
12   }
 1 package top.toly.www.unit_common.home;
 2 
 3 import android.content.Intent;
 4 import android.os.Bundle;
 5 import android.support.v7.app.AppCompatActivity;
 6 import android.support.v7.widget.RecyclerView;
 7 import android.view.View;
 8 
 9 import java.util.ArrayList;
10 import java.util.List;
11 
12 import butterknife.Bind;
13 import butterknife.ButterKnife;
14 import top.toly.www.unit_common.R;
15 import top.toly.www.unit_common.activity.MainActivity;
16 import top.toly.www.unit_common.bean.ItemBean;
17 import utils.ev.recyclerview.MyRVAdapter;
18 import utils.ev.recyclerview.MyRVHolder;
19 import utils.ui.UIUtils;
20 
21 public class Activity_Home_RV extends AppCompatActivity {
22 
23     @Bind(R.id.recyclerview)
24     RecyclerView mRecyclerview;
25     private List<ItemBean> mItems;//itemBean的集合
26 
27     @Override
28     protected void onCreate(Bundle savedInstanceState) {
29         super.onCreate(savedInstanceState);
30         setContentView(R.layout.activity_home_rv);
31         ButterKnife.bind(this);
32 
33         setItemsData();//为item设置数据
34     initRV();//初始化RecyclerView
35 
36     }
37 
38     private void initRV() {
39         // 注:笔者已对RecyclerView进行封装,以下几行就搞定RecyclerView的简单使用,封装代码见下
40     UIUtils.setStyle4RV(mRecyclerview, 3, UIUtils.GRIDVIEW, this);//设置类型 
41         MyRVAdapter<ItemBean> rvAdapter = new MyRVAdapter<ItemBean>(mItems, R.layout.rv_item_home) {
42             @Override
43             public void setDatas(MyRVHolder holder, ItemBean data, int position) {
44                 holder.setText(R.id.tv_title, data.getName())
45                         .setImageViewRes(R.id.iv_icon, data.getResId());
46             }
47         };
48 
49         mRecyclerview.setAdapter(rvAdapter);
50         rvAdapter.setOnRvItemClickListener(new MyRVAdapter.OnRvItemClickListener() {
51             @Override
52             public void OnRvItemClick(View v, int pos) {
53                 switch (pos) {
54                     case 0:
55                         startActivity(new Intent(Activity_Home_RV.this, MainActivity.class));
56                         break;
57                 }
58             }
59         });
60     }
61 
62     /**
63      * 为item设置数据
64      *
65      * @return
66      */
67 
68     public List<ItemBean> setItemsData() {
69         mItems = new ArrayList<>();
70         mItems.add(new ItemBean("test", R.drawable.ic_launcher_background));
71 mItems.add(new ItemBean("test1", R.drawable.ic_launcher_background));
72         return mItems;
73     }
74 
75 }
布局:

activity_home_rv.xml:
 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout
 3     xmlns:android="http://schemas.android.com/apk/res/android"
 4     xmlns:tools="http://schemas.android.com/tools"
 5     android:layout_width="match_parent"
 6     android:layout_height="match_parent">
 7 
 8 <android.support.v7.widget.RecyclerView
 9     android:id="@+id/recyclerview"
10     android:layout_width="match_parent"
11     android:layout_height="wrap_content"/>
12 
13 </RelativeLayout>
rv_item_home.xml:
 1 <?xml version="1.0" encoding="utf-8"?>
 2   <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3                 android:layout_width="match_parent"
 4                 android:layout_height="wrap_content"
 5                 android:padding="5dp">
 6 
 7         <ImageView
 8             android:id="@+id/iv_icon"
 9             android:layout_width="50dp"
10             android:layout_height="50dp"
11             android:src="@drawable/ic_launcher_background" />
12 
13         <TextView
14             android:id="@+id/tv_title"
15             android:layout_width="match_parent"
16             android:layout_height="wrap_content"
17             android:layout_marginLeft="3dp"
18             android:layout_toRightOf="@+id/iv_icon"
19             android:layout_centerInParent="true"
20             android:text="Content"
21             android:textAllCaps="false"
22             android:textColor="#000000" />
23 </RelativeLayout>
效果如下:随着mItems数量增加,RecyclerView也会增加
 
 00-Unit_Common综述-RecyclerView封装
下面是笔者封装的代码:虽然比较多,但拷贝进去就能用。好了,综述就到这里。
RecyclerView进行封装:两个类MyRVHolder和MyRVAdapter
  1 package utils.ev.recyclerview;
  2 
  3 import android.graphics.Bitmap;
  4 import android.support.v7.widget.RecyclerView;
  5 import android.util.SparseArray;
  6 import android.view.View;
  7 import android.widget.ImageView;
  8 import android.widget.TextView;
  9 
 10 import com.bumptech.glide.Glide;
 11 import com.bumptech.glide.load.engine.DiskCacheStrategy;
 12 
 13 import utils.ui.UIUtils;
 14 
 15 /**
 16  * 作者:张风捷特烈
 17  * 时间:2018/4/10:14:22
 18  * 邮箱:1981462002@qq.com
 19  * 说明:View的持有人
 20  */
 21 public class MyRVHolder extends RecyclerView.ViewHolder {
 22 
 23     //键值对中键是int类型使用SparseArray比map更好
 24     private SparseArray<View> mViews;//持有的所有View集合
 25     private View mItemView;//Item的
 26 
 27     public MyRVHolder(View itemView) {
 28 
 29         super(itemView);
 30         mItemView = itemView;
 31         mViews = new SparseArray<>();
 32     }
 33 
 34     /**
 35      * 获取pos
 36      *
 37      * @return
 38      */
 39     public int getPos() {
 40         return this.getLayoutPosition();
 41     }
 42 
 43     /**
 44      * 通过viewId获取控件
 45      *
 46      * @param viewId
 47      * @param <T>
 48      * @return
 49      */
 50     public <T extends View> T getView(int viewId) {
 51         View view = mViews.get(viewId);
 52         if (view == null) {
 53             view = mItemView.findViewById(viewId);
 54             mViews.put(viewId, view);//以id为键,view为值
 55         }
 56         return (T) view;
 57     }
 58 
 59     public View getItemView() {
 60         return mItemView;
 61     }
 62 
 63     /**
 64      * 设置item背景颜色
 65      */
 66     public MyRVHolder setColor(int color) {
 67         mItemView.setBackgroundColor(color);
 68         return this;
 69     }
 70 
 71     /**
 72      * 设置TextView文本方法
 73      *
 74      * @param viewId
 75      * @param text
 76      * @return
 77      */
 78     public MyRVHolder setText(int viewId, String text) {
 79         TextView view = getView(viewId);
 80         view.setText(text);
 81         return this;
 82     }
 83 
 84     /**
 85      * 通过资源id设置ImageView图片
 86      * @param viewId
 87      * @param resId
 88      * @return
 89      */
 90     public MyRVHolder setImageViewRes(int viewId, int resId) {
 91         ImageView view = getView(viewId);
 92         view.setImageResource(resId);
 93         return this;
 94     }
 95 
 96     /**
 97      * 通过Bitmap设置ImageView图片
 98      * @param viewId
 99      * @param bitmap
100      * @return
101      */
102     public MyRVHolder setImageViewBitmap(int viewId, Bitmap bitmap) {
103         ImageView view = getView(viewId);
104         view.setImageBitmap(bitmap);
105         return this;
106     }
107 
108     /**
109      * 通过url设置图片
110      * @param viewId
111      * @param url
112      * @return
113      */
114     public MyRVHolder setImageViewUrl(int viewId, String url) {
115         ImageView view = getView(viewId);
116         //此处使用Glide进行Url类型图片的加载,如果未添加Glide依赖会报错
117         //依赖: implementation 'com.github.bumptech.glide:glide:3.7.0'
118         Glide.with(UIUtils.getContext())
119                 .load(url)
120                 .skipMemoryCache(false)
121                 .diskCacheStrategy(DiskCacheStrategy.SOURCE)
122                 .into(view);
123 
124         return this;
125     }
126 
127     /////////////////////可继续拓展完善,添加更多方法//////////////////////
128 }
  1 package utils.ev.recyclerview;
  2 
  3 import android.support.v7.widget.RecyclerView;
  4 import android.view.View;
  5 import android.view.ViewGroup;
  6 
  7 import java.util.List;
  8 
  9 import utils.ui.UIUtils;
 10 
 11 /**
 12  * 作者:张风捷特烈
 13  * 时间:2018/4/10:14:28
 14  * 邮箱:1981462002@qq.com
 15  * 说明:RecyclerView的Adapter封装类
 16  */
 17 public abstract class MyRVAdapter<T> extends RecyclerView.Adapter<MyRVHolder> {
 18     protected List<T> mDatas;
 19     protected int mItemId;
 20     private View mItemView;
 21 
 22     public MyRVAdapter(List<T> datas, int itemId) {
 23         mDatas = datas;
 24         mItemId = itemId;
 25     }
 26 
 27     @Override
 28     public MyRVHolder onCreateViewHolder(ViewGroup parent, int viewType) {
 29         mItemView = UIUtils.inflate(mItemId);
 30         final MyRVHolder myRVHolder = new MyRVHolder(mItemView);
 31         //点击事件方式
 32         mItemView.setOnClickListener(new View.OnClickListener() {
 33             @Override
 34             public void onClick(View v) {//使用回调实现点击监听
 35                 if (mOnRvItemClickListener != null) {
 36                     mOnRvItemClickListener.OnRvItemClick(v, myRVHolder.getPos());
 37 
 38                 }
 39             }
 40         });
 41 
 42         return myRVHolder;
 43     }
 44 
 45     @Override
 46     public void onBindViewHolder(MyRVHolder holder, int position) {
 47 
 48         setDatas(holder, mDatas.get(position), position);
 49 
 50     }
 51 
 52     @Override
 53     public int getItemCount() {
 54         return mDatas.size();
 55     }
 56 
 57     /**
 58      * 抽象方法,通过holder可对各控件进行操作
 59      *
 60      * @param holder   View的持有人
 61      * @param data     数据
 62      * @param position 点击位置
 63      */
 64     public abstract void setDatas(MyRVHolder holder, T data, int position);
 65 
 66 ///////////////////////////////////////////////////////////
 67 
 68     /**
 69      * 添加item
 70      *
 71      * @param i
 72      * @param aNew
 73      */
 74     public void addData(int i, T aNew) {
 75         mDatas.add(i, aNew);
 76         notifyItemInserted(i);//刷新数据
 77     }
 78 
 79     /**
 80      * 删除item
 81      *
 82      * @param i
 83      */
 84     public void deleteData(int i) {
 85         mDatas.remove(i);
 86         notifyItemRemoved(i);//刷新数据
 87     }
 88 
 89     public View getItemView() {
 90         return mItemView;
 91     }
 92 
 93     /////////////////为RecyclerView设置点击监听接口/////////////////////////
 94     /**
 95      * 为RecyclerView设置点击监听接口
 96      */
 97     public interface OnRvItemClickListener {
 98         void OnRvItemClick(View v, int pos);//item被点击的时候回调方法
 99     }
100 
101     /**
102      * 声明监听器接口对象
103      */
104     private OnRvItemClickListener mOnRvItemClickListener;
105 
106     /**
107      * 设置RecyclerView某个的监听方法
108      *
109      * @param onRvItemClickListener
110      */
111     public void setOnRvItemClickListener(OnRvItemClickListener onRvItemClickListener) {
112         mOnRvItemClickListener = onRvItemClickListener;
113     }
114 }

这样可以使用了,不过为了添加分割线,还有免去写一些初始化的设置方法,把其封装在我的UiUtils中,静态方法如下:
 
 
 1 ////////////////////////设置RecyclerView/////////////////////////////////////////
 2     public static final int GRIDVIEW = 0;
 3     public static final int LISTVIEW = 1;
 4     public static final int PULL = 2;
 5 
 6     /**
 7      *
 8      * @param rv  RecyclerView
 9      * @param count count 数量  LISTVIEW可随意
10      * @param style 模式 GRIDVIEW  LISTVIEW  PULL
11      * @param ctx  上下文
12      */
13     public static GridLayoutManager setStyle4RV(RecyclerView rv, int count, int style,Context ctx) {
14         switch (style) {
15             case GRIDVIEW://GridView类型
16                 rv.addItemDecoration(new MyDividerItemDecoration(ctx));//设置分割线
17                 GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), count, GridLayoutManager.VERTICAL, false);
18                 rv.setLayoutManager(gridLayoutManager);
19                 return gridLayoutManager;
20             case LISTVIEW://ListView类型
21                 rv.addItemDecoration(new SampleDivider(ctx));
22                 rv.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
23                 return null;
24             case PULL://瀑布流类型
25                 rv.addItemDecoration(new MyDividerItemDecoration(ctx));
26                 rv.setLayoutManager(new StaggeredGridLayoutManager(count, StaggeredGridLayoutManager.VERTICAL));
27                 return null;
28         }
29         return null;
30     }
MyDividerItemDecoration 分割线
  1 package utils.ev.recyclerview;
  2 
  3 import android.content.Context;
  4 import android.content.res.TypedArray;
  5 import android.graphics.Canvas;
  6 import android.graphics.Rect;
  7 import android.graphics.drawable.Drawable;
  8 import android.support.v7.widget.GridLayoutManager;
  9 import android.support.v7.widget.OrientationHelper;
 10 import android.support.v7.widget.RecyclerView;
 11 import android.view.View;
 12 
 13 
 14 public class MyDividerItemDecoration extends RecyclerView.ItemDecoration {
 15 
 16     private static final int[] ATTRS = new int[]{
 17             android.R.attr.listDivider
 18     };
 19 
 20     /**
 21      * 用于绘制间隔样式
 22      */
 23     private Drawable mDivider;
 24 
 25     public MyDividerItemDecoration(Context context) {
 26         // 获取默认主题的属性
 27         final TypedArray a = context.obtainStyledAttributes(ATTRS);
 28         mDivider = a.getDrawable(0);
 29         a.recycle();
 30     }
 31 
 32 
 33     @Override
 34     public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
 35         // 绘制间隔,每一个item,绘制右边和下方间隔样式
 36         int childCount = parent.getChildCount();
 37         int spanCount = ((GridLayoutManager)parent.getLayoutManager()).getSpanCount();
 38         int orientation = ((GridLayoutManager)parent.getLayoutManager()).getOrientation();
 39         boolean isDrawHorizontalDivider = true;
 40         boolean isDrawVerticalDivider = true;
 41         int extra = childCount % spanCount;
 42         extra = extra == 0 ? spanCount : extra;
 43         for(int i = 0; i < childCount; i++) {
 44             isDrawVerticalDivider = true;
 45             isDrawHorizontalDivider = true;
 46             // 如果是竖直方向,最右边一列不绘制竖直方向的间隔
 47             if(orientation == OrientationHelper.VERTICAL && (i + 1) % spanCount == 0) {
 48                 isDrawVerticalDivider = false;
 49             }
 50 
 51             // 如果是竖直方向,最后一行不绘制水平方向间隔
 52             if(orientation == OrientationHelper.VERTICAL && i >= childCount - extra) {
 53                 isDrawHorizontalDivider = false;
 54             }
 55 
 56             // 如果是水平方向,最下面一行不绘制水平方向的间隔
 57             if(orientation == OrientationHelper.HORIZONTAL && (i + 1) % spanCount == 0) {
 58                 isDrawHorizontalDivider = false;
 59             }
 60 
 61             // 如果是水平方向,最后一列不绘制竖直方向间隔
 62             if(orientation == OrientationHelper.HORIZONTAL && i >= childCount - extra) {
 63                 isDrawVerticalDivider = false;
 64             }
 65 
 66             if(isDrawHorizontalDivider) {
 67                 drawHorizontalDivider(c, parent, i);
 68             }
 69 
 70             if(isDrawVerticalDivider) {
 71                 drawVerticalDivider(c, parent, i);
 72             }
 73         }
 74     }
 75 
 76     @Override
 77     public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
 78         int spanCount = ((GridLayoutManager) parent.getLayoutManager()).getSpanCount();
 79         int orientation = ((GridLayoutManager)parent.getLayoutManager()).getOrientation();
 80         int position = parent.getChildLayoutPosition(view);
 81         if(orientation == OrientationHelper.VERTICAL && (position + 1) % spanCount == 0) {
 82             outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
 83             return;
 84         }
 85 
 86         if(orientation == OrientationHelper.HORIZONTAL && (position + 1) % spanCount == 0) {
 87             outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
 88             return;
 89         }
 90 
 91         outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight());
 92     }
 93 
 94     /**
 95      * 绘制竖直间隔线
 96      *
 97      * @param canvas
 98      * @param parent
 99      *              父布局,RecyclerView
100      * @param position
101      *              irem在父布局中所在的位置
102      */
103     private void drawVerticalDivider(Canvas canvas, RecyclerView parent, int position) {
104         final View child = parent.getChildAt(position);
105         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
106                 .getLayoutParams();
107         final int top = child.getTop() - params.topMargin;
108         final int bottom = child.getBottom() + params.bottomMargin + mDivider.getIntrinsicHeight();
109         final int left = child.getRight() + params.rightMargin;
110         final int right = left + mDivider.getIntrinsicWidth();
111         mDivider.setBounds(left, top, right, bottom);
112         mDivider.draw(canvas);
113     }
114 
115     /**
116      * 绘制水平间隔线
117      *
118      * @param canvas
119      * @param parent
120      *              父布局,RecyclerView
121      * @param position
122      *              item在父布局中所在的位置
123      */
124     private void drawHorizontalDivider(Canvas canvas, RecyclerView parent, int position) {
125         final View child = parent.getChildAt(position);
126         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
127                 .getLayoutParams();
128         final int top = child.getBottom() + params.bottomMargin;
129         final int bottom = top + mDivider.getIntrinsicHeight();
130         final int left = child.getLeft() - params.leftMargin;
131         final int right = child.getRight() + params.rightMargin + mDivider.getIntrinsicWidth();
132         mDivider.setBounds(left, top, right, bottom);
133         mDivider.draw(canvas);
134     }
135 }