关于使用recycleview写多种布局布局遇到的坑
最近项目中遇到多种布局嵌套使用情况,为了不多麻烦去写自定义控件监听事件的分发,便使用了recycleview.对于第一次在项目中使用这个玩意,在看过官方文档后并不是很理解其介绍(主要是全英文),于是乎在优快云怼了个中文注解的示例demo来用,于是乎就有了下面的这个问题.
1. 首先看原型图
2. 服务器返回的数据
3.代码部分.
Activity:
package com.example.administrator.a3dmark.detail_shop;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.example.administrator.a3dmark.Activity.BaseActivity;
import com.example.administrator.a3dmark.R;
import com.example.administrator.a3dmark.adapter.ShopAdapter;
import com.example.administrator.a3dmark.bean.Banners;
import com.example.administrator.a3dmark.bean.ShopGoods;
import com.example.administrator.a3dmark.util.Contants;
import com.example.administrator.a3dmark.util.ItemDivider;
import com.example.administrator.a3dmark.util.SharedUtil;
import com.lzy.okgo.OkGo;
import com.lzy.okgo.callback.StringCallback;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import butterknife.BindView;
import butterknife.ButterKnife;
import okhttp3.Call;
import okhttp3.Response;
/**
* 店铺
* Created by Administrator on 2017/3/8.
*/
public class Shop_Activity extends BaseActivity {
@BindView(R.id.image_title_white_back)
ImageView imageTitleWhiteBack;
@BindView(R.id.tv_title_white)
TextView tvTitleWhite;
@BindView(R.id.tv_title_white_vice)
TextView tvTitleWhiteVice;
Intent intent;
@BindView(R.id.tv_collection_title)
TextView tvCollectionTitle;
@BindView(R.id.tv_title_num)
TextView tvTitleNum;
@BindView(R.id.ll_title)
LinearLayout llTitle;
@BindView(R.id.recyclerView)
RecyclerView recyclerView;
private String bussiness_id;
private String userid;
//轮播图List<bean>对象
private ArrayList<Banners> Flybanners = new ArrayList<>();
//商品List<bean>对象
private ArrayList<ShopGoods> goodses = new ArrayList<ShopGoods>();
private ShopAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.shop_activity);
ButterKnife.bind(this);
intent = getIntent();
bussiness_id = intent.getStringExtra("bussiness_id");
Toast.makeText(Shop_Activity.this, bussiness_id, Toast.LENGTH_SHORT).show();
userid = (String) SharedUtil.getParam(this, "userid", "");
llTitle.setVisibility(View.VISIBLE);
recyclerView.setLayoutManager(new GridLayoutManager(recyclerView.getContext(), 3, GridLayoutManager.VERTICAL, false));
recyclerView.addItemDecoration(new ItemDivider().setDividerWith(this, 20));
adapter = new ShopAdapter(Shop_Activity.this);
initGradData();
recyclerView.setAdapter(adapter);
adapter.setOnItemClickListener(new ShopAdapter.OnRecyclerViewItemClickListener() {
@Override
public void onItemClick(View view) {
int position = recyclerView.getChildAdapterPosition(view);
startActivity(new Intent(Shop_Activity.this, Boutique_Detail.class)
.putExtra("goods_id", goodses.get(position).getId())
.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
);
}
});
imageTitleWhiteBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
}
//商品接口
private void initGradData() {
final ProgressDialog mDialog = ProgressDialog.show(this, "获取数据", "获取数据中");
OkGo.post(Contants.DTORE)
.params("bussinessId", bussiness_id)
.execute(new StringCallback() {
@Override
public void onSuccess(String s, Call call, Response response) {
Log.d("code===initGradData", s);
mDialog.dismiss();
try {
JSONObject json = new JSONObject(s);
if (s.indexOf("error") != -1) {//有错误
Toast.makeText(Shop_Activity.this, json.getString("error"), Toast.LENGTH_SHORT).show();
return;
}
JSONObject success = json.getJSONObject("success");
tvTitleWhite.setText(success.getString("shopName"));
tvTitleNum.setText("店铺号:" + success.getString("introduce"));
JSONArray info = success.getJSONArray("goodsInfoArray");
for (int i = 0; i < info.length(); i++) {
JSONObject obj = info.getJSONObject(i);
ShopGoods shopgoods = new ShopGoods();
shopgoods.setGoodsname(obj.getString("goodsName"));
shopgoods.setId(obj.getString("goodsId"));
shopgoods.setImages(obj.getString("img"));
shopgoods.setPricenow(obj.getString("priceNow"));
goodses.add(shopgoods);
}
adapter.setGoods(goodses);
JSONArray showPage = success.getJSONArray("showPage");
for (int j = 0; j < showPage.length(); j++) {
JSONObject page = info.getJSONObject(j);
Banners banners = new Banners();
//banners.setId(page.getString("id"));
banners.setImg(page.getString("img"));
Flybanners.add(banners);
}
adapter.setFlybanners(Flybanners);
adapter.notifyDataSetChanged();
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void onError(Call call, Response response, Exception e) {
super.onError(call, response, e);
mDialog.dismiss();
Toast.makeText(Shop_Activity.this, e.toString(), Toast.LENGTH_LONG).show();
}
});
}
}
Adapter:
package com.example.administrator.a3dmark.adapter;
import android.content.Context;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.example.administrator.a3dmark.R;
import com.example.administrator.a3dmark.bean.Banners;
import com.example.administrator.a3dmark.bean.ShopGoods;
import com.recker.flybanner.FlyBanner;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Administrator on 2017/3/8.
*/
public class ShopAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
//轮播图List<bean>对象
private ArrayList<Banners> Flybanners;
//商品List<bean>对象
private ArrayList<ShopGoods> goods;
//type
public static final int TYPE_1 = 0xff0111;
public static final int TYPE_MAIN = 0xffffff;
public ShopAdapter(Context context) {
this.context = context;
Flybanners = new ArrayList<>();
goods = new ArrayList<>();
}
public ArrayList<Banners> getFlybanners() {
return Flybanners;
}
public void setFlybanners(ArrayList<Banners> flybanners) {
Flybanners = flybanners;
}
public List<ShopGoods> getGoods() {
return goods;
}
public void setGoods(ArrayList<ShopGoods> goods) {
this.goods = goods;
}
//自定义监听事件
public static interface OnRecyclerViewItemClickListener {
void onItemClick(View view);
//void onItemLongClick(View view);
}
//监听接口
private ShopAdapter.OnRecyclerViewItemClickListener mOnItemClickListener = null;
//监听实现
public void setOnItemClickListener(ShopAdapter.OnRecyclerViewItemClickListener listener) {
mOnItemClickListener = listener;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType){
case TYPE_1:
return new ShopAdapter.MyViewHolder1(LayoutInflater.from(parent.getContext()).inflate(R.layout.md_recycle_item_type1, parent, false));
case TYPE_MAIN:
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_shop_typemain, parent, false);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(view);
}
}
});
return new ShopAdapter.MyViewHolderMain(view);
default:
Log.d("error","viewholder is null");
return null;
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof ShopAdapter.MyViewHolder1){
bindType1((ShopAdapter.MyViewHolder1) holder, position);
}else if (holder instanceof ShopAdapter.MyViewHolderMain){
bindTypeMain((ShopAdapter.MyViewHolderMain) holder, position);
}
}
/**
* Main RecyclerView getItemCount
* @return
*/
@Override
public int getItemCount() {
return getGoods().size();
}
@Override
public int getItemViewType(int position) {
if (position == 0){
return TYPE_1;
}else {
return TYPE_MAIN;
}
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
if(manager instanceof GridLayoutManager) {
final GridLayoutManager gridManager = ((GridLayoutManager) manager);
gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
int type = getItemViewType(position);
switch (type){
case TYPE_1:
return gridManager.getSpanCount();
default:
return 1;
}
}
});
}
}
private void bindType1(ShopAdapter.MyViewHolder1 holder, int position){
//轮播图URL
ArrayList<String> ImagesUrl = new ArrayList();
if (getFlybanners().size() != 0) {
for (Banners flyBanners : getFlybanners()) {
ImagesUrl.add(flyBanners.getImg());
}
holder.flyBanner.setImagesUrl(ImagesUrl);
}
holder.flyBanner.setOnItemClickListener(new FlyBanner.OnItemClickListener() {
@Override
public void onItemClick(int position) {
// Intent intent = new Intent(context, Boutique_Detail.class);
// intent.putExtra("goods_id", Flybanners.get(position).getId());
// context.startActivity(intent);
}
});
}
private void bindTypeMain(ShopAdapter.MyViewHolderMain holder, int position){
Glide.with(context).load(getGoods().get(position).getImages()).fitCenter().placeholder(R.drawable.icon_empty).into(holder.img);
holder.textprice.setText(getGoods().get(position).getPricenow());
holder.name.setText(getGoods().get(position).getGoodsname());
holder.buy.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//去试衣间
}
});
}
public class MyViewHolder1 extends RecyclerView.ViewHolder {
public FlyBanner flyBanner;
public MyViewHolder1(View itemView) {
super(itemView);
flyBanner = (FlyBanner) itemView.findViewById(R.id.banner);
}
}
public class MyViewHolderMain extends RecyclerView.ViewHolder {
public ImageView img;//商品图片
public TextView name;//商品简介
public TextView textprice;//商品价格
public TextView buy;//立即购买
public MyViewHolderMain(final View itemView) {
super(itemView);
img = (ImageView) itemView.findViewById(R.id.img);
name = (TextView) itemView.findViewById(R.id.name);
textprice = (TextView) itemView.findViewById(R.id.price);
buy = (Button) itemView.findViewById(R.id.buy);
}
}
}
问题:
在运行项目后,服务器明明返回给了一个如下红色方框标记的商品数据,而却没有显示出来.
细挖代码后发现,是如下代码位置出了问题.
/**
* Main RecyclerView getItemCount
* @return
*/
@Override
public intgetItemCount() {
return getGoods().size();
}
@Override
public intgetItemViewType(int position) {
if (position == 0){
return TYPE_1;
}else {
return TYPE_MAIN;
}
}
当getItemCount的值return的是getGoods().size();
而getItemViewType的position == 0时加载了另一个布局,
这”另一个布局”作为了getItemCount的一个item而正好
getGoods().size()为1,也就是position == 0.
就出现了本该有的一个商品却没有显示,使数据出现丢失的情况.
理所当然getItemCount应该返回returngetGoods().size()+1;
而当我重新运行项目竟然报错 java.lang.IndexOutOfBoundsException(数组下标越界异常)
根据报错位置找到是下划线位置报错:
private void bindTypeMain(ShopAdapter.MyViewHolderMain holder,int position){
Glide.with(context).load(getGoods().get(position).getImages()).fitCenter().placeholder(R.drawable.icon_empty).into(holder.img);
holder.textprice.setText(getGoods().get(position).getPricenow());
holder.name.setText(getGoods().get(position).getGoodsname());
holder.buy.setOnClickListener(newView.OnClickListener() {
@Override
public voidonClick(View view) {
//去试衣间
}
});
}
当getGoods().size()+1后,position的值就不能再匹配对应的数据,
position是大于getGoods().size()的.
反复调试后:
private void bindTypeMain(ShopAdapter.MyViewHolderMain holder, int position){
ShopGoods shopGoods = null;
if (position <= getGoods().size()) {
shopGoods = getGoods().get(position-1);
}
Glide.with(context).load(shopGoods.getImages()).fitCenter().placeholder(R.drawable.icon_empty).into(holder.img);
holder.textprice.setText(shopGoods.getPricenow());
holder.name.setText(shopGoods.getGoodsname());
holder.buy.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//去试衣间
}
});
}
这样设置后运行项目,看到界面(喜极而泣)终于显示出来了
好接着开始写点击事件,写好后点击一看toast,靠商品对应的ID不对?
ID出现乱序.靠什么鬼?
再挖代码,挖挖找找....真TMD(别乱猜中文,我反正是想说”他妹的”)不负有心人,再activity点击事件的回调中这样写:
//item的点击事件
adapter.setOnItemClickListener(new ShopAdapter.OnRecyclerViewItemClickListener() {
@Override
public void onItemClick(View view) {
ShopGoods shopGoods = null;
int position = recyclerView.getChildAdapterPosition(view);
if (position <= goodses.size()) {
shopGoods = goodses.get(position-1);
}
startActivity(new Intent(Shop_Activity.this,Boutique_Detail.class)
.putExtra("goods_id",shopGoods.getId())
.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
);
}
});
再次以怀疑人生的心情运行一次项目,打开界面,点击,
wo~~kao~kao~kao~~~
终于搞定!!!
(转眼第二天测试!!!)然而程序总是喜欢跟哥开玩笑,当我再次运行项目(日志显示)服务器给我返回了两个商品数据,而测试手机界面确实也有两个商品数量,这里强调:是”数量”.很是奇怪第二个商品没任何数据?靠靠靠,又开始挖代码~挖啊挖,半小时过去了.然而并没有什么卵用!已接近崩溃了,早一万句草泥马已经飞奔过去.到了接近快一个小时的时候,发现了红色标注的地方:
private void bindTypeMain(ShopAdapter.MyViewHolderMain holder, int position){
ShopGoods shopGoods = null;
if (position <= getGoods().size()) {
shopGoods = getGoods().get(position-1);
}
Glide.with(context).load(shopGoods.getImages()).fitCenter().placeholder(R.drawable.icon_empty).into(holder.img);
holder.textprice.setText(shopGoods.getPricenow());
holder.name.setText(shopGoods.getGoodsname());
holder.buy.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//去试衣间
}
});
}
就这个判断,正确判断应该是
if (position <= getGoods().size()+1) {
shopGoods = getGoods().get(position-1);
}
在黑字标注的地方 +1 欧了运行项目显示正常!点击商品异常退出应用!
又TM 什么鬼?
再去点击事件查看 靠靠啊靠~~
adapter.setOnItemClickListener(new ShopAdapter.OnRecyclerViewItemClickListener() {
@Override
public void onItemClick(View view) {
ShopGoods shopGoods = null;
int position = recyclerView.getChildAdapterPosition(view);
if (position <= goodses.size()+1) {
shopGoods = goodses.get(position-1);
}
startActivity(new Intent(Shop_Activity.this,Boutique_Detail.class)
.putExtra("goods_id",shopGoods.getId())
.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
);
}
});
原来少 +1 ,加上运行项目,显示正常,点击商品跳转正常!
这次才算真正的终于搞定!!!
4年前关于RecycleView写的这个使用案列,刚入坑感觉逻辑想的有点复杂了,对新手阅读理解太不友好,还有当时只是作为一个个人笔记也没好好整理一下格式, 今日整理资料就顺便整理一下.
简单一点其实在adapter的bindTypeMain()中先赋值position -= 1; 即可
private void bindTypeMain(ShopAdapter.MyViewHolderMain holder,int position){
position -= 1;
//下面再使用position就不会越界了
}