RecyclerView adapter - onCreateViewHolder receives unknown viewType - java

I got a weird exception in the function onCreateViewHolder inside my custom adapter. Probably because getItemViewType function returns different values as expected.
This is how getItemViewType function looks like:
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
#Override
public int getItemViewType(int position) {
if (position == 0)
return TYPE_HEADER;
return TYPE_ITEM;
}
As you can see, it returns 0 or 1 only, no other option.
This is how onCreateViewHolder function looks like:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_HEADER) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_groups, null);
return new HeaderHolder(view);
} else if(viewType == TYPE_ITEM) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.explore_group_row, null);
return new ItemHolder(view);
}
throw new RuntimeException("there is no type that matches the type " + viewType + " make sure your using types correctly");
}
However, I got some crashes from crashlytics that there is no type that matches with viewTypes of 6, 7, 8, 11, etc.. Not sure where it gets from...
In addition, I can't reproduce this issue, no idea how it happens.
I saw a similar issue in this thread but no solution.
Here you can see the exception from crashlytics:
Fatal Exception: java.lang.RuntimeException: there is no type that matches the type 6 make sure your using types correctly
at com.example.eliran.forum.GroupsFragment$GroupsAdapter.onCreateViewHolder(GroupsFragment.java:72)
at androidx.recyclerview.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:5)
at androidx.recyclerview.widget.RecyclerView$Recycler.a(RecyclerView.java:295)
at androidx.recyclerview.widget.GapWorker.prefetchPositionWithDeadline(GapWorker.java:14)
at androidx.recyclerview.widget.GapWorker.flushTaskWithDeadline(GapWorker.java:15)
at androidx.recyclerview.widget.GapWorker.flushTasksWithDeadline(GapWorker.java:22)
at androidx.recyclerview.widget.GapWorker.a(GapWorker.java:3)
at androidx.recyclerview.widget.GapWorker.run(GapWorker.java:71)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7078)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)
Here's the full code of the adapter:
private class GroupsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_HEADER) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_groups, null);
return new HeaderHolder(view);
} else if(viewType == TYPE_ITEM) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.explore_group_row, null);
return new ItemHolder(view);
}
throw new RuntimeException("there is no type that matches the type " + viewType + " make sure your using types correctly");
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int i) {
if (holder instanceof ItemHolder) {
ItemHolder theHolder = (ItemHolder) holder;
Group group = groups.get(i - 1);
theHolder.title.setText(group.getTitle());
theHolder.description.setText(group.getDescription());
if(group.getImage() != null) {
Picasso.get()
.load(group.getImage())
.fit()
.into(theHolder.image);
} else {
Picasso.get()
.load(R.drawable.profile_picture_holder)
.fit()
.into(theHolder.image);
}
} else if (holder instanceof HeaderHolder){
HeaderHolder theHolder = (HeaderHolder) holder;
if (showMyGroupsPb) {
theHolder.myGroupsPb.setVisibility(View.VISIBLE);
theHolder.myGroupSectionHeader.setOnBtnListener(null);
} else {
theHolder.myGroupsPb.setVisibility(View.GONE);
theHolder.myGroupSectionHeader.setOnBtnListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(rootView.getContext(), MyGroupsActivity.class);
intent.putExtra("groups", myGroups);
startActivityForResult(intent, TheFinals.JOIN_LEAVE_GROUP_CODE);
}
});
}
}
}
#Override
public int getItemViewType(int position) {
if (position == 0)
return TYPE_HEADER;
return TYPE_ITEM;
}
#Override
public int getItemCount() {
return groups.size() + 1;
}
class HeaderHolder extends RecyclerView.ViewHolder {
private ProgressBar myGroupsPb;
private SectionHeader myGroupSectionHeader;
public HeaderHolder(View view) {
super(view);
myGroupsRv = view.findViewById(R.id.groupsRecyclerView);
myGroupsPb = view.findViewById(R.id.pb);
LinearLayoutManager horizontalLayoutManager
= new LinearLayoutManager(rootView.getContext(), LinearLayoutManager.HORIZONTAL, false);
myGroupsRv.setLayoutManager(horizontalLayoutManager);
myGroupsRv.setAdapter(myGroupAdapter);
myGroupSectionHeader = view.findViewById(R.id.myGroupSectionHeader);
}
}
class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
protected TextView title;
protected ImageView image;
protected TextView description;
public ItemHolder(View view) {
super(view);
this.title = view.findViewById(R.id.title);
this.image = view.findViewById(R.id.image);
this.description = view.findViewById(R.id.description);
view.setOnClickListener(this);
}
#Override
public void onClick(View view) {
int itemPosition = groupsRecyclerView.getChildLayoutPosition(view) - 1;
selectedGroupIndex = itemPosition;
Intent intent = new Intent(rootView.getContext(), GroupActivity.class);
intent.putExtra("group", groups.get(itemPosition));
startActivityForResult(intent, TheFinals.JOIN_LEAVE_GROUP_CODE);
}
}
}

