android reflash position after remove arrayList item - android

i have recyclerview with adapter.
#Override
public void onBindViewHolder(#NonNull CustomViewHolder holder, int position) {
int positions = position;
Bitmap bitmap = BitmapFactory.decodeByteArray(arrayList.get(position).getImg(), 0, arrayList.get(position).getImg().length);
holder.button.setImageBitmap(bitmap);
holder.seekBar.setProgress(arrayList.get(position).getSeek());
holder.btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int index = arrayList.indexOf(arrayList.get(positions));
arrayList.remove(arrayList.get(positions);
adapter.notifyItemRemoved(index);
}
});
}
if i have 2 item in arrayList
first, i remove item position 0.
and second, if i remove item position 0 again, i have error
android IndexOutOfBoundsException: Index: 1, Size: 1
i don't know why..
deleted one item of the two items.
And the position of one remaining item should be 0.
but why show error?
After deleting one item, I checked that the size of the arrayList was 1.
what i have to do?

Related

Problem with spinner in Recyclerview android

Recyclerview item contains spinner and textviews. All item's spinners are correctly set. Whenever ith item spinner changes, ith item textviews values will also be changed.
The problem here is, one item spinner click action changes other items spinner values.
Adapter class:
public void onBindViewHolder(#NonNull final MyViewHolder holder, final int position) {
//do not re-initialize spinner when behaviour of item is changing
if (holder.spinnerWeightUnit.getAdapter() == null) {
List<String> unit_list = new ArrayList<>();
for (Productsku x : productskuList) {
unit_list.add(x.getWeight() + x.getUnit());
}
initializeUnitSpinner(holder.spinnerWeightUnit, unit_list);
holder.spinnerWeightUnit.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
Timber.d("adapter item changed");
setPriceStockValues(holder, position, i);
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {}
});
}
}
private void initializeUnitSpinner(Spinner spinnerUnit, List<String> unit_list) {
ArrayAdapter<String> unitArrayAdapter = new ArrayAdapter<>(mContext, android.R.layout.simple_spinner_item, unit_list);
unitArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinnerUnit.setAdapter(unitArrayAdapter);
}
private void setPriceStockValues(MyViewHolder holder, int position, int unit_position) {
//set textviews
}
scenario:
0th item spinner value selected -> all spinners are correct
1st item spinner value selected -> all spinners are correct
again 0th item spinner value selected -> all spinners are set to 1st item's spinner values
again 1st item spinner values selected -> all spinner are set to 0th item's spinner values
..and so on.
I am unable to find out the solution for this. Please any one can help?
Thank you.
try with removing this line
if (holder.spinnerWeightUnit.getAdapter() == null)
set up your adapter every time when onBindViewHolder get called. RecyclerView uses recycling pattern - reusing previously used/shown Views/ViewHolders. above if prevents to re-set paticluar item on proper position, keeps old OnItemSelected listener and adapter

Load the first item after the first item in recyclerview

I need to implement the recyclerview with functionality as to display the first item after last like circle
Suggest you to try following changes in your adapter class. Set getItemCount to return a very large value like Integer.MAX_VALUE
#Override
public int getItemCount() {
return Integer.MAX_VALUE;
}
Then to get the actual item position need a modulus operation
#Override
public void onBindViewHolder(ActionItemViewHolder holder, int position) {
position = position % SIZE_OF_LIST;
};

How to update the view of a particular position of a recycler view which is not currently in focus on the screen?

