java.lang.ArrayIndexOutOfBoundsException: length=27; index=-1 - java

I'm getting this error when i'm implementing footer within recycler view.
This is how i have done it. I was using two types for showing different views in list, but something is not set well in method getItemCount() or maybe when i'm getting position of clicked item model in list.
This is what i have so far:
private final int VIEW_TYPE_ITEM = 0;
private final int VIEW_TYPE_FOOTER = 1;
#Override
public int getItemCount() {
return mUsers == null ? 0 : mUsers.size() + 1;
}
#Override
public int getItemViewType(int position) {
if (isFooterPosition(position)) {
return VIEW_TYPE_FOOTER;
}
return VIEW_TYPE_ITEM;
}
private boolean isFooterPosition(int position) {
return position == mUsers.size() + 1;
}
private User getUser (int position) {
return mUsers.get(position - 1); // Here i'm getting an error mentioned in title
}
Edit:
if (holder instanceof UserHolder) {
final User user = getUser(position);
UserHolder userViewHolder = (UserHolder) holder;
userViewHolder.tvUserName.setText(user.getName());
userViewHolder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mOnItemClickListener.onItemClick(v, position);
}
});
} else if (holder instanceof FooterViewHolder) {
FooterViewHolder footerViewHolder = (FooterViewHolder) holder;
Typeface typeface = Typeface.createFromAsset(mContext.getAssets(), "Lato-Thin.ttf");
footerViewHolder.mButton.setTypeface(typeface);
}
I have some items for normal holder view in list and one item for footer view.

mUsers.get(position - 1); will crash when the position is 0 because you're looking for the item at index -1 which is invalid.
If you're adding a footer, which will be present after all of the previous items, then do you need do the substraction?
Position 0 => User 0
Position 1 => User 1
Position N => User N
Position N + 1 => Footer
It might be better to just return mUsers.get(position).
Edit: There's another small issue:
Here's an issue:
private boolean isFooterPosition(int position) {
return position == mUsers.size() + 1;
}
mUsers.size() is 20, so users will have positions 0-19.
isFooterPosition should return true for 20 (users size + 1). However, that will return false because the footer is at position 21.
Thus, you have a spot (20) that is completely invalid.
private boolean isFooterPosition(int position) {
return position == mUsers.size();
}

I think you can modify getUser() method like following:
private boolean isValidPos(int position){
return position >= 0 && position < mUsers.size();
}
private User getUser (int position) {
if (isFooterPosition(position)) return null;
return isValidPos(position) ? mUsers.get(position) : null;
}

I can't see exactly what you want to do, but, as someone said before me, the error is in mUsers.get(position - 1);, because it's gonna search for a negative index if position == 0.
So, if you really need the subtraction, you can do like this:
private User getUser (int position) {
if(position != 0)
return mUsers.get(position - 1);
else
return mUsers.get(position);
}
But, as you can see, for position == 0, it will return the same output as position == 1.

I have found a solution and i have also extended RecyclerView with one more typeview for showing one view where there is no item:
First i have declared these variables in my adapter:
public static final int COUNT_FOOTER = 1;
public static final int COUNT_NO_ITEMS = 1;
private final int VIEW_TYPE_ITEM = 0;
private final int VIEW_TYPE_FOOTER = 1;
private final int VIEW_TYPE_NO_ITEM = 2;
After that i have define three view types for my list:
#Override
public int getItemViewType(int position) {
if (!mUsers.isEmpty()) {
if (position < mUsers.size()) {
return VIEW_TYPE_ITEM;
} else {
return VIEW_TYPE_FOOTER;
}
} else {
if (position == 0) {
return VIEW_TYPE_NO_ITEM;
} else {
return VIEW_TYPE_FOOTER;
}
}
}
#Override
public int getItemCount() {
if (!mUsers.isEmpty()) {
return mUsers.size() + COUNT_FOOTER;
} else {
return COUNT_NO_ITEMS + COUNT_FOOTER;
}
}
private User getUser (int position) {
return mUsers.get(position);
}

Related

OnbindViewHolder holder not returning exact position when more data are added