Related

How can i modify getItemViewType() with adapter input as a string and cursor?

I have created a recycler view and want to put two lists in it.
The first list is just a string that gets changed into fonts, which I have implemented in the onBindView method.
The second list is the cursor which includes the favorite fonts of the users.
I followed this answer on SO but I don't have any idea how I'm going to modify the getItemViewType() method so that I can get to know which list type is currently calling for view.
My adapter code
here data is the class that contains the list of fonts
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final int VIEW_TYPE_TEXTVIEW = 0;
private final int VIEW_TYPE_ITEM_1 = 1;
private int total_no_of_row;
private Context context;
private String str;
private Cursor cursor;
private final LayoutInflater inflater;
public MyAdapter(Context context, int total_no_of_row, String str , Cursor cursor) {
this.context = context;
this.total_no_of_row = total_no_of_row;
this.str = str;
this.cursor = cursor;
inflater = LayoutInflater.from(context);
}
#Override
public int getItemViewType(int position) {
return super.getItemViewType(position);
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if(viewType == VIEW_TYPE_TEXTVIEW) {
View view = inflater.inflate(R.layout.row_layout, parent, false);
return new Item1Holder(view);
}
else if (viewType == VIEW_TYPE_ITEM_1){
View view = inflater.inflate(R.layout.row_layout, parent, false);
return new Item2Holder(view);
}
return null;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
if(holder instanceof Item1Holder){
data d = new data();
cursor.moveToPosition(position);
Log.d("this2",cursor.getInt(1)+"");
String temp = d.Convert(str, cursor.getInt(1));
((Item1Holder)holder).textView.setText(temp);
((Item1Holder)holder).rowNumber.setText("⚫");
((Item1Holder)holder).fav.setImageResource(R.drawable.ic_baseline_favorite_24);
}else if(holder instanceof Item2Holder){
data d = new data();
String temp = d.Convert(str, position);
((Item2Holder)holder).textView.setText(temp);
((Item2Holder)holder).rowNumber.setText((position + 1) + ".");
if (util.COPY) {
if (position == util.CURRENT_POSITION) {
((Item2Holder)holder).imageView.setBackgroundResource(R.drawable.background_when_row_clicked);
} else {
((Item2Holder)holder).imageView.setBackgroundResource(R.drawable.background_when_row_clicked_two);
}
}
}
}
#Override
public int getItemCount() {
return total_no_of_row + cursor.getCount();
}
class Item1Holder extends RecyclerView.ViewHolder {
TextView textView;
ImageView imageView , fav;
TextView rowNumber;
public Item1Holder(#NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.text_body);
imageView = itemView.findViewById(R.id.copy_png);
rowNumber = itemView.findViewById(R.id.row_number);
fav = itemView.findViewById(R.id.fav);
}
}
class Item2Holder extends RecyclerView.ViewHolder {
TextView textView;
ImageView imageView , fav;
TextView rowNumber;
public Item2Holder(#NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.text_body);
imageView = itemView.findViewById(R.id.copy_png);
rowNumber = itemView.findViewById(R.id.row_number);
fav = itemView.findViewById(R.id.fav);
}
}
}
in main activity
here temp is the string that users want to convert into the fonts
Cursor cursor = show();
myAdapter = new MyAdapter(this , 500 , temp , cursor);
recyclerView.setAdapter(myAdapter);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
I'm not a professional android developer I'm still learning the stuff. so sorry if my code is too bad to understand
You are passing null in the onCreateViewHolder() method, and not specified position in getItemViewType() method. All you have to do is, update your code. do not pass the null value in the onCreateViewHolder method. Below code, I've tried, and works perfectly. Happy coding...
private static final int CATEGORY = 1;
private static final int NAME = 2;
private Collection<CategorizedName> data;
#Override
public int getItemViewType(int position) {
if (items.get(position) instanceof Category) {
return CATEGORY;
} else if (items.get(position) instanceof Name) {
return NAME;
}
throw new RuntimeException('error');
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
RecyclerView.ViewHolder viewHolder;
LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
switch (viewType) {
case CATEGORY:
View categoryView = inflater.inflate(R.layout.viewholder_category, viewGroup,
false);
viewHolder = new CategoryViewHolder(categoryView);
break;
case NAME:
View nameView = inflater.inflate(R.layout.viewholder_name, viewGroup, false);
viewHolder = new NameViewHolder(nameView);
break;
}
return viewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
switch (viewHolder.getItemViewType()) {
case USER:
CategoryViewHolder categoryViewHolder = (CategoryViewHolder) viewHolder;
bindCategoryViewHolder(categoryViewHolder, position);
break;
case IMAGE:
NameViewHolder nameViewHolder = (NameViewHolder) viewHolder;
bindNameViewHolder(nameViewHolder, position);
break;
}
}
Replace your getItemViewType() with this
#Override
public int getItemViewType(int position){
if(position < total_no_of_row){
return VIEW_TYPE_TEXTVIEW;
}
if(position - total_no_of_row < cursor.getCount()){
return VIEW_TYPE_ITEM_1;
}
return -1;
}
you can also refer to my answer this