I am actually making some visibility changes to items that are clicked of the recycler view. But when the user clicks on one object and then clicks on the other object then the previous object should come to its initial state.
The manager.findViewByPosition(position) is working fine if the view is in focus of the screen but I am not able to get the view if the element is not in current focus.
For example:- the user clicks on 1st(position) item then clicks on the last position then the findViewByPosition returns a null.
Please help and let me know if there is some other way of doing it.
The expected result should be the view of the last item to be refreshed but it's not happening for the views that are not in the current focus of the screen.
Below is my code snippet. Updated with what you suggested.
public class BodyPartWithMmtRecyclerView extends
RecyclerView.Adapter<BodyPartWithMmtRecyclerView.ViewHolder>
{
//variables defined.
int selectedPosition = -1;
static class ViewHolder extends RecyclerView.ViewHolder {
//All the view items declared here.
ViewHolder(View view) {
super(view);
//All the views are defined here.
}
}
public BodyPartWithMmtRecyclerView(List<BodyPartWithMmtSelectionModel> bodyPartsList, Context context){
//array list initialization and shared preference variables initialization
}
public BodyPartWithMmtRecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
//Creating a new view.
}
public void onBindViewHolder(#NonNull final BodyPartWithMmtRecyclerView.ViewHolder holder, #SuppressLint("RecyclerView") final int position) {
BodyPartWithMmtSelectionModel bodyPartWithMmtSelectionModel = bodyPartsList.get(position);
holder.iv_bodypart.setImageResource(bodyPartWithMmtSelectionModel.getIv_body_part());
holder.tv_body_part_name.setText(bodyPartWithMmtSelectionModel.getExercise_name());
if(selectedPosition!=position && selectedPosition!=-1){
//updated the elements view to default view. Like made the visibility and other changes here.
}
//some click listeners on the sub-elements of the items. Like textviews, spinner, etc
holder.iv_bodypart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((BodyPartSelection)context).setFabVisible();
if(selectedPosition!=-1){
((BodyPartSelection)context).visibilityChanged(selectedPosition,position);
/*here what I was doing is whenever the user clicks on an item I check weather a previous item is clicked or not then if yes then I send the position to a function that makes it to default but the issue was that if the item is not in the focus of the screen the findViewByPosition returns null.*/
}
selectedPosition = position;
bodypartSelected = holder.tv_body_part_name.getText().toString();
holder.iv_bodypart.setVisibility(View.INVISIBLE);
holder.rl_left_right.setVisibility(View.VISIBLE);
}
});
//and other listeners below
}
#Override
public int getItemCount() {
return bodyPartsList==null?0:bodyPartsList.size();
}
#Override
public int getItemViewType(int position) {
return position;
}
}
VisibilityChanged function
public void visibilityChanged(int position, int clicked){
View view = manager.findViewByPosition(position);
if(view!=null) {
Log.i("inside","visibility change");
ImageView imageView = view.findViewById(R.id.bodypartImage);
//other elements and changing the visibility of elemets to default.
}
}
I have updated my code based on the snippet you updated. Please don't change the visibility condition if-else I have added with any different logic which I saw in your code snippet. As you did, it will not update both selected and default view as RecyclerView reuse the view layout. So if the condition is not proper, you may see multiple items as selected or some other types of unwated behaviour.
public void onBindViewHolder(#NonNull final BodyPartWithMmtRecyclerView.ViewHolder holder, #SuppressLint("RecyclerView") final int position) {
BodyPartWithMmtSelectionModel bodyPartWithMmtSelectionModel = bodyPartsList.get(position);
holder.iv_bodypart.setImageResource(bodyPartWithMmtSelectionModel.getIv_body_part());
holder.tv_body_part_name.setText(bodyPartWithMmtSelectionModel.getExercise_name());
if(selectedPosition == position){
//updated the elements view to SELECTED VIEW. Like made the visibility and other changes here.
} else {
//updated the elements view to default view. Like made the visibility and other changes here.
}
//some click listeners on the sub-elements of the items. Like textviews, spinner, etc
holder.iv_bodypart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((BodyPartSelection)context).setFabVisible();
/Comment by Hari: Don't try to change the visibility of default as it will be done automatically after calling notifyDataSetChanged(). */
if(selectedPosition!=-1){
((BodyPartSelection)context).visibilityChanged(selectedPosition,position);
/*here what I was doing is whenever the user clicks on an item I check weather a previous item is clicked or not then if yes then I send the position to a function that makes it to default but the issue was that if the item is not in the focus of the screen the findViewByPosition returns null.*/
/*Comment by Hari: This snippet is valuable which is missing as you are getting null issue here.
However Don't try to change the visibility of default as it will be done automatically after calling notifyDataSetChanged(). */
}
selectedPosition = position;
bodypartSelected = holder.tv_body_part_name.getText().toString();
holder.iv_bodypart.setVisibility(View.INVISIBLE);
holder.rl_left_right.setVisibility(View.VISIBLE);
//Keep this as last statement in onClick
notifyDataSetChanged();
}
});
//and other listeners below
}
Let me know your further response.
Based on #Hari N Jha's Answer.
Call notifyDataSetChanged() when you update anything. E.g
int selectedPosition = -1;
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
//....
if(position == selectedPosition) {
//Add background color change of your layout or as you want for selected item.
} else {
//Add background color change of your layout or as you want for default item.
}
notifyDataSetChanged(); //Call notifyDataSetChanged() here after done all the stufs
//...
}

