第一家公司做外包,做过不少电商项目,首页设计基本都是这个样
从上至下分别一个轮播图框,一个横向滑动的view用来展示一些特殊的列表,几个快捷菜单,和一个显示商品的列表。
实际应用里截图:
这里就用一个基本的例子来实现如何RecyclerView怎么进行多item布局。
先看效果图:
首先,轮播图的bean,横向List的bean,和正常list的bean肯定不同,而adapter只有一个与一个数组绑定,所以三种bean要可以是统一类型的bean,所以我定义了一个三种bean的父类bean就叫它ExampleBaseBean
public class ExampleBaseBean { private int viewType; public int getViewType() { return viewType; } public void setViewType(int viewType) { this.viewType = viewType; } }
如代码所示,这个BaseBean主要作用就是提供一个int类型的vieType的对象用来区分不同类型的bean。
有了BaseBean,接下来就是三种bean了。
public class TitleBean extends ExampleBaseBean { //轮播bean,为了方便叫titleBean private List<String> titles;//轮播的数据源一般都为数组。 public List<String> getTitles() { return titles; } public void setTitles(List<String> titles) { this.titles = titles; } }
public class BodyBean extends ExampleBaseBean { //中间横向滑动的bean ,通常都有图片展示,这里用本地图片展示 private List<Integer> res; public List<Integer> getRes() { return res; } public void setRes(List<Integer> res) { this.res = res; } }
public class FootBean extends ExampleBaseBean { //正常列表的bean private String str; public FootBean(String str) { this.str = str; } public String getStr() { return str; } }
三种bean定义完,该着手adapter了。
public class ExampleAdapter extends RecyclerView.Adapter { public final static int TITLE = 1001;//标题的viewType public final static int BODY = 1002;//横向列表的viewType public final static int FOOT = 1003;//正常列表的viewType private List<ExampleBaseBean> mlist;//adapter的数据源 private Context context; private LayoutInflater inflater; public ExampleAdapter(List<ExampleBaseBean> mlist) { this.mlist = mlist; }
首先在这个adapter里将会用到viewtype写成静态常量,方便外面统一。
先写三种bean对应的ViewHolder:
private class TitleHolder extends RecyclerView.ViewHolder { ViewPager vp; public TitleHolder(View itemView) { super(itemView); vp = itemView.findViewById(R.id.vp); } } private class BodyHolder extends RecyclerView.ViewHolder { RecyclerView rv; public BodyHolder(View itemView) { super(itemView); rv = itemView.findViewById(R.id.rv); } } private class FootHolder extends RecyclerView.ViewHolder { TextView tv_foot; public FootHolder(View itemView) { super(itemView); tv_foot = itemView.findViewById(R.id.tv_foot); } }
三种ViewHolder写完了,然后就是复写RecyclerView.Adapter的getItemViewType方法来控制它返回给onCreateViewHolder的viewType:
@Override public int getItemViewType(int position) { if (mlist.size() > 0) { return mlist.get(position).getViewType(); } return super.getItemViewType(position); }
然后就是adapter的主要方法onCreateViewHolder方法:
@NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { if (context == null) context = parent.getContext(); if (inflater == null) inflater = LayoutInflater.from(context); View view; switch (viewType) { case TITLE: view = inflater.inflate(R.layout.listitem_title, parent, false); return new TitleHolder(view); case BODY: view = inflater.inflate(R.layout.listitem_body, parent, false); return new BodyHolder(view); case FOOT: view = inflater.inflate(R.layout.listitem_foot, parent, false); return new FootHolder(view); } return null; }
根据onCreateViewHolder方法传来的viewType进行判断然后返回对应item布局的ViewHolder。
ViewHolder会被传到onBindViewHolder方法,接下来就只再要实现onBindViewHolder里就OK了。
@Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { if (holder instanceof TitleHolder) { TitleBean titleBean = (TitleBean) mlist.get(position); ((TitleHolder) holder).vp.setAdapter(new PagerAdapter(titleBean.getTitles())); } if (holder instanceof BodyHolder) { BodyBean bodyBean = (BodyBean) mlist.get(position); ((BodyHolder) holder).rv.setLayoutManager(new LinearLayoutManager(context, LinearLayout.HORIZONTAL, false)); ((BodyHolder) holder).rv.setAdapter(new BodyAdapter(bodyBean.getRes())); } if (holder instanceof FootHolder) { FootBean footBean = (FootBean) mlist.get(position); ((FootHolder) holder).tv_foot.setText(footBean.getStr()); } }
这里的viewPager的adapter我图省事直接写成内部类了
private class PagerAdapter extends android.support.v4.view.PagerAdapter { List<String> stringList; public PagerAdapter(List<String> stringList) { this.stringList = stringList; } @NonNull @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { ViewPager.LayoutParams params = new ViewPager.LayoutParams(); params.width = ViewPager.LayoutParams.WRAP_CONTENT; params.height = ViewPager.LayoutParams.WRAP_CONTENT; params.gravity = Gravity.CENTER; TextView textView = new TextView(container.getContext()); textView.setText(stringList.get(position)); textView.setTextSize(30); textView.setTextColor(Color.parseColor("#333333")); textView.setLayoutParams(params); container.addView(textView); return textView; } @Override public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { container.removeView((View) object); } @Override public int getCount() { return stringList.size(); } @Override public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { return view == object; } }
还有一个Body的adapter就是平时展示一种数据的adapter,这里也贴一下。
public class BodyAdapter extends RecyclerView.Adapter { private List<Integer> res; public BodyAdapter(List<Integer> res) { this.res = res; } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.listitem_body_img, parent, false); return new ImgHolder(view); } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { if (holder instanceof ImgHolder) { ((ImgHolder) holder).iv.setImageResource(res.get(position)); } } private class ImgHolder extends RecyclerView.ViewHolder { ImageView iv; public ImgHolder(View itemView) { super(itemView); iv = itemView.findViewById(R.id.iv); } } @Override public int getItemCount() { return res.size(); } }
还有就是Acitivy里模拟数据的添加代码:
public class ExampleActivity extends AppCompatActivity { private ExampleAdapter adapter; private List<ExampleBaseBean> mlist = new ArrayList<>(); private RecyclerView rv; private Context context; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_example); context = this; rv = findViewById(R.id.rv); initTitle(); initBody(); initFoot(); initAdapter(); } private void initFoot() { for (int i = 0; i < 10; i++) { FootBean footBean=new FootBean("foot:"+i); footBean.setViewType(ExampleAdapter.FOOT);//正常列表 mlist.add(footBean); } } private void initBody() { List<Integer> res = new ArrayList<>(6); res.add(R.mipmap.pic1); res.add(R.mipmap.pic2); res.add(R.mipmap.pic3); res.add(R.mipmap.pic4); res.add(R.mipmap.pic5); res.add(R.mipmap.pic6); BodyBean bodyBean = new BodyBean(); bodyBean.setRes(res); bodyBean.setViewType(ExampleAdapter.BODY);//设置横向列表的类型 mlist.add(bodyBean); } private void initTitle() { List<String> titles = new ArrayList<>(5); for (int i = 0; i < 5; i++) { titles.add(new StringBuilder("标题").append(i).toString()); } TitleBean titleBean = new TitleBean(); titleBean.setTitles(titles); titleBean.setViewType(ExampleAdapter.TITLE);//设置为轮播类型 mlist.add(titleBean); } private void initAdapter() { if (adapter == null) { adapter = new ExampleAdapter(mlist); rv.setLayoutManager(new LinearLayoutManager(context)); rv.setAdapter(adapter); } else { adapter.notifyDataSetChanged(); } } }
Item的xml布局也极其简单都是只有一个控件,就不贴了。