关于使用recycleview写多种布局布局遇到的坑
最近项目中遇到多种布局嵌套使用情况,为了不多麻烦去写自定义控件监听事件的分发,便使用了recycleview.对于第一次在项目中使用这个玩意,在看过官方文档后并不是很理解其介绍(主要是全英文),于是乎在CSDN怼了个中文注解的示例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 /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;@Overrideprotected 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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();}}@Overridepublic 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 /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;//typepublic 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;}@Overridepublic 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() {@Overridepublic void onClick(View view) {if (mOnItemClickListener != null) {mOnItemClickListener.onItemClick(view);}}});return new ShopAdapter.MyViewHolderMain(view);default:Log.d("error","viewholder is null");return null;}}@Overridepublic 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*/@Overridepublic int getItemCount() {return getGoods().size();}@Overridepublic int getItemViewType(int position) {if (position == 0){return TYPE_1;}else {return TYPE_MAIN;}}@Overridepublic 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() {@Overridepublic 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){//轮播图URLArrayList<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() {@Overridepublic 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() {@Overridepublic 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*/@Overridepublic intgetItemCount() {return getGoods().size();}@Overridepublic 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 voidbindTypeMain(ShopAdapter.MyViewHolderMain holder,intposition){
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就不会越界了}