00-Unit_Common综述-RecyclerView封装
程序员文章站
2022-07-26 18:08:27
自学安卓也有一年的时间了,与代码相伴的日子里,苦乐共存。能坚持到现在确实已见到了“往日所未曾见证的风采”。今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也会增加
下面是笔者封装的代码:虽然比较多,但拷贝进去就能用。好了,综述就到这里。
对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 }