I'm having this problem when ever more data is added to the recycleview the total number of of item to be be displayed reduces by 1 or 2 depending on the number of items in the recycleview or it will return full size sometimes
Here is my code
private static final int MENU_ITEM_VIEW_TYPE= 0;
// The Native Express ad view type.
private static final int NATIVE_AD_VIEW_TYPE = 1;
private static final int ITEM_FEED_COUNT = 6;
private final Activity activity;
Keystore stopAdsPref;
Boolean isStopAd;
public AdapterPosts(Context context, Activity activity, List<ModelPost> postList) {
this.context = context;
this.postList = postList;
this.activity = activity;
myUid = Objects.requireNonNull(FirebaseAuth.getInstance().getCurrentUser()).getUid();
likeRef = FirebaseDatabase.getInstance().getReference().child("Likes");
postsRef = FirebaseDatabase.getInstance().getReference().child("Posts");
stopAdsPref = Keystore.getInstance(context);
isStopAd = stopAdsPref.getBoolean(STOP_ADS);
}
public void setItems(ArrayList<ModelPost> emp) {
postList.addAll(emp);
}
#NonNull
#Override
public MyHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if (viewType == MENU_ITEM_VIEW_TYPE) {
View view = LayoutInflater.from(context).inflate(R.layout.row_posts, parent, false);
return new AdapterPosts.MyHolder(view);
} else if (viewType == NATIVE_AD_VIEW_TYPE) {
View view = LayoutInflater.from(context).inflate(R.layout.layout_ad, parent, false);
return new AdapterPosts.MyHolder(view);
} else {
return null;
}
}
I think the problem is after int pos = position - Math.round(position / ITEM_FEED_COUNT);
#Override
public void onBindViewHolder(#NonNull
MyHolder holder, int position) {
Log.e(TAG, "onBindViewHolder: step 1 " + position);
if (holder.getItemViewType() == MENU_ITEM_VIEW_TYPE) {
int pos = position - Math.round(position / ITEM_FEED_COUNT);
Log.e(TAG, "onBindViewHolder: step 2: " + pos);
((AdapterPosts.MyHolder) holder).bindData(postList.get(pos), pos, holder);
} else if (holder.getItemViewType() == NATIVE_AD_VIEW_TYPE) {
if (isStopAd.equals(true)) {
Log.e(TAG, "AD stop");
} else {
((AdapterPosts.MyHolder) holder).bindAdData();
}
}
requestQueue = Volley.newRequestQueue(context);
}
#Override
public int getItemCount() {
if (postList.size() > 0) {
return postList.size() + Math.round(postList.size() / ITEM_FEED_COUNT);
}
return postList.size();
}
#Override
public int getItemViewType(int position{
if ((position + 1) % ITEM_FEED_COUNT== 0) {
return NATIVE_AD_VIEW_TYPE;
}
return MENU_ITEM_VIEW_TYPE;
}
I was able to solve this it might be helpful to someone in the future code below
Answer from here
#Override
public int getItemCount() {
int additionalContent = 0;
if (data.size() > 0 && LIST_AD_DELTA > 0 && data.size() > LIST_AD_DELTA) {
additionalContent = data.size() / LIST_AD_DELTA;
}
return data.size() + additionalContent;
}
#Override
public void onBindViewHolder(BaseRecyclerHolder baseHolder, int position) {
if (getItemViewType(position) == CONTENT) {
ContentRecyclerHolder holder = (ContentRecyclerHolder) baseHolder;
Content content = data.get(getRealPosition(position));
} else {
AdRecyclerHolder holder = (AdRecyclerHolder) baseHolder;
AdRequest adRequest = new AdRequest.Builder().build();
if (adRequest != null && holder.adView != null){
holder.adView.loadAd(adRequest);
}
}
}

Stop recyclerview from trying to update when "Favourite" button is clicked

I have a "favourite" button for each row of my recyclerview which the user clicks when the like the image (obviously). Each row is a cardview that I only want to "flip" when the user opens the fragment.
When the user clicks the button I update my database with "Y" or "N".
My problem is that my recyclerview refreshes even though the list hasn't changed. When it refreshes all my cards flip which I do not want. How can I stop the recyclerview from updating when the button is clicked?
Here is my adapter class
#Override
public void onBindViewHolder(#NotNull final ClothesViewHolder holder, final int position) {
String image;
if (flip) {
holder.flipView.flipTheView();
}
ClothingItem current = mClothingItems.get(position);
holder.itemNameView.setText(current.getItem());
holder.categoryNameView.setText(current.getCategory());
holder.seasonNameView.setText(current.getSeason());
Integer yesCount = current.getYesCount();
Integer noCount = current.getNoCount();
if (current.getFavourite().equalsIgnoreCase("N")) {
holder.animationView.setProgress(0);
}
else {
holder.animationView.setProgress(1);
}
holder.yesTextView.setText(String.valueOf(yesCount));
holder.noTextView.setText(String.valueOf(noCount));
image = current.getPhotoPath();
Glide.with(holder.cardView)
.load(image)
.into(holder.pictureView);
flip = false;
} catch (NullPointerException e) {
Log.e("Picture","onBindViweHolder: Null Point:" + e.getMessage());
}
holder.animationView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
listener.onFavouriteClick(position);
}
});
public interface clickButtons {
void onFavouriteClick(int position);
}
Fragment Class
#Override
public void onFavouriteClick(int position) {
RecyclerView.ViewHolder holder = recyclerView.findViewHolderForAdapterPosition(position);
LottieAnimationView animationView = holder.itemView.findViewById(R.id.favouriteAnimation);
ClothingItem item = springList.get(position);
final Long id = item.getId();
if (animationView.getProgress() > 0) {
animationView.setProgress(0);
mClothingViewModel.updateFavourite(id.intValue(), "N");
adapter.notifyItemChanged(position,"favourite");
} else if (animationView.getProgress() == 0) {
animationView.playAnimation();
mClothingViewModel.updateFavourite(id.intValue(),"Y");
}
}
I tried to use onBindViewHolder with payloads but I get the same result. I think I'm not calling this properly
adapter.notifyItemChanged(position,"favourite");
Adapter class
#Override
public void onBindViewHolder(final ClothesViewHolder holder ,final int position, final List<Object> payloads){
String image;
if(!payloads.isEmpty()) {
ClothingItem current = mClothingItems.get(position);
holder.itemNameView.setText(current.getItem());
holder.categoryNameView.setText(current.getCategory());
holder.seasonNameView.setText(current.getSeason());
Integer yesCount = current.getYesCount();
Integer noCount = current.getNoCount();
if(yesCount == null) {
yesCount = 0;
}
if (noCount == null) {
noCount = 0;
}
if (current.getFavourite() == null || current.getFavourite().equalsIgnoreCase("N")) {
holder.animationView.setProgress(0);
}
else {
holder.animationView.setProgress(1);
}
// Log.d("Counting","Yes count " + yesCount + " no count " + noCount);
holder.yesTextView.setText(String.valueOf(yesCount));
holder.noTextView.setText(String.valueOf(noCount));
image = current.getPhotoPath();
Glide.with(holder.cardView)
.load(image)
.into(holder.pictureView);
} else {
onBindViewHolder(holder,position);
}
}
Fragment
#Override
public void onFavouriteClick(int position) {
RecyclerView.ViewHolder holder = recyclerView.findViewHolderForAdapterPosition(position);
LottieAnimationView animationView = holder.itemView.findViewById(R.id.favouriteAnimation);
ClothingItem item = springList.get(position);
final Long id = item.getId();
if (animationView.getProgress() > 0) {
animationView.setProgress(0);
mClothingViewModel.updateFavourite(id.intValue(), "N");
adapter.notifyItemChanged(position,"favourite");
} else if (animationView.getProgress() == 0) {
animationView.playAnimation();
mClothingViewModel.updateFavourite(id.intValue(),"Y");
adapter.notifyItemChanged(position,"favourite");
}
}
Recyclerview is updated or refreshed when notifiydataSetChanged is called.
try to remove notifiyDataSetChanged in the click event maybe