Apply cascade animation RecyclerView Items in Android

I want to do the next animation:
When I click on an item of my RecyclerView the items before disappear with a cascade animation.
For example: I Click on my item placed at position 10, then the items from 0 to 9 have to disappear applying a cascade swipe-left animation.
How can do it?
At this moment I tried to apply like this:
ViewHolder methods:
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
swipeAnimation(position);
}
});
lstViewsHolder.add(holder.itemView);
setAnimation(holder.itemView, position);
}
private void swipeAnimation(int position){
int init = 0;
while(init < position){
lstViewsHolder.get(init).startAnimation(AnimationUtils.loadAnimation(context, R.anim.swipe_left));
init++;
}
}
The correct way of doing this is using a custom ItemAnimator. You should try to extend either SimpleItemAnimator or DefaultItemAnimator to apply your animation.
The ItemAnimators respond to data events from the adapter, so you should include logic in your adapter that calls notifyItemRemoved()or notifyItemRangeRemoved() so that the proper animation is triggered.
I think you should iterate over you're array and each time remove that item and then call notifyItemRemoved, you may want to delay each operation so that you're animation look smooth.
mResults.remove(position);
adapter.notifyItemRemoved(position);

Android Adapter "java.lang.IndexOutOfBoundsException: Invalid index 4, size is 4"