Multiple type of views in Recycler View not setting correctly

I have two array list and want to set the adapter so that it looks like below screen one. But when I am setting adapter I am getting view like second screen. This is my code of Adapter:
public class ItemArrayAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
final int VIEW_TYPE_MESSAGE = 0;
final int VIEW_TYPE_IMAGE = 1;
private static final String TAG = "ItemArrayAdapter";
Context context;
ArrayList<BeforLoginDao.Article> mArticleList;
ArrayList<BeforLoginDao.Quiz> mQuizList;
int i=0;
public ItemArrayAdapter(Context context,ArrayList<BeforLoginDao.Quiz> mQuizList, ArrayList<BeforLoginDao.Article> mArticleList ){
this.context = context;
this.mArticleList = mArticleList;
this.mQuizList = mQuizList;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View itemView;
if(viewType == VIEW_TYPE_MESSAGE){
itemView= LayoutInflater.from(parent.getContext()).inflate(R.layout.article_layout,parent,false);
return new ArticleViewHolder(itemView);
}
else if(viewType == VIEW_TYPE_IMAGE){
itemView= LayoutInflater.from(parent.getContext()).inflate(R.layout.quiz,parent,false);
return new QuizViewHolder(itemView);
}
return null;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position){
Log.e(TAG, "onBindViewHolder: " +position);
if(viewHolder instanceof ArticleViewHolder){
((ArticleViewHolder) viewHolder).populate(mArticleList.get(position));
}
else if(viewHolder instanceof QuizViewHolder){
((QuizViewHolder) viewHolder).populate(mQuizList.get(position - mArticleList.size()));
}
}
#Override
public int getItemCount(){
return mArticleList.size() + mQuizList.size();
}
#Override
public int getItemViewType(int position){
Log.e(TAG, "getItemViewType: " +position );
if(position < mArticleList.size()){
return VIEW_TYPE_MESSAGE;
}
if(position - mArticleList.size() < mQuizList.size()){
return VIEW_TYPE_IMAGE;
}
return -1;
}
public class ArticleViewHolder extends RecyclerView.ViewHolder {
public TextView mTitle, mTag1, mTag2 ;
ImageView mImageView;
public ArticleViewHolder(View itemView) {
super(itemView);
mTitle = itemView.findViewById(R.id.tv_article_title);
mTag1 = itemView.findViewById(R.id.tv_article_tag1);
mTag2 = itemView.findViewById(R.id.tv_article_tag2);
mImageView=itemView.findViewById(R.id.img_article);
}
public void populate(BeforLoginDao.Article chatWrapper){
mTitle.setText(chatWrapper.articleTitle.toString());
mTag1.setText(chatWrapper.subtag1);
mTag2.setText(chatWrapper.subtag2);
}
}
public class QuizViewHolder extends RecyclerView.ViewHolder {
public TextView tvQuizTitle, tvQuizTag1, tvQuizTag2 ;
public QuizViewHolder(View itemView) {
super(itemView);
tvQuizTitle = itemView.findViewById(R.id.tv_quiz_title);
tvQuizTag1 =itemView.findViewById(R.id.tv_quiz_tag1);
tvQuizTag2=itemView.findViewWithTag(R.id.tv_quiz_tag2);
}
public void populate(BeforLoginDao.Quiz imageDataWrapper){
Log.e(TAG, "ViewHolderTwo: " +imageDataWrapper.toString() );
tvQuizTitle.setText(imageDataWrapper.quizTitle);
tvQuizTag1.setText(imageDataWrapper.quiz_subtag1);
}
}
}
To solve this error I also checked many solution on SO.
Ref: enter link description here
Below is screen one and screen two. Screen one that I need, Screen two that I am getting.

Recyclerview holder mixes on scroll

When scroll recyclerview some items mixes. After I add ads after every 15 items, holder get wrong data. Some items are vip items. I will change background color of these items. But when I scroll it dublicates mixes. How can I solve?
This is my adapter
private Context mCtx;
private List<Car> carList;
private RecyclerViewAnimator mAnimator;
private int AD_TYPE=1;
private int CONTENT_TYPE=2;
private int LIST_AD_DELTA=15;
public ProductAllCarAdapter(RecyclerView recyclerView,Context mCtx, List<Car> carList) {
this.mCtx = mCtx;
this.carList = carList;
mAnimator = new RecyclerViewAnimator(recyclerView);
}
#Override
public ProductAllCarAdapter.ProductViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType == AD_TYPE){
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.product_add_item, null);
ProductAllCarAdapter.ProductViewHolder vh = new ProductAllCarAdapter.ProductViewHolder(itemView);
mAnimator.onCreateViewHolder(itemView);
return vh;
} else {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.product_car_item, null);
ProductAllCarAdapter.ProductViewHolder vh = new ProductAllCarAdapter.ProductViewHolder(itemView);
mAnimator.onCreateViewHolder(itemView);
return vh;
}
}
#Override
public int getItemViewType(int position) {
if (position>0 && position % LIST_AD_DELTA == 0)
return AD_TYPE;
return CONTENT_TYPE;
}
#Override
public void onBindViewHolder(ProductAllCarAdapter.ProductViewHolder holder, int position) {
if (getItemViewType(position) == CONTENT_TYPE) {
final Car car = carList.get(holder.getAdapterPosition());
GlideApp.with(mCtx).load(car.getImg()).into(holder.imageView);
if (car.getVip() == 1) {
holder.relativeLayout.setBackgroundColor(ContextCompat.getColor(mCtx, R.color.colorVip));
holder.imageViewVIP.setVisibility(View.VISIBLE);
}
final String carid = String.valueOf(car.getCarid());
mAnimator.onBindViewHolder(holder.itemView, position);
} else {
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Context mcontext = view.getContext();
Bundle bundle = ActivityOptionsCompat.makeCustomAnimation(mcontext, android.R.anim.fade_in, android.R.anim.fade_out).toBundle();
Intent intent = new Intent(mcontext, AdsItem.class);
mcontext.startActivity(intent, bundle);
}
});
mAnimator.onBindViewHolder(holder.itemView, position);
}
}
private int getRealPosition(int position) {
if (LIST_AD_DELTA == 0) {
return position;
} else {
return position - position / LIST_AD_DELTA;
}
}
#Override
public long getItemId(int position) { return position; }
#Override
public int getItemCount() {
int additionalContent = 0;
if (carList.size() > 0 && carList.size() > LIST_AD_DELTA) {
additionalContent = ( carList.size() / LIST_AD_DELTA);
}
return carList.size() + additionalContent;
}
public static class ProductViewHolder extends RecyclerView.ViewHolder {
private View mView;
ImageView imageView, imageViewVIP;
RelativeLayout relativeLayout;
public ProductViewHolder(View itemView) {
super(itemView);
mView = itemView;
imageView = itemView.findViewById(R.id.imageView);
imageViewVIP = itemView.findViewById(R.id.imageViewVIP);
relativeLayout = itemView.findViewById(R.id.relativeLayoutpc);
}
public void setOnClickListener(View.OnClickListener listener) {
mView.setOnClickListener(listener);
}
}
I think problem onBindViewHolder function use wrong holder. ArrayList also return true value but on scroll it mixes.
You just have to add the corresponding else of the following if statement block.
if (car.getVip() == 1) {
holder.relativeLayout.setBackgroundColor(ContextCompat.getColor(mCtx, R.color.colorVip));
holder.imageViewVIP.setVisibility(View.VISIBLE);
} else {
holder.relativeLayout.setBackgroundColor(ContextCompat.getColor(mCtx, R.color.colorNormal));
holder.imageViewVIP.setVisibility(View.GONE);
}
This is inside your onBindViewHolder function where the view type is CONTENT_TYPE.
Hope that solves your problem.

Show different view at end of recyclerview in every section like paytm

I have a created this design. Which consists of list of list.
Now I wanted to add a view as "See All" in every section at the end of every recycler view. I wanted like this which is done by paytm.
Here is my code. I have two recyclerview one is vertical and another is horizontal. Here is my BaseRowAdapter which contains another recyclerview.
BaseRowAdapter.java
public class BaseRowAdapter extends RecyclerView.Adapter<BaseRowAdapter.ViewHolder> {
public ArrayList<MainType> types;
private Context context;
private LayoutInflater layoutInflater;
public BaseRowAdapter(ArrayList<MainType> types, Context context) {
this.types = types;
this.context = context;
this.layoutInflater = LayoutInflater.from(context);
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = layoutInflater.inflate(R.layout.vertical_scroll_single_entry, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.recyclerView.setAdapter(new BaseRowItemAdapter(context, types.get(position).list));
holder.recyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
holder.recyclerView.setHasFixedSize(true);
holder.tvHeading.setText(types.get(position).name);
}
#Override
public int getItemCount() {
return types.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
RecyclerView recyclerView;
TextView tvHeading;
public ViewHolder(View itemView) {
super(itemView);
recyclerView = (RecyclerView) itemView.findViewById(R.id.rvItems);
tvHeading = (TextView) itemView.findViewById(R.id.tvHeading);
}
}
}
And for Inner recyclerview I have to show see more layout at the end. I have done something like this. which result into crashing app.
BaseRowItemAdapter.java
public class BaseRowItemAdapter extends RecyclerView.Adapter<BaseRowItemAdapter.CustomViewHolder> {
private Context context;
private ArrayList<SubType> subTypes;
private LayoutInflater inflater;
public BaseRowItemAdapter(Context context, ArrayList<SubType> subTypes) {
this.context = context;
this.subTypes = subTypes;
this.inflater = LayoutInflater.from(context);
}
#Override
public int getItemViewType(int position) {
return (position == subTypes.size()) ? R.layout.see_more : R.layout.single_item_card;
}
#Override
public CustomViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
if (viewType == R.layout.single_item_card) {
view = inflater.inflate(R.layout.single_item_card, parent, false);
} else {
view = inflater.inflate(R.layout.see_more, parent, false);
}
return new CustomViewHolder(view);
}
#Override
public void onBindViewHolder(CustomViewHolder holder, int position) {
SubType subType = subTypes.get(position);
if (position != subTypes.size()) {
holder.tvProductName.setText(subType.name);
Picasso.with(context).load(subType.image).into(holder.ivProduct);
if (!subType.disc.equals("0")) {
holder.tvDiscountPercentage.setText(subType.disc + "%");
holder.tvActualAmount.setPaintFlags(holder.tvActualAmount.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
int a = Integer.parseInt(subType.amount);
float b = Float.parseFloat(subType.disc) / 100;
holder.tvActualAmount.setText("₹ " + subType.amount);
int c = (int) (a * b);
holder.tvDiscountAmount.setText("₹ " + c);
} else {
holder.tvDiscountPercentage.setBackgroundColor(Color.parseColor("#ffffff"));
holder.tvActualAmount.setText("₹ " + subType.amount);
}
} else {
holder.seeMore.setText("See More >>");
}
}
#Override
public int getItemCount() {
return subTypes.size()+1;
}
public class CustomViewHolder extends RecyclerView.ViewHolder {
public ImageView ivProduct;
public TextView tvProductName, tvActualAmount, tvDiscountAmount, tvDiscountPercentage, seeMore;
public CustomViewHolder(View itemView) {
super(itemView);
tvProductName = (TextView) itemView.findViewById(R.id.tvProductName);
tvActualAmount = (TextView) itemView.findViewById(R.id.tvActualAmount);
tvDiscountAmount = (TextView) itemView.findViewById(R.id.tvDiscountAmount);
tvDiscountPercentage = (TextView) itemView.findViewById(R.id.tvDiscountPercentage);
ivProduct = (ImageView) itemView.findViewById(R.id.ivProduct);
seeMore = (TextView) itemView.findViewById(R.id.seeMore);
}
}
}
Here are my both model classes.
public class SubType {
public int id;
public String typeId;
public String name;
public String image;
public String unit;
public String amount;
public String disc;
}
public class MainType {
public int id;
public String name;
public ArrayList<SubType> list;
}
My few logcat details
java.lang.IndexOutOfBoundsException: Invalid index 2, size is 2
at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)
at java.util.ArrayList.get(ArrayList.java:308)
at com.ashishkudale.linksolution.adapters.BaseRowItemAdapter.onBindViewHolder(BaseRowItemAdapter.java:54)
at com.ashishkudale.linksolution.adapters.BaseRowItemAdapter.onBindViewHolder(BaseRowItemAdapter.java:23)
It is crashing because of IndexOutOfBoundsException. I don't know how to solve this please help.
Change your onBindViewHolder
#Override
public void onBindViewHolder(CustomViewHolder holder, int position) {
if (position != subTypes.size()) {
SubType subType = subTypes.get(position);
holder.tvProductName.setText(subType.name);
Picasso.with(context).load(subType.image).into(holder.ivProduct);
if (!subType.disc.equals("0")) {
holder.tvDiscountPercentage.setText(subType.disc + "%");
holder.tvActualAmount.setPaintFlags(holder.tvActualAmount.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
int a = Integer.parseInt(subType.amount);
float b = Float.parseFloat(subType.disc) / 100;
holder.tvActualAmount.setText("₹ " + subType.amount);
int c = (int) (a * b);
holder.tvDiscountAmount.setText("₹ " + c);
} else {
holder.tvDiscountPercentage.setBackgroundColor(Color.parseColor("#ffffff"));
holder.tvActualAmount.setText("₹ " + subType.amount);
}
} else {
holder.seeMore.setText("See More >>");
}
}

IndexOutOfBoundsException when clicking RecyclerView item

Having problem when clicking items of RecyclerView, app crashes and giving the output:
java.lang.IndexOutOfBoundsException: Invalid index 24, size is 20
at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)
at java.util.ArrayList.get(ArrayList.java:308)
at com.google.gson.JsonArray.get(JsonArray.java:147)
at com.devpocket.kvartirka.MainActivity.itemClicked(MainActivity.java:334)
at com.devpocket.kvartirka.Adapters.OffersAdapter$ViewHolder.onClick(OffersAdapter.java:161)
at android.view.View.performClick(View.java:5162)
at android.view.View$PerformClick.run(View.java:20873)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:5834)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1388)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1183)
java.lang.IndexOutOfBoundsException: Invalid index 24, size is 20
The code which I'm using is like this, this is an adapter:
public class OffersAdapter extends RecyclerView.Adapter<OffersAdapter.ViewHolder> {
private ClickListener clickListener;
private CityData cityData;
private ArrayList<CityData> cityItemList = new ArrayList<CityData>();
private Context mContext;
private String text;
public static Button headerButton1;
public OffersAdapter(Context context, ArrayList<CityData> cityItemList, String text) {
this.cityItemList = cityItemList;
this.mContext = context;
this.text = text;
}
#Override
public int getItemViewType(int position) {
int viewType;
if (position == 0) {
viewType = 0;
} else {
viewType = 1;
}
return viewType;
}
#Override
public OffersAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, final int viewType) {
View itemLayoutView;
ViewHolder viewHolder;
if (viewType == 0) {
itemLayoutView = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.logo_layout, viewGroup, false);
viewHolder = new ViewHolder(itemLayoutView, viewType);
} else {
itemLayoutView = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.offers_singleitem, viewGroup, false);
viewHolder = new ViewHolder(itemLayoutView, viewType);
}
return viewHolder;
}
#Override
public void onBindViewHolder(OffersAdapter.ViewHolder viewHolder, int position) {
if (position > 0) {
cityData = cityItemList.get(position-1);
String type = cityData.getType();
if ("flat".equals(type)) {
viewHolder.address.setText(cityData.getAddress());
viewHolder.description.setText(cityData.getDescription());
viewHolder.roomNumbers.setText(cityData.getRoomNumbers());
if (TextUtils.isEmpty(cityData.getMetro())) {
viewHolder.metroImageView.setVisibility(View.GONE);
}
viewHolder.metro.setText(cityData.getMetro());
viewHolder.prices.setText(cityData.getPrices() + " ₽");
Ion.with(viewHolder.offerImage)
.fitXY()
.load(cityData.getURL());
} else if("cottage".equals(type)) {
viewHolder.address.setText(cityData.getAddress());
viewHolder.description.setText(cityData.getDescription());
viewHolder.roomNumbers.setVisibility(View.GONE);
viewHolder.cottageImage.setVisibility(View.VISIBLE);
if (TextUtils.isEmpty(cityData.getMetro())) {
viewHolder.metroImageView.setVisibility(View.GONE);
}
viewHolder.metro.setText(cityData.getMetro());
viewHolder.prices.setText(cityData.getPrices() + " ₽");
Ion.with(viewHolder.offerImage)
.fitXY()
.load(cityData.getURL());
}
}
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView roomNumbers; //количество комнат
public TextView address; //адресс квартиры
public TextView description; //описание квартиры
public TextView metro; //описание квартиры
public TextView prices; //цена за день
public ImageView offerImage; //картинка квартиры
public ImageView cottageImage; //обозначение коттеджа
public ImageView metroImageView; //значок метро
public ViewHolder(View itemView, int position) {
super(itemView);
if(position > 0) {
roomNumbers = (TextView) itemView.findViewById(R.id.roomNumbers);
address = (TextView) itemView.findViewById(R.id.addressTextView);
description = (TextView) itemView.findViewById(R.id.conditionsTextView);
metro = (TextView) itemView.findViewById(R.id.metroTextView);
offerImage = (ImageView) itemView.findViewById(R.id.imageView);
prices = (TextView) itemView.findViewById(R.id.priceTV);
cottageImage = (ImageView) itemView.findViewById(R.id.cottageImage);
metroImageView = (ImageView) itemView.findViewById(R.id.metroImageView);
itemView.setOnClickListener(this);
} else {
headerButton1 = (Button) itemView.findViewById(R.id.headerButton1);
headerButton1.setText(Html.fromHtml(text));
headerButton1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(mContext, FilterActivity.class);
mContext.startActivity(intent);
}
});
}
}
#Override
public void onClick(View v) {
if(clickListener != null) {
notifyDataSetChanged();
clickListener.itemClicked(v, getPosition());
}
}
}
#Override
public int getItemCount() {
return (null != cityItemList ? cityItemList.size() : 0);
}
public void setClickListener(ClickListener clickListener) {
this.clickListener = clickListener;
}
public interface ClickListener {
public void itemClicked(View view, int position);
}
}
And this is how adding data into adapter:
private ArrayList<CityData> cityList = new ArrayList<CityData>();
CityData cityItem = new CityData();
cityItem.setAddress(String.valueOf(flatAddress));
cityItem.setType(type);
cityItem.setDescription(description);
cityItem.setRoomNumbers(rooms);
cityItem.setMetro(metro);
cityItem.setUrl(imageURL);
cityItem.setPrices(day);
cityList.add(cityItem);
adapter.notifyDataSetChanged();
adapter.setClickListener(MainActivity.this);
And the on itemClicked
#Override
public void itemClicked(View view, int position) {
String offerData = null;
for(int i = 0; i <= position; i++ ) {
if(i == position) {
JsonElement obj = jsonObjectToPass.get(position-1);
offerData = String.valueOf(obj);
}
}
Intent intent = new Intent(MainActivity.this, OfferInfo.class);
intent.putExtra("offerData", offerData);
intent.putExtra("position", position);
startActivity(intent);
}
Change itemClicked() to this
#Override
public void itemClicked(View view, int position) {
String offerData = null;
JsonElement obj = jsonObjectToPass.get(position-1);
offerData = String.valueOf(obj);
Intent intent = new Intent(MainActivity.this, OfferInfo.class);
intent.putExtra("offerData", offerData);
intent.putExtra("position", position);
startActivity(intent);
}
No need of for loop while you are getting the position directly
Your problem is this line: JsonElement obj = jsonObjectToPass.get(position-1);
Without further information, you try to retrieve the element #24 (position == 25) when there is only 20 items in the list.
Maybe add a validation in your if like this:
if(i == position && jsonObjectToPass.size() < position) {
And check your condition, it seems wrong. Your code will enter in the if only one time in the for loop. So why putting a loop there ?

Categories