前言
安卓5.0可以说是安卓变革的一代,谷歌在这一代开始引入了大量的材质设计(Material Design)使得view更加立体,感官上更让人舒服。同时也引入了很多新的控件,RecyclerView就是其中之一。
1.为啥要使用RecyclerView?
这篇文章详细的说明了RecyclerView适配器中的ViewHolder的复用性,比listview和gridview通过setTag和getTag从内存中获取之前实例化的view不知道高到哪去了。
https://www.2cto.com/kf/201607/522038.html
但是在照着网上的适配器写法写好之后你会发现,这就是个展示的view,每个item并没有对应的点击事件,你需要自己写对应的点击方法。当然这也方便我们自定义点击回调等方法。
2.绘制首页
那么废话不多说,先上图:
如果不用RecyclerView,采取的方式一般是外层一个ScrollView,内部套ListView或者GridView(重写绘制方法)或者通过ListView加头的方式实现。那么RecyclerView该如何实现呢?
通常一个电商类的app拿到UI的设计图,整个app首页基本分为广告位,功能位(各个跳转按钮)和底部内容。
针对这3个部分,写3个对应的xml:
1)广告位
<?xml version="1.0" encoding="utf-8"?><!-- 头部广告 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.yoogonet.mainrecdemo.view.RecyclerViewBanner
android:id="@+id/mainBanner"
android:layout_width="match_parent"
android:layout_height="180dp"
app:pointFocusBg="@color/red2_text"
app:pointUnfocusBg="@color/really_white"
app:point_size="30" />
</LinearLayout>
2)功能位
<?xml version="1.0" encoding="utf-8"?><!-- 中部按钮 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<View style="@style/lineHeight" />
<LinearLayout
android:id="@+id/mainBodyLin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_container_selector"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="10dp"
android:src="@mipmap/ic_launcher_round" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="按钮1"
android:textColor="@color/grey0_text"
android:textSize="14sp" />
</LinearLayout>
<View style="@style/lineHeight" />
<LinearLayout
android:id="@+id/mainBodyLin2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_container_selector"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="10dp"
android:src="@mipmap/ic_launcher_round" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="按钮2"
android:textColor="@color/grey0_text"
android:textSize="14sp" />
</LinearLayout>
<View style="@style/lineHeight" />
<LinearLayout
android:id="@+id/mainBodyLin3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_container_selector"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="10dp"
android:src="@mipmap/ic_launcher_round" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="按钮3"
android:textColor="@color/grey0_text"
android:textSize="14sp" />
</LinearLayout>
<View style="@style/lineHeight" />
<LinearLayout
android:id="@+id/mainBodyLin4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_container_selector"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="10dp"
android:src="@mipmap/ic_launcher_round" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="按钮4"
android:textColor="@color/grey0_text"
android:textSize="14sp" />
</LinearLayout>
<View style="@style/lineHeight" />
</LinearLayout>
<View style="@style/lineWidth" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<View style="@style/lineHeight" />
<LinearLayout
android:id="@+id/mainBodyLin5"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_container_selector"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="10dp"
android:src="@mipmap/ic_launcher_round" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="按钮5"
android:textColor="@color/grey0_text"
android:textSize="14sp" />
</LinearLayout>
<View style="@style/lineHeight" />
<LinearLayout
android:id="@+id/mainBodyLin6"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_container_selector"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="10dp"
android:src="@mipmap/ic_launcher_round" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="按钮6"
android:textColor="@color/grey0_text"
android:textSize="14sp" />
</LinearLayout>
<View style="@style/lineHeight" />
<LinearLayout
android:id="@+id/mainBodyLin7"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_container_selector"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="10dp"
android:src="@mipmap/ic_launcher_round" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="按钮7"
android:textColor="@color/grey0_text"
android:textSize="14sp" />
</LinearLayout>
<View style="@style/lineHeight" />
<LinearLayout
android:id="@+id/mainBodyLin8"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_container_selector"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="10dp"
android:src="@mipmap/ic_launcher_round" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="按钮8"
android:textColor="@color/grey0_text"
android:textSize="14sp" />
</LinearLayout>
<View style="@style/lineHeight" />
</LinearLayout>
</LinearLayout>
3)底部内容
<?xml version="1.0" encoding="utf-8"?><!-- 底部列表 -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/mainBottomImg"
android:layout_width="match_parent"
android:layout_height="180dp"
android:scaleType="centerCrop"
android:src="@color/deep_black" />
</RelativeLayout>
首页适配器:
public class MainAdapter extends RecyclerView.Adapter<MainAdapter.BaseViewHolder> {
private Context context;
private GlideUtil mGlide;
private OnADItemClickListener onADItemClickListener;//广告点击
private OnLinItemClickListener onLinItemClickListener;//按钮点击
private OnItemClickListener onItemClickListener;//底部点击
private MainEntity mainEntity;//整体数据类
private List<LinearLayout> mainLinDataList = new ArrayList<>();
public static final int TYPE_HEADER = 1;//外部判断输出item的大小
public static final int TYPE_BODY = 2;
public static final int TYPE_BOTTOM = 3;
public MainAdapter(MainEntity mainEntity) {
this.mainEntity = mainEntity;
}
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
context = parent.getContext();
if (null == mGlide) {
mGlide = new GlideUtil(context);
}
View view = null;
BaseViewHolder holder = null;
switch (viewType) {
case TYPE_HEADER:
view = LayoutInflater.from(context).inflate(R.layout.item_main_header, null);
holder = new MainHeaderViewHolder(view);
break;
case TYPE_BODY:
view = LayoutInflater.from(context).inflate(R.layout.item_main_body, null);
holder = new MainBodyViewHolder(view);
break;
case TYPE_BOTTOM:
view = LayoutInflater.from(context).inflate(R.layout.item_main_bottom, null);
holder = new MainBottomViewHolder(view);
break;
}
return holder;
}
//返回所有可见的item的数量,如果传入的类包含集合这计算集合的长度让count++
public int getItemCount() {
return mainEntity.getShopDataList().size() + 2;
}
public int getItemViewType(int position) {
if (0 == position) {
return TYPE_HEADER;
} else if (1 == position) {
return TYPE_BODY;
} else {
return TYPE_BOTTOM;
}
}
//给各个布局的赋值,position为item的数量,并不能指定每块内容里的item的下标
public void onBindViewHolder(BaseViewHolder holder, int position) {
if (holder instanceof MainHeaderViewHolder) {
List<String> bannerDataList = mainEntity.getBannerDataList();
if (null != bannerDataList && !bannerDataList.isEmpty()) {
((MainHeaderViewHolder) holder).mainBanner.isShowIndicatorPoint(true);
((MainHeaderViewHolder) holder).mainBanner.setRvBannerDatas(bannerDataList);
((MainHeaderViewHolder) holder).mainBanner.setOnSwitchRvBannerListener((position1, bannerView) -> mGlide.displayNoPlaceholder(bannerView, bannerDataList.get(position1 % bannerDataList.size()), null));
((MainHeaderViewHolder) holder).mainBanner.setOnRvBannerClickListener(position2 -> {
if (null != onADItemClickListener) {
onADItemClickListener.setOnADItemClickListener(position2 % bannerDataList.size());
}
});
}
} else if (holder instanceof MainBodyViewHolder) {
if (null != mainLinDataList && !mainLinDataList.isEmpty()) {
for (int i = 0; i < mainLinDataList.size(); i++) {
int linPosition = i;
mainLinDataList.get(i).setOnClickListener(v -> {
if (null != onLinItemClickListener) {
onLinItemClickListener.setOnLinItemClickListener(linPosition);
}
});
}
}
} else if (holder instanceof MainBottomViewHolder) {
List<ShopEntity> shopDataList = mainEntity.getShopDataList();
if (null != shopDataList && shopDataList.size() > 1) {
ShopEntity shopEntity = shopDataList.get(position - 2);
if (null != shopEntity) {
mGlide.displayNoPlaceholder(((MainBottomViewHolder) holder).mainBottomImg, shopEntity.getUrl(), null);
((MainBottomViewHolder) holder).mainBottomImg.setOnClickListener(v -> {
if (null != onItemClickListener) {
onItemClickListener.setOnItemClickListener(position - 2);
}
});
}
}
}
}
//所有分级组键的基类
class BaseViewHolder extends RecyclerView.ViewHolder {
BaseViewHolder(View itemView) {
super(itemView);
}
}
//头部广告组键
class MainHeaderViewHolder extends BaseViewHolder {
private RecyclerViewBanner mainBanner;
MainHeaderViewHolder(View view) {
super(view);
mainBanner = view.findViewById(R.id.mainBanner);
}
}
//中部按钮组件(只做find布局操作,赋值部分交由onBindViewHolder去完成)
class MainBodyViewHolder extends BaseViewHolder {
private int mLinIds[] = {R.id.mainBodyLin, R.id.mainBodyLin2, R.id.mainBodyLin3, R.id.mainBodyLin4,
R.id.mainBodyLin5, R.id.mainBodyLin6, R.id.mainBodyLin7, R.id.mainBodyLin8};//按钮id数据
MainBodyViewHolder(View view) {
super(view);
for (int mLinId : mLinIds) {
LinearLayout lin = view.findViewById(mLinId);
mainLinDataList.add(lin);
}
}
}
//底部商品组件
class MainBottomViewHolder extends BaseViewHolder {
private ImageView mainBottomImg;
MainBottomViewHolder(View view) {
super(view);
mainBottomImg = view.findViewById(R.id.mainBottomImg);
}
}
//设置广告点击
public void setOnADItemClickListener(OnADItemClickListener onADItemClickListener) {
this.onADItemClickListener = onADItemClickListener;
}
//设置按钮点击
public void setOnLinItemClickListener(OnLinItemClickListener onLinItemClickListener) {
this.onLinItemClickListener = onLinItemClickListener;
}
//设置商品点击
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
public interface OnADItemClickListener {
void setOnADItemClickListener(int position);
}
public interface OnLinItemClickListener {
void setOnLinItemClickListener(int position);
}
public interface OnItemClickListener {
void setOnItemClickListener(int position);
}
}
可以看到3部分的ViewHolder都继承了基础的BaseViewHolder,并且的getItemViewType中做了区分,需要注意的是,这个时候传入的集合要把其余加入的布局的数量加上,就比如我增加了头部的广告和中段的按钮,所以在总长度这里+2,并且在getItemViewType中,0和1返回的是头部和中段按钮。
接着就是写在主页写适配器的时候需要做一定的区分:
设置首页适配器,需要对占位做修改,比如底部数据的一行3个,配置manager的时候写了3,广告位1个就要占3个位置所以返回3,按钮位同理,那么底部按钮其实占的是1。
mainRecyclerView.setHasFixedSize(true);
GridLayoutManager manager = new GridLayoutManager(this, 3);
manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
public int getSpanSize(int position) {
int viewType = mainAdapter.getItemViewType(position);
int count = 1;
switch (viewType) {
case MainAdapter.TYPE_HEADER://首页头由于是一大块整体,占2个位置
count = 3;
break;
case MainAdapter.TYPE_BODY:
count = 3;
break;
case MainAdapter.TYPE_BOTTOM://底部 内容一个数据占一块
count = 1;
break;
}
return count;
}
});
mainRecyclerView.setLayoutManager(manager);
//画间距
SparseArray<SCommonItemDecoration.ItemDecorationProps> propMap = new SparseArray<>();
SCommonItemDecoration.ItemDecorationProps prop1 = new SCommonItemDecoration.ItemDecorationProps(DisplayUtil.dip2px(this, 10), DisplayUtil.dip2px(this, 10), true, true);
propMap.put(MainAdapter.TYPE_BOTTOM, prop1);
mainRecyclerView.addItemDecoration(new SCommonItemDecoration(propMap));
配置好后即可完成绘制。