I have problem with deleting item's from ArrayList and synchronising Adapter.
I have my RecyclerView adapter with some ArrayList inside it called items. I download some list from the server and dispaly inside it. Whenever I click on some of list items I would like to delete it from server, from local ArrayList and notify the adapter about it. The problem is that when I delete everything from down to up from the list everything is ok, but when f.e. I delete 1st element from the list and then randomly some of the elements it deletes element after the one I clicked. In some cases the app crashes (f.e. I delete 1st element then the last one). The error I get is f.e.:
java.lang.IndexOutOfBoundsException: Invalid index 4, size is 4
Look like it's something with list size but i don't know what is wrong?
Here is the function where I got position from (setPopUpListener(popupMenu, position)):
// Binding New View
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
RecipeItem item = items.get(position);
// Binding Recipe Image
Picasso.with(context).load(item.getImgThumbnailLink()).into(holder.recipeItemImage);
// Binding Recipe Title
holder.recipeItemTitle.setText(item.getTitle());
// Binding Recipe Subtitle
String subtitle = "Kuchnia " + item.getKitchenType() + ", " + item.getMealType();
holder.recipeItemSubtitle.setText(subtitle);
// Binding Recipe Likes Count
holder.recipeItemLikesCount.setText(Integer.toString(item.getLikeCount()));
// Binding Recipe Add Date
holder.recipeItemAddDate.setText(item.getAddDate());
// Binding Recipe Options Icon
holder.recipeItemOptionsIcon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
PopupMenu popupMenu = new PopupMenu(context, v);
setPopUpListener(popupMenu, position); // Setting Popup Listener
inflatePopupMenu(popupMenu); // Inflating Correct Menu
popupMenu.show();
}
});
// Item Click Listener
holder.setClickListener(new RecipeItemClickListener() {
#Override
public void onClick(View view, int position) {
// taking to recipe activity
}
});
}
Here is setPopUpListener() - just look at removeFromFavourites(position):
// Setting Popup Listener
private void setPopUpListener(PopupMenu popupMenu, final int position) {
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
switch (popupType) {
// Add To Favourites Menu
case 0: {
switch (item.getItemId()) {
case R.id.item_add: {
addToFavourites(position);
return true;
}
}
}
// Remove From Favourites Menu
case 1: {
switch (item.getItemId()) {
case R.id.item_remove: {
removeFromFavourites(position);
return true;
}
}
}
}
return false;
}
});
}
Here is where the error appears (removeFromFavourites(position)):
// Removing User's Favourite
private void removeFromFavourites(int position) {
// Checking Connection Status
if (!FormValidation.isOnline(context)) {
showSnackbarInfo(context.getString(R.string.err_msg_connection_problem),
R.color.snackbar_error_msg);
} else {
SQLiteHandler db = new SQLiteHandler(context);
// Getting User Unique ID
String userUniqueId = db.getUserUniqueId();
db.close();
RecipeItem listItem = items.get(position);
// Getting Recipe Unique ID
String recipeUniqueId = listItem.getUniqueId();
// Removing From User's Favourites
removeFromUserFavouritesOnServer(recipeUniqueId, userUniqueId);
// Removing Item From Local Array List
items.remove(position);
// Notifying Adapter That Item Has Been Removed
notifyItemRemoved(position);
}
}
SOLUTION - HOPE THIS WILL HELP SOMEBODY
I have found the soution for this. If somebody will ever try to dynamicly remove elements from his array list and notifyItemRemoved(position) do not send clicked position as a parameter inside onBindViewHolder(ViewHolder holder, int position). You will meet with exactly the same situation as I did.
If you have 4 displayed elements in a list f.e. [0, 1, 2, 3] and try to remove from the end of the list everything will be fine cause clicked positions will match exactly the same positions in ArrayList. For example if you click 4th element:
position = 3 - position you will get when clicked on list element; myArray.remove(position) - will remove element with index = 3 and notifyItemRemoved(position) - will animate the list and remove deleted element from the displayed list. You are going to have following list: [0, 1, 2]. This is fine.
Situation changes when you want to delete random element. Let's say I want to delete 3rd displayed list element. I click on it to delete so i get:
position = 2 -> myArray.remove(position) -> notifyItemRemoved(position)
In this case the ArrayList I am going to get will be like this: [0, 1, 3]. In I now click on the last dispalyed element and would like to delete it that's what I will get:
position = 3->myArray.remove(position) -> notifyItemRemoved(position)
But what happens? App suddenly crashes with exception: java.lang.IndexOutOfBoundsException: Invalid index 3, size is 3. It means that we are trying to get element at the position that does not exist. But why? I got my clicked position from element... This is what happened:
At the beggining we had:
ARRAY LIST INDEXES -> [0, 1, 2, 3]
POSITIONS FROM CLICK -> [0, 1, 2, 3]
After Deleting 3rd element:
ARRAY LIST INDEXES -> [0, 1, 2]
POSITIONS FROM CLICK -> [0, 1, 3]
Now when I try to delete element at position = 3 we can't do that. We do not have that position. The max position we can get is 2. Thats why we get the exception. How to manage that problem?
In onBindViewHolder(ViewHolder holder, int position) we used position in
removeFromFavourites(position). But we also have our returned holder. If we use method called: getAdapterPosition() from class RecyclerView.ViewHolder we are at home.
getAdapterPosition
From developer site: http://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html#getAdapterPosition()
This will always return index identical to that in ArrayList. So summaring all we had to do was changing position parameter with holder.getAdapterPosition():
// Binding New View
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
RecipeItem item = items.get(position);
// Binding Recipe Image
Picasso.with(context).load(item.getImgThumbnailLink()).into(holder.recipeItemImage);
// Binding Recipe Title
holder.recipeItemTitle.setText(item.getTitle());
// Binding Recipe Subtitle
String subtitle = "Kuchnia " + item.getKitchenType() + ", " + item.getMealType();
holder.recipeItemSubtitle.setText(subtitle);
// Binding Recipe Likes Count
holder.recipeItemLikesCount.setText(Integer.toString(item.getLikeCount()));
// Binding Recipe Add Date
holder.recipeItemAddDate.setText(item.getAddDate());
// Binding Recipe Options Icon
holder.recipeItemOptionsIcon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
PopupMenu popupMenu = new PopupMenu(context, v);
setPopUpListener(popupMenu, holder.getAdapterPosition()); // Setting Popup Listener
inflatePopupMenu(popupMenu); // Inflating Correct Menu
popupMenu.show();
}
});
// Item Click Listener
holder.setClickListener(new RecipeItemClickListener() {
#Override
public void onClick(View view, int position) {
// taking to recipe activity
}
});
}
Use
notifyItemRangeChanged(position, getItemCount());
after
notifyItemRemoved(position);
You don't need to use index, just use position.
You are changing internal data structure which is not a good way
Suggestions:
In case of add/remove operation, do to the main array (e.g. ArrayList) which is passed to the adapter
Never initialize the main array (which is passed to the adapter) to null, it will lost the adapter reference
After adding/removing just call adapter.notifyDataSetChanged(); for adapter
just Use
product_list.remove(product_list.get(position));
notifyItemRemoved(position);
notifyItemRangeChanged(position, itemCount);

Categories

Resources