实现每日优鲜中RecyclerView多条目背景效果
Posted 周文凯
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现每日优鲜中RecyclerView多条目背景效果相关的知识,希望对你有一定的参考价值。
前段时间有朋友问我怎么使用DelegationAdapter实现每日优鲜的一种效果,安装每日优鲜之后发现一个比较炫酷的效果,如下图所示。
通过查看它的布局发现,整体是使用RecyclerView
来实现的,头部的“平价好菜”是一个条目,下面的每种菜也是一个条目。那么“洋葱”和“油菜”后面的背景是怎么设置上去的呢?
思路
通过以上可以看到,背景不在任意一个条目上,是画在RecyclerView
上面的。画在RecyclerView上
的,大家想到了什么?没错就是ItemDecoration
,可能有的朋友不太了解ItemDecoration
,这里我简单的说下。
ItemDecoration
ItemDecoration
通过字面意思就可以理解到,是条目装饰,即可以在条目底层、上层绘制一些装饰物,来看一下都提供了哪些方法。
public abstract static class ItemDecoration
// 在条目绘制之前绘制装饰,即在RecyclerView条目的底层展示。
public void onDraw(Canvas c, RecyclerView parent, State state)
// 在条目绘制之后绘制装饰,即在RecyclerView条目的上层展示。
public void onDrawOver(Canvas c, RecyclerView parent, State state)
// 重新设置条目的边距
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)
是不是非常简单,仅仅提供了三个方法,重新设置条目边距,在条目底层绘制,在条目上层绘制。比如这里的,我们要重新设置蔬菜条目的边距,这样“洋葱”条目下面的内容才会被看到,否则我们在“洋葱”条目底层绘制了装饰也无法看到。
关于ItemDecoration
的具体讲解,网上非常多,这里不展开来讲,不太了解的朋友可以先去查下资料。
实现
既然我们有了一种实现方案,那么就开始搞吧。俗话说,兵马未动,粮草先行。这里我们要现有接口数据以及实体Bean对象,这是根基,否则后面会寸步难行。
接口数据格式
定义接口数据,最简单的方式是什么呢?向每日优鲜致敬,我管抓别人接口、反编译别人的APP叫致敬,为什么是致敬呢?我理解是从别人那获取方案,是向别人学习,要怀有敬畏和感恩。那么,我们就抓它的接口吧。
去除标识响应状态,额外的条目数据之后,精简下接口大致这样:
"cellList": [
"bgImage": "https://j-image.missfresh.cn/mis_img_20190313192600772.png?mryxw=1125&mryxh=852",
"cellType": 9,
"secondBanner":
"path": "https://j-image.missfresh.cn/mis_img_20190313192555728.png?mryxw=1125&mryxh=270"
,
"cellType": 7,
"normalProduct":
"cartImage": "https://j-image.missfresh.cn/img_20170425134548759.png",
"image": "https://fms-image.missfresh.cn/3bfd39e84df242d8a24e405be3f16705.jpg",
"name": "樱桃小番茄500g*1盒",
"subtitle": "点亮沙拉的红色甜心",
"price": 490
,
"cellType": 7,
"normalProduct":
"cartImage": "https://j-image.missfresh.cn/img_20170425134548759.png",
"image": "https://image.missfresh.cn/6ca7c143db584168860cf7bd0a758fed.jpg",
"name": "平价胡萝卜500g",
"subtitle": "兔仙女都爱胡萝北",
"price": 250
]
通过观察接口我们发现,有个cellType
字段用于标识条目类型,如果cellType=9
就是上面的整条样式,如果cellType=7
就是下面的蔬菜条目样式。既然有了数据,那么实体Bean就很简单了。
public class Products
public List<CellItem> cellList;
public static class CellItem
public int cellType;
public SecondBanner secondBanner;
public String bgImage;
public NormalProduct normalProduct;
public static class SecondBanner
public String path;
public static class NormalProduct
public String image;
public String name;
public String subtitle;
public int price;
public String cartImage;
RecyclerView架子
activity_main
布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#f8f8f8"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
Adapter编写
通过上面的实体Bean定义,我们知道这里有两种类型的条目,并且是通过cellType
进行区分的,我们可以通过getItemViewType
返回不同的类型进行区分。
public class ProductsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
private List<Products.CellItem> dataItems = new ArrayList<>();
public void setProducts(List<Products.CellItem> dataItems)
this.dataItems = dataItems;
notifyDataSetChanged();
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
return null;
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
@Override
public int getItemViewType(int position)
return dataItems.get(position).cellType;
@Override
public int getItemCount()
return dataItems.size();
接下来就是onCreateViewHolder
,在不同的ViewType
的时候需要有不同的ViewHolder
。
再把这个图拿过来,顶部的“评价好菜”比较简单,就一张图片。
layout_second_banner_item
布局
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/iv_image"
android:layout_width="match_parent"
android:layout_height="95dp"
android:scaleType="fitXY" />
SecondBannerViewHolder
代码
public class SecondBannerViewHolder extends RecyclerView.ViewHolder
ImageView image;
public SecondBannerViewHolder(View itemView)
super(itemView);
image = itemView.findViewById(R.id.iv_image);
layout_normal_product_item
布局,也不是特别复杂。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="257dp"
android:background="@drawable/shape_normal_product_bg">
<ImageView
android:id="@+id/iv_image"
android:layout_width="138dp"
android:layout_height="138dp"
android:layout_marginTop="18dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="9dp"
android:layout_marginRight="10dp"
android:ellipsize="end"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="#ff474245"
android:textSize="14sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iv_image" />
<TextView
android:id="@+id/tv_sub_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="3dp"
android:layout_marginRight="10dp"
android:ellipsize="end"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="#ff969696"
android:textSize="12.0sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_name" />
<TextView
android:id="@+id/tv_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="8dp"
android:ellipsize="start"
android:includeFontPadding="false"
android:singleLine="true"
android:textColor="#f44089"
android:textSize="16sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_sub_title" />
<ImageView
android:id="@+id/iv_cart"
android:layout_width="49dp"
android:layout_height="49dp"
android:layout_marginTop="6dp"
android:layout_marginRight="10dp"
android:scaleType="fitXY"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_sub_title" />
</android.support.constraint.ConstraintLayout>
NormalProductViewHolder
代码
public class NormalProductViewHolder extends RecyclerView.ViewHolder
ImageView image;
TextView name;
TextView subTitle;
TextView price;
ImageView cart;
public NormalProductViewHolder(View itemView)
super(itemView);
image = itemView.findViewById(R.id.iv_image);
name = itemView.findViewById(R.id.tv_name);
subTitle = itemView.findViewById(R.id.tv_sub_title);
price = itemView.findViewById(R.id.tv_price);
cart = itemView.findViewById(R.id.iv_cart);
OK,这两种类型的ViewHolder
就算完成了,剩下的只要在onCreateViewHolder
中调用就行了。
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
if (viewType == 9)
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_second_banner_item, parent, false);
return new SecondBannerViewHolder(view);
else if (viewType == 7)
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_normal_product_item, parent, false);
return new NormalProductViewHolder(view);
else
// 不可能到达
return null;
剩下的就是绑定数据onBindViewHolder
,两种对应的类型绑定对应的数据。
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
Context context = holder.itemView.getContext();
Products.CellItem item = dataItems.get(position);
if (holder instanceof SecondBannerViewHolder)
Glide.with(context)
.load(item.secondBanner.path)
.into(((SecondBannerViewHolder) holder).image);
else if (holder instanceof NormalProductViewHolder)
NormalProductViewHolder normalProductHolder = (NormalProductViewHolder) holder;
Glide.with(context)
.load(item.normalProduct.image)
.into(normalProductHolder.image);
normalProductHolder.name.setText(item.normalProduct.name);
normalProductHolder.subTitle.setText(item.normalProduct.subtitle);
normalProductHolder.price.setText("¥" + ((float) item.normalProduct.price / 100));
Glide.with(context)
.load(item.normalProduct.cartImage)
.into(normalProductHolder.cart);
在MainActivity中初始化RecyclerView以及数据
public class MainActivity extends AppCompatActivity
private RecyclerView recyclerView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recycler_view);
GridLayoutManager layoutManager = new GridLayoutManager(this, 2);
recyclerView.setLayoutManager(layoutManager);
ProductsAdapter adapter = new ProductsAdapter();
recyclerView.setAdapter(adapter);
String productListStr = LocalFileUtils.getStringFormAsset(this, "products.json");
Products products = new Gson().fromJson(productListStr, Products.class);
adapter.setProducts(products.cellList);
满怀激动的运行了一把,咦,这是什么玩意儿。是我们没有设置cellType=9
是进行通栏显示。
设置cellType=9
通栏显示
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup()
@Override
public int getSpanSize(int position)
return adapter.getItemViewType(position) == 9 ? 2 : 1;
);
这样是不是有点那么回事了,这样我们的架子算是搭好了。
条目间距设置
通过我们上面搭的架子,发现是没有条目的分割线的,再把我们要实现的效果拿过来,通过肉眼查看,感觉蔬菜条目距离左右边距10dp,条目之间距离6dp。
BackgroundItemDecoration
条目装饰,设置蔬菜条目距离左右边距10dp,条目之间距离6dp,下方6dp。
public class BackgroundItemDecoration extends RecyclerView.ItemDecoration
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)
Context context = view.getContext();
if (!isTargetItem(view, parent))
super.getItemOffsets(outRect, view, parent, state);
else if (isLeftItem(view, parent))
outRect.set(dp2px(context, 10), 0, dp2px(context, 3), dp2px(context, 6));
else
outRect.set(dp2px(context, 3), 以上是关于实现每日优鲜中RecyclerView多条目背景效果的主要内容,如果未能解决你的问题,请参考以下文章
程序员面试每日优鲜被恶心到,网友:和阿里比?小公司戏真够多的