Java: How to make own total number counter for every RecyclerView item?

I need to make a total number counter for every RecyclerView item individually. Here are a picture and code to clear this situation out:
TTL is that total counter that I need. When I click player 1 plus symbol, total counter in that particular item should be increased by 1 and minus symbol decrease by 1.
What I have at the moment:
ViewHolder (handle minus and plus symbols):
public static class GameViewHolder extends RecyclerView.ViewHolder {
public TextView mTextPlayer, mTextPar, mTotalTxt, mNumberTotal;
public ImageView mImageMinus, mImagePlus;
public GameViewHolder(#NonNull View itemView, final GameAdapter.OnItemClickListener listener) {
super(itemView);
mTextPlayer = itemView.findViewById(R.id.gameNameRecycler);
mTextPar = itemView.findViewById(R.id.gameParNumberRecycler);
mImageMinus = itemView.findViewById(R.id.game_minus_btn);
mImagePlus = itemView.findViewById(R.id.game_plus_btn);
mTotalTxt = itemView.findViewById(R.id.game_total_txt);
mNumberTotal = itemView.findViewById(R.id.game_total_number);
mImageMinus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (listener != null) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
listener.onMinusClick(position);
}
}
}
});
mImagePlus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (listener != null) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
listener.onPlusClick(position);
}
}
}
});
}
}
In Activity:
mAdapter.setOnItemClickListener(new GameAdapter.OnItemClickListener() {
int totalCounter = 0;
#Override
public void onMinusClick(int position) {
String parNum = mGameItemList.get(position).getText2();
int intParNm = Integer.valueOf(parNum);
if (intParNm != 1) {
intParNm -= 1;
totalCounter -= 1;
mGameItemList.get(position).changeText2(Integer.toString(intParNm));
mGameItemList.get(position).changeText4(Integer.toString(totalCounter));
mAdapter.notifyDataSetChanged();
}
}
#Override
public void onPlusClick(int position) {
String parNum = mGameItemList.get(position).getText2();
int intParNm = Integer.valueOf(parNum);
if (intParNm != 99) {
intParNm += 1;
totalCounter += 1;
mGameItemList.get(position).changeText2(Integer.toString(intParNm));
mGameItemList.get(position).changeText4(Integer.toString(totalCounter));
mAdapter.notifyDataSetChanged();
}
}
});
So this is what I have now, but atm every item uses that same itemCounter, so if I click player 1 plus button, its TTL is 1 (as it should) but after that when I click player 2 plus button, player 2 TTL should be also 1 but is in fact 2, cause that 1 extra came from that player 1 plus click.
How I can change this to work for both individually?
On thing is you can call notifyItemChanged(position), on the position of the row that has changed, it will only update that item in the recycler view.
Instead of holding your count in totalCounter, you could get the value from the viewholder that is already assigned, so something like String totalCounter = ((TextView) recyclerView.findViewHolderForAdapterPosition(position).itemView.findViewById(R.id.view)).getText().toString();
#Override
public void onMinusClick(int position) {
String parNum = mGameItemList.get(position).getText2();
int intParNm = Integer.valueOf(parNum);
if (intParNm != 1) {
intParNm -= 1;
totalCounter -= 1;
mGameItemList.get(position).changeText2(Integer.toString(intParNm));
mGameItemList.get(position).changeText4(Integer.toString(totalCounter));
adapter.notifyItemChanged(position);
}
}

How to recognize the last page of viewpager and automatically redirect to first page when swiped

My title is my problem, I have 3 pages in a viewPager. How do I redirect to first page when I swipe on the last page in viewPager?
ViewPager.OnPageChangeListener viewPagerPageChangeListener = new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
int x = position;
Log.i("ON Possition",Integer.Tostring(x));
if(x == 3){
viewPager.setCurrentItem(x);
Log.i("ON last Possition",Integer.Tostring());
}
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
};
you can't move to first page smoothly from last page.
read below code. we should fake item count.
private class MyPagerAdapter extends PagerAdapter {
Context context;
public MyPagerAdapter(Context context) {
this.context= context;
}
#Override
public int getCount() {
if (tList != null && tList.size() > 1) {
return tList.size() * MAX_PAGE; // simulate infinite by big number of products
} else {
return tList.size();
}
}
#Override
public int getItemPosition(Object object) {
int position;
if (tList != null && tList.size() > 1) {
position = super.getItemPosition(object) % tList.size(); // use modulo for infinite cycling
return position;
} else {
return super.getItemPosition(object);
}
}
public int getItemPosition(int index) {
int position;
if (tList!= null && tList.size() > 1) {
position = index % tList.size(); // use modulo for infinite cycling
return position;
} else {
return index;
}
}
}

Android ArrayList - Adding items one by one results in IndexOutOfBoundsException

I use following function to animate filtering a list (actually I once found that somewhere, don't know where anymore):
public void animateTo(List<T> items) {
applyAndAnimateRemovals(items);
applyAndAnimateAdditions(items);
applyAndAnimateMovedItems(items);
}
private void applyAndAnimateRemovals(List<T> newItems) {
for (int i = mListItems.size() - 1; i >= 0; i--) {
final T item = mListItems.get(i);
if (!newItems.contains(item)) {
removeItem(i);
}
}
}
private void applyAndAnimateAdditions(List<T> newItems) {
for (int i = 0, count = newItems.size(); i < count; i++) {
final T item = newItems.get(i);
if (!mListItems.contains(item)) {
addItem(i, item);
}
}
}
private void applyAndAnimateMovedItems(List<T> newItems) {
for (int toPosition = newItems.size() - 1; toPosition >= 0; toPosition--) {
final T item = newItems.get(toPosition);
final int fromPosition = mListItems.indexOf(item);
if (fromPosition >= 0 && fromPosition != toPosition) {
moveItem(fromPosition, toPosition);
}
}
}
public T removeItem(int position) {
final T item = mListItems.remove(position);
notifyItemRemoved(position);
return item;
}
public void addItem(int position, T item) {
mListItems.add(position, item); // <- EXCEPTION IS THROWN HERE
notifyItemInserted(position);
}
public void moveItem(int fromPosition, int toPosition) {
final T model = mListItems.remove(fromPosition);
mListItems.add(toPosition, model);
notifyItemMoved(fromPosition, toPosition);
}
Sometimes I get an exception like following in the addItem function:
java.lang.IndexOutOfBoundsException: Invalid index 203, size is 201
Actually, how can this happen? The loop in applyAndAnimateAdditions begins at 0, adds items one by one if they are not already in the list. How can the index exception occur?
You can add this to check if the position is equal or larger(>=) to the ArrayLists size just add the item to the end of the Arraylist.
public void addItem(int position, T item) {
if(position >= mListItems.size())
mListItems.add(mListItems.size()-1, item);
else
mListItems.add(position, item);
notifyItemInserted(position);
}
So here's your problem you are iterating through newItems but you are adding to mListItems

Categories

Resources