I have ItemTouchHelper class that uses swiping and drag and drop for performing actions. But i want to change drag and drop behavior. It should swap positions of 2 elements, the first one I dragged and the other one where it is dropped on. i want to exchange the items of the dragged & dropped positions. not to change positions of all items among both of them.
How to do it
this is my class for drag and drop
public class ItemTouchHelper extends
androidx.recyclerview.widget.ItemTouchHelper.Callback {
private Drawable icon;
private Context context;
private ColorDrawable background;
private final ItemTouchHelperListener dragDropListener;
public ItemTouchHelper(Context context, Drawable icon,
ItemTouchHelperListener dragDropListener) {
this.icon = icon;
this.dragDropListener = dragDropListener;
this.context = context;
this.background = new ColorDrawable(context.getResources().getColor(R.color.deleteItem));
}
#Override
public void onChildDraw(#NonNull Canvas c, #NonNull RecyclerView recyclerView,
#NonNull RecyclerView.ViewHolder viewHolder,
float dX, float dY,
int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX,
dY, actionState, isCurrentlyActive);
View itemView = viewHolder.itemView;
int iconMargin = (itemView.getHeight() - icon.getIntrinsicHeight()) / 2;
int iconTop = itemView.getTop() + (itemView.getHeight() - icon.getIntrinsicHeight()) / 2;
int iconBottom = iconTop + icon.getIntrinsicHeight();
if (dX > 0) {
background = new ColorDrawable(context.getResources().getColor(R.color.deleteItem));
iconMargin = (itemView.getHeight() - icon.getIntrinsicHeight()) / 2;
iconTop = itemView.getTop() + (itemView.getHeight() - icon.getIntrinsicHeight()) / 2;
iconBottom = iconTop + icon.getIntrinsicHeight();
int iconRight = itemView.getLeft() + iconMargin + icon.getIntrinsicWidth();
int iconLeft = itemView.getLeft() + iconMargin;
icon.setBounds(iconLeft, iconTop, iconRight, iconBottom);
background.setBounds(itemView.getLeft(), itemView.getTop(),
itemView.getLeft() + ((int) dX),
itemView.getBottom());
} else if (dX < 0) {
int iconRight = itemView.getRight() - iconMargin;
int iconLeft = itemView.getRight() - iconMargin - icon.getIntrinsicWidth();
icon.setBounds(iconLeft, iconTop, iconRight, iconBottom);
background.setBounds(itemView.getRight(), itemView.getTop(),
itemView.getRight() + ((int) dX),
itemView.getBottom());
} else {
background.setBounds(0, 0, 0, 0);
icon.setBounds(0, 0, 0, 0);
}
background.draw(c);
icon.draw(c);
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder,
int direction) {
dragDropListener.deleteElementDialog(viewHolder.getAdapterPosition());
}
#Override
public int getMovementFlags(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder) {
int dragFlags = UP | DOWN;
int swipeFlags = START | END;
return makeMovementFlags(dragFlags, swipeFlags);
}
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder,
#NonNull RecyclerView.ViewHolder target) {
dragDropListener.onRowMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
return true;
}
#Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder,
int actionState) {
if (actionState != ACTION_STATE_IDLE && actionState != ACTION_STATE_SWIPE) {
dragDropListener.onRowSelected(viewHolder);
}
super.onSelectedChanged(viewHolder, actionState);
}
#Override
public void clearView(#NonNull RecyclerView recyclerView,
#NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, Objects.requireNonNull(viewHolder));
dragDropListener.onRowClear(Objects.requireNonNull(viewHolder));
}
#Override
public boolean isLongPressDragEnabled() {
return true;
}
}
and this is my ItemTouchHelperListener:
public void setItemTouchHelperListener() {
ItemTouchHelperListener itemTouchHelperListener = new
ItemTouchHelperListener() {
#Override
public void onRowMoved(int fromPosition, int toPosition) {
presenter.rowMoved(fromPosition, toPosition);
}
#Override
public void onRowSelected(RecyclerView.ViewHolder myViewHolder) {
if (myViewHolder instanceof ElementsAdapter.ElementsViewHolder) {
elementsAdapter.rowSelected((ElementsAdapter.ElementsViewHolder) myViewHolder);
presenter.rowSelected(myViewHolder.getAdapterPosition());
}
}
#Override
public void onRowClear(RecyclerView.ViewHolder myViewHolder) {
if (myViewHolder instanceof ElementsAdapter.ElementsViewHolder) {
elementsAdapter.rowClear((ElementsAdapter.ElementsViewHolder) myViewHolder);
presenter.rowClear(myViewHolder.getAdapterPosition());
}
}
#Override
public void deleteElementDialog(int adapterPosition) {
createDeleteDialog(adapterPosition);
}
};
You can achieve this by registering both the dragged item using onMove() method, and the dropped item using clearView() method; then modify the data source of your RecyclerView adapter; so you can use a temp item that stores the dragged item; then set the dropped-by item with the dragged one; and finally put the temp item on the dropped one.
Then utilize RecyclerView adapter notifyItemChanged() for both items to update the layout with this change
Note: here I disabled the swiping as your question mainly on the drag & drop
final int[] oldPos = new int[1];
final int[] newPos = new int[1];
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP |
ItemTouchHelper.DOWN |
ItemTouchHelper.LEFT |
ItemTouchHelper.RIGHT,
0) {
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
oldPos[0] = viewHolder.getAdapterPosition();
newPos[0] = target.getAdapterPosition();
return false;
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
}
#Override
public void clearView(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
moveItem(oldPos[0], newPos[0]);
}
});
private void moveItem(int oldPos, int newPos) {
Item temp = mItems.get(oldPos);
mItems.set(oldPos, mItems.get(newPos));
mItems.set(newPos, temp);
mAdapter.notifyItemChanged(oldPos);
mAdapter.notifyItemChanged(newPos);
}
The result
I totally agree with #Zain answer but there is one problem i.e suppose you want to replace the 1st item with 3rd then you will click and hold the 3rd item and drag it over the 1st item. Once you dropped, you will notice the 3rd item will get again shifted to its original position and after that, both items will get updated properly. It doesn't look good.
I've just slightly modified the #Zain answer.
private int fromPos = -1;
private int toPos = -1;
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new
ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP | ItemTouchHelper.DOWN |
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT, 0) {
#Override
public boolean onMove(#NonNull RecyclerView recyclerView,
#NonNull RecyclerView.ViewHolder viewHolder,
#NonNull RecyclerView.ViewHolder target) {
toPos = target.getAdapterPosition();
return false;
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder,
int direction) {}
#Override
public void onSelectedChange(#NonNull RecyclerView.ViewHolder
viewHolder, int actionState) {
switch(actionState){
case ItemTouchHelper.ACTION_STATE_DRAG:{
fromPos = viewHolder.getAdapterPosition();
break;
}
case ItemTouchHelper.ACTION_STATE_IDLE: {
//Execute when the user dropped the item after dragging.
if(fromPos != -1 && toPos != -1
&& fromPos != toPos) {
moveItem(fromPos, toPos);
fromPos = -1;
toPos = -1;
}
break;
}
}
private void moveItem(int oldPos, int newPos) {
Item temp = mItems.get(oldPos);
mItems.set(oldPos, mItems.get(newPos));
mItems.set(newPos, temp);
mAdapter.notifyItemChanged(oldPos);
mAdapter.notifyItemChanged(newPos);
}
Related
UPDATE When I Log.i("adapterPos", String.valueOf(adapter.adapterPos)); in onSwiped, it returns -1 even before setting it to -1...
I have a recyclerView, as well as a custom adapter to display information about an exercise.
When a user taps on a recyclerView item, the items background is set to green, and the save button turns into an update button, so that the user can update their existing recorded exercises.
I also have an onSwiped method which is implemented inside my fragment. When a user swipes an item, it is deleted from the database.
When a user swipes to delete an item, I would like to set the background for ALL items back to the default (grey).
How could this be done?
Note: if item 2 is selected and the user decides to swipe item 1, even after item 1 is deleted, item 2 is still currently selected.
Record Exercise Fragment (Relevant code)
#Override
public void onExerciseClicked(int position) {
if (recyclerItemClicked == false) {
saveBtn.setText("Update");
clearBtn.setVisibility(View.GONE);
recyclerItemClicked = true;
double selectedWeight = adapter.getWeight(position);
String selectedWeightString = Double.toString(selectedWeight);
editTextWeight.setText(selectedWeightString);
int selectedReps = adapter.getReps(position);
String selectedRepsString = Integer.toString(selectedReps);
editTextReps.setText(selectedRepsString);
} else {
clearBtn.setVisibility(View.VISIBLE);
saveBtn.setText("Save");
recyclerItemClicked = false;
}
}
public void initRecyclerView() {
adapter = new CompletedExercisesListAdapter2(allExercises, this);
recyclerView.setAdapter(adapter);
new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerView);
}
public void setExercises(List<Log_Entries> exercises) {
allExercises.clear();
allExercises.addAll(exercises);
}
ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT) {
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int logID = allExercises.get(viewHolder.getAdapterPosition()).getLog_id();
logViewModel.deleteByID(logID);
clearBtn.setVisibility(View.VISIBLE);
saveBtn.setText("Save");
Log.i("adapterPos", String.valueOf(adapter.adapterPos)); // this is always -1 for some reason
// backup the currently selected position
int selectedPos = adapter.adapterPos;
// change the currently selected position to -1
adapter.adapterPos = -1;
adapter.notifyItemChanged(selectedPos);
}
};
Adapter
public class CompletedExercisesListAdapter2 extends RecyclerView.Adapter {
private OnExerciseClickListener onExerciseClickListener;
private List<Log_Entries> allCompletedExercises = new ArrayList<>();
public int adapterPos = -1;
public boolean isSelected = false;
public CompletedExercisesListAdapter2(ArrayList<Log_Entries> allCompletedExercises, OnExerciseClickListener onExerciseClickListener) {
this.allCompletedExercises = allCompletedExercises;
this.onExerciseClickListener = onExerciseClickListener;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view;
if (viewType == 0) {
view = layoutInflater.inflate(R.layout.new_completed_exercise_item, parent, false);
return new ViewHolderOne(view, onExerciseClickListener);
}
view = layoutInflater.inflate(R.layout.completed_exercise_item, parent, false);
return new ViewHolderTwo(view, onExerciseClickListener);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
if (getItemViewType(position) == 0) {
ViewHolderOne viewHolderOne = (ViewHolderOne) holder;
if (adapterPos == position) {
viewHolderOne.relativeLayout.setBackgroundColor(Color.parseColor("#567845"));
}
else {
viewHolderOne.relativeLayout.setBackgroundResource(R.color.dark_grey);
}
viewHolderOne.textViewExerciseName.setText(String.valueOf(allCompletedExercises.get(position).getChildExerciseName()));
viewHolderOne.textViewSetNumber.setText(String.valueOf(viewHolderOne.getAdapterPosition() + 1));
viewHolderOne.textViewWeight.setText(String.valueOf(allCompletedExercises.get(position).getTotal_weight_lifted()));
viewHolderOne.textViewReps.setText(String.valueOf(allCompletedExercises.get(position).getReps()));
} else if (getItemViewType(position) == 1) {
ViewHolderTwo viewHolderTwo = (ViewHolderTwo) holder;
if (adapterPos == position) {
viewHolderTwo.relativeLayout.setBackgroundColor(Color.parseColor("#567845"));
} else {
viewHolderTwo.relativeLayout.setBackgroundResource(R.color.dark_grey);
}
viewHolderTwo.textViewSetNumber.setText(String.valueOf(viewHolderTwo.getAdapterPosition() + 1));
viewHolderTwo.textViewWeight.setText(String.valueOf(allCompletedExercises.get(position).getTotal_weight_lifted()));
viewHolderTwo.textViewReps.setText(String.valueOf(allCompletedExercises.get(position).getReps()));
}
}
#Override
public int getItemCount() {
return allCompletedExercises.size();
}
#Override
public int getItemViewType(int position) {
// if list is sorted chronologically
if (position == 0) {
return 0;
}
if (allCompletedExercises.get(position).getChildExerciseName().equals(allCompletedExercises.get(position - 1).getChildExerciseName())) {
return 1;
} else {
return 0;
}
}
public class ViewHolderOne extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView textViewExerciseName;
private TextView textViewSetNumber;
private TextView textViewWeight;
private TextView textViewReps;
OnExerciseClickListener mOnExerciseClickListener;
private RelativeLayout relativeLayout;
public ViewHolderOne(#NonNull View itemView, OnExerciseClickListener onExerciseClickListener) {
super(itemView);
textViewExerciseName = itemView.findViewById(R.id.textView_ExerciseName3);
textViewSetNumber = itemView.findViewById(R.id.textView_Set_Number56);
textViewWeight = itemView.findViewById(R.id.textView_weight78);
textViewReps = itemView.findViewById(R.id.textView_repss0);
mOnExerciseClickListener = onExerciseClickListener;
relativeLayout = (RelativeLayout) itemView.findViewById(R.id.exercise_item_relative);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
onExerciseClickListener.onExerciseClicked(getAdapterPosition());
if (isSelected) {
adapterPos = -1;
isSelected = false;
} else {
adapterPos = getAdapterPosition();
isSelected = true;
}
notifyDataSetChanged();
}
}
class ViewHolderTwo extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView textViewSetNumber;
private TextView textViewWeight;
private TextView textViewReps;
OnExerciseClickListener mOnExerciseClickListener;
private RelativeLayout relativeLayout;
public ViewHolderTwo(#NonNull View itemView, OnExerciseClickListener onExerciseClickListener) {
super(itemView);
textViewSetNumber = itemView.findViewById(R.id.textView_Set_Number);
textViewWeight = itemView.findViewById(R.id.textView_weight);
textViewReps = itemView.findViewById(R.id.textView_repss);
relativeLayout = (RelativeLayout) itemView.findViewById(R.id.exercise_item_rel);
mOnExerciseClickListener = onExerciseClickListener;
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
onExerciseClickListener.onExerciseClicked(getAdapterPosition());
if (!isSelected) {
adapterPos = getAdapterPosition();
isSelected = true;
} else {
adapterPos = -1;
isSelected = false;
}
notifyDataSetChanged();
}
}
public interface OnExerciseClickListener {
void onExerciseClicked(int position);
}
public double getWeight(int position) {
double weight = allCompletedExercises.get(position).getTotal_weight_lifted();
return weight;
}
public int getReps(int position) {
int reps = allCompletedExercises.get(position).getReps();
return reps;
}
}
In onSwipe(), just reset the adapterPos of your adapter object to -1 and call notifyItemChanged() on that position.
// backup the currently selected position
int selectedPos = adapter.adapterPos;
// change the currently selected position to -1
adapter.adapterPos = -1;
adapter.isSelected = false;
adapter.notifyItemChanged(selectedPos);
Note: onSwipe() is called after the ViewHolder being swiped fully.
I think the only thing you need to do for this to work is to make sure that notifyDataSetChanged() is executed once after you set adapterpos= -1.
notifyDataSetChanged() will reexecute onBindViewHolder for the entire adapter as opposed to notifyItemChanged() which should result in all items evaluating to a gray background.
you are actually already doing this in your viewholders onClick method in order to prevent multiple items to have a green background at once. so yust execute the method again after a swipe. so just do the following:
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int logID = allExercises.get(viewHolder.getAdapterPosition()).getLog_id();
logViewModel.deleteByID(logID);
clearBtn.setVisibility(View.VISIBLE);
saveBtn.setText("Save");//<-- why if no item is selected?
// backup the currently selected position
//int selectedPos = adapter.adapterPos;<-- why would you do this, nothing is not shown as selected
// change the currently selected position to -1
adapter.adapterPos = -1;
adapter.notifyDataSetChanged();//now everything will be gray
}
this will work for sure but it will mess with the delete animation you might be using after the swipe. following the previous answer another code that might work is the following:
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int copyOfAdapterPos=adapterPos;
adapterPos=-1;
notifyItemChanged(copyOfAdapterPos);
int logID = allExercises.get(viewHolder.getAdapterPosition()).getLog_id();
logViewModel.deleteByID(logID);
clearBtn.setVisibility(View.VISIBLE);
saveBtn.setText("Save");//<-- why if no item is selected?
adapter.notifyItemRemoved(viewHolder.getAdapterPosition());//now everything will be gray
}
i havnt tested it but if it works it will look nicer
I have a fragment with a recyclerView.
A user is allowed to delete an exercise logEntry by swiping right.
A user can also update a logEntry by clicking on the recyclerView item.
(This highlights the current selected item green, hides the clear button and converts the save button to an update button.)
To deselect the item , the user can click on the recyclerView a second time.
Unfortunately, if a user was to select an item and then decides to swipe to delete an item, the recyclerView item which takes the same position as the originally selected item is remains highlighted green.
When a user swipes to delete an item, I would like the background of all recyclerView items to be reset to grey.
How could this be done?
Relevant Fragment Code
OnExerciseClicked
#Override
public void onExerciseClicked(int position) {
if (recyclerItemClicked == false) {
saveBtn.setText("Update");
clearBtn.setVisibility(View.GONE);
recyclerItemClicked = true;
double selectedWeight = adapter.getWeight(position);
String selectedWeightString = Double.toString(selectedWeight);
editTextWeight.setText(selectedWeightString);
int selectedReps = adapter.getReps(position);
String selectedRepsString = Integer.toString(selectedReps);
editTextReps.setText(selectedRepsString);
} else {
clearBtn.setVisibility(View.VISIBLE);
saveBtn.setText("Save");
recyclerItemClicked = false;
}
}
OnSwiped
ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT) {
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int logID = allExercises.get(viewHolder.getAdapterPosition()).getLog_id();
logViewModel.deleteByID(logID);
clearBtn.setVisibility(View.VISIBLE);
saveBtn.setText("Save");
adapter.notifyDataSetChanged();
}
};
Adapter
public class CompletedExercisesListAdapter2 extends RecyclerView.Adapter {
private OnExerciseClickListener onExerciseClickListener;
private List<Log_Entries> allCompletedExercises = new ArrayList<>();
private int adapterPos = -1;
public boolean flag = false;
public CompletedExercisesListAdapter2(ArrayList<Log_Entries> allCompletedExercises, OnExerciseClickListener onExerciseClickListener) {
this.allCompletedExercises = allCompletedExercises;
this.onExerciseClickListener = onExerciseClickListener;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view;
if (viewType == 0) {
view = layoutInflater.inflate(R.layout.new_completed_exercise_item, parent, false);
return new ViewHolderOne(view, onExerciseClickListener);
}
view = layoutInflater.inflate(R.layout.completed_exercise_item, parent, false);
return new ViewHolderTwo(view, onExerciseClickListener);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
if (getItemViewType(position) == 0) {
ViewHolderOne viewHolderOne = (ViewHolderOne) holder;
if (adapterPos == position) {
viewHolderOne.relativeLayout.setBackgroundColor(Color.parseColor("#567845"));
} else {
viewHolderOne.relativeLayout.setBackgroundResource(R.color.dark_grey);
}
viewHolderOne.textViewExerciseName.setText(String.valueOf(allCompletedExercises.get(position).getChildExerciseName()));
//viewHolderOne.textViewSetNumber.setText(String.valueOf(allCompletedExercises.get(position).getSet_number()));
viewHolderOne.textViewSetNumber.setText(String.valueOf(viewHolderOne.getAdapterPosition() + 1));
viewHolderOne.textViewWeight.setText(String.valueOf(allCompletedExercises.get(position).getTotal_weight_lifted()));
viewHolderOne.textViewReps.setText(String.valueOf(allCompletedExercises.get(position).getReps()));
} else if (getItemViewType(position) == 1) {
ViewHolderTwo viewHolderTwo = (ViewHolderTwo) holder;
if (adapterPos == position) {
viewHolderTwo.relativeLayout.setBackgroundColor(Color.parseColor("#567845"));
} else {
viewHolderTwo.relativeLayout.setBackgroundResource(R.color.dark_grey);
}
// viewHolderTwo.textViewSetNumber.setText(String.valueOf(allCompletedExercises.get(position).getSet_number()));
viewHolderTwo.textViewSetNumber.setText(String.valueOf(viewHolderTwo.getAdapterPosition() + 1));
viewHolderTwo.textViewWeight.setText(String.valueOf(allCompletedExercises.get(position).getTotal_weight_lifted()));
viewHolderTwo.textViewReps.setText(String.valueOf(allCompletedExercises.get(position).getReps()));
}
}
#Override
public int getItemCount() {
return allCompletedExercises.size();
}
#Override
public int getItemViewType(int position) {
// if list is sorted chronologically
if (position == 0) {
return 0;
}
if (allCompletedExercises.get(position).getChildExerciseName().equals(allCompletedExercises.get(position - 1).getChildExerciseName())) {
return 1;
} else {
return 0;
}
}
class ViewHolderOne extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView textViewExerciseName;
private TextView textViewSetNumber;
private TextView textViewWeight;
private TextView textViewReps;
OnExerciseClickListener mOnExerciseClickListener;
private RelativeLayout relativeLayout;
public ViewHolderOne(#NonNull View itemView, OnExerciseClickListener onExerciseClickListener) {
super(itemView);
textViewExerciseName = itemView.findViewById(R.id.textView_ExerciseName3);
textViewSetNumber = itemView.findViewById(R.id.textView_Set_Number56);
textViewWeight = itemView.findViewById(R.id.textView_weight78);
textViewReps = itemView.findViewById(R.id.textView_repss0);
mOnExerciseClickListener = onExerciseClickListener;
relativeLayout = (RelativeLayout) itemView.findViewById(R.id.exercise_item_relative);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
onExerciseClickListener.onExerciseClicked(getAdapterPosition());
if (flag) {
adapterPos = -1;
flag = false;
} else {
adapterPos = getAdapterPosition();
flag = true;
}
notifyDataSetChanged();
}
}
class ViewHolderTwo extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView textViewSetNumber;
private TextView textViewWeight;
private TextView textViewReps;
OnExerciseClickListener mOnExerciseClickListener;
private RelativeLayout relativeLayout;
public ViewHolderTwo(#NonNull View itemView, OnExerciseClickListener onExerciseClickListener) {
super(itemView);
textViewSetNumber = itemView.findViewById(R.id.textView_Set_Number);
textViewWeight = itemView.findViewById(R.id.textView_weight);
textViewReps = itemView.findViewById(R.id.textView_repss);
relativeLayout = (RelativeLayout) itemView.findViewById(R.id.exercise_item_rel);
mOnExerciseClickListener = onExerciseClickListener;
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
onExerciseClickListener.onExerciseClicked(getAdapterPosition());
if (!flag) {
adapterPos = getAdapterPosition();
flag = true;
} else {
adapterPos = -1;
flag = false;
}
notifyDataSetChanged();
}
}
public interface OnExerciseClickListener {
void onExerciseClicked(int position);
}
public double getWeight(int position) {
double weight = allCompletedExercises.get(position).getTotal_weight_lifted();
return weight;
}
public int getReps(int position) {
int reps = allCompletedExercises.get(position).getReps();
return reps;
}
}
I highly recommend using a library like Groupie to abstract away the complicated state management of RecyclerView. Otherwise you'll find this imperative style quickly becomes unmaintainable. The future is declarative UI (Compose etc). This way it is much easier to keep track of Items rather than positions.
It's quite hard to follow your code (you should use descriptive names for things, like isSelected instead of flag). However I think your problem lies in your bind function not returning false here:
if (adapterPos == position)
after you swipe. Use break points to work out why
My app shows video feeds in my app using recyclerview. It seems to have started crashing now out of nowhere and I can't wrap my head around to fix this.
My adapter:
public class VideosAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements View.OnClickListener {
private ArrayList<Object> data = new ArrayList<>();
private Context context;
private int lastItemPosition = -1;
private final int VIEW_TYPE_LIST = 0;
private final int VIEW_TYPE_LOADER = 1;
private final int VIEW_TYPE_NETWORK_ERROR = 2;
private final int VIEW_TYPE_AD = 3;
private boolean isLoading = false;
private LoadMoreListener loadMoreListener;
private int visibleThreshold = 2;
private int lastVisibleItem, totalItemCount;
private VideoListener videoListener;
public VideosAdapter(Context context, RecyclerView mRecyclerView, VideoListener videoListener) {
this.context=context;
this.videoListener = videoListener;
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
LocalMessageManager.getInstance().send(R.id.recyclerview_scroll);
assert linearLayoutManager != null;
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
int firstVisibleItem = linearLayoutManager.findFirstVisibleItemPosition();
if ((firstVisibleItem + Constants.ADS.NUMBER_OF_ITEMS_BEFORE_REQUEST_AD) % Constants.ADS.LOAD_ADS_AT_POSITION == 0){
//
int pos = firstVisibleItem + Constants.ADS.NUMBER_OF_ITEMS_BEFORE_REQUEST_AD;
if(pos > lastItemPosition && data.size()>pos && data.get(pos - 1) != null) {
if (!(data.get(pos) instanceof NativeAd)) {
videoListener.requestAds(pos);
}
}
}
if (!isLoading && NetworkUtil.hasConnection(context)) {
if (totalItemCount <= (lastVisibleItem + visibleThreshold)) {
if (loadMoreListener != null) {
loadMoreListener.onLoadMore();
}
isLoading = true;
}
}
}
});
}
#Override
public int getItemViewType(int position) {
if(data.get(position) instanceof Error)return VIEW_TYPE_NETWORK_ERROR;
if(data.get(position) == null)return VIEW_TYPE_LOADER;
if(data.get(position) instanceof NativeAd)return VIEW_TYPE_AD;
return VIEW_TYPE_LIST;
}
public void setData(ArrayList<Object> objectList) {
this.data.clear();
this.data.addAll(objectList);
this.notifyDataSetChanged();
}
public void setMoreData(ArrayList<Videos> videos) {
data.addAll(videos);
this.notifyDataSetChanged();
}
public void setAd(NativeAd ad, int pos) {
if(!(pos>data.size())) {
data.add(pos, ad);
this.notifyItemInserted(pos);
}
}
#Override
public int getItemCount() {
return data != null ? data.size() : 0;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, final int position) {
lastItemPosition = position;
switch (holder.getItemViewType()) {
case VIEW_TYPE_LIST:
final VideoViewHolder viewHolder = (VideoViewHolder) holder;
viewHolder.bindTo((Videos) data.get(position));
break;
case VIEW_TYPE_LOADER:
final ViewLoader viewLoader = (ViewLoader) holder;
viewLoader.rotateLoading.start();
break;
case VIEW_TYPE_NETWORK_ERROR:
StaggeredGridLayoutManager.LayoutParams layoutParams = new StaggeredGridLayoutManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
layoutParams.setFullSpan(true);
holder.itemView.setLayoutParams(layoutParams);
ViewError viewError = (ViewError) holder;
if(SharedPrefernces.getUseNightMode()){
viewError.img.setColorFilter(App.getContext().getResources().getColor(R.color.white));
}else{
viewError.img.setColorFilter(App.getContext().getResources().getColor(R.color.black));
}
break;
case VIEW_TYPE_AD:
final AdsViewHolder adsViewHolder = (AdsViewHolder) holder;
NativeAd nativeAd = (NativeAd)data.get(position);
adsViewHolder.bind(nativeAd);
break;
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int i) {
RecyclerView.ViewHolder viewHolder = null;
LayoutInflater inflater = LayoutInflater.from(context);
switch (i) {
case VIEW_TYPE_LIST:
View va;
if(SharedPrefernces.get_feed_type()==0){
va = inflater.inflate(R.layout.large_image_video_list, parent, false);
}else{
va = inflater.inflate(R.layout.video_list, parent, false);
}
viewHolder = new VideoViewHolder(va, videoListener);
break;
case VIEW_TYPE_LOADER:
View ld = inflater.inflate(R.layout.loader, parent, false);
viewHolder = new ViewLoader(ld);
break;
case VIEW_TYPE_NETWORK_ERROR:
View ne = inflater.inflate(R.layout.no_video_stories, parent, false);
viewHolder = new ViewError(ne);
break;
case VIEW_TYPE_AD:
View ads = inflater.inflate(R.layout.ad_item_large, parent, false);
viewHolder = new AdsViewHolder(ads);
break;
}
return viewHolder;
}
#Override
public void onClick(View view) {
switch (view.getId()){
case R.id.pin:
break;
case R.id.share:
break;
}
}
public class ViewLoader extends RecyclerView.ViewHolder {
private RotateLoading rotateLoading;
ViewLoader(View view) {
super(view);
rotateLoading = (RotateLoading) view.findViewById(R.id.rotateloading);
}
}
public class ViewError extends RecyclerView.ViewHolder {
private ImageView img;
ViewError(View view) {
super(view);
img = view.findViewById(R.id.img);
}
}
public void setLoaded(){
data.remove(data.size()-1);
this.notifyItemRemoved(data.size()-1);
isLoading = false;
}
public void setLoadMoreListener(LoadMoreListener loadMoreListener) {
this.loadMoreListener = loadMoreListener;
}
public void setLoader(){
data.add(null);
this.notifyItemInserted(data.size()-1);
}
}
Code from the fragment which sets this adapter and also throws the error:
recyclerView = (RecyclerView) layout.findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 1));
adapter = new VideosAdapter(getActivity(),recyclerView,this);
adapter.setLoadMoreListener(() -> recyclerView.post(() -> {
if(data.size()> 0 && data.get(1) instanceof Videos){
adapter.setLoader();
loadMoreFeeds();
}
}));
recyclerView.setAdapter(adapter);
Error Log:
java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
at java.util.ArrayList.get(ArrayList.java:437)
I tried to few fixes from other SOF answers but couldn't understand what went wrong with my code considering it was running fine and it suddenly seems to have started crashing. I removed a header in the adapter if that matters.
Your problem is in this piece of code:
adapter.setLoadMoreListener(() -> recyclerView.post(() -> {
if(data.size()> 0 && data.get(1) instanceof Videos){
adapter.setLoader();
loadMoreFeeds();
}
}));
you are checking if the length of your array is bigger than 0, but it means that there could also be only one element. So, therefore, you should change this line to:
if(data.size() > 0 && data.get(0) instanceof Videos)
or to:
if(data.size() > 1 && data.get(1) instanceof Videos)
Remember that the count of elements in the array starts at 0.
Hope it helps!
After implementing a custom animation for these CardViews, the first two are not behaving in the way they are supposed to. Item A and Item B won't expand upon the 2nd click, but yet Item C works perfectly fine.
public class MyRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private Context mContext;
RecyclerViewHeader header;
List<MyRecyclerViewItem> listItems;
ValueAnimator mAnimator;
public MyRecyclerAdapter(Context context, RecyclerViewHeader header, List<MyRecyclerViewItem> listItems)
{
this.mContext = context;
this.header = header;
this.listItems = listItems;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType == TYPE_HEADER)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_header, parent, false);
return new MyRecyclerAdapter.VHHeader(v);
}
else if(viewType == TYPE_ITEM)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item, parent, false);
return new MyRecyclerAdapter.VHItem(v);
}
throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
}
private MyRecyclerViewItem getItem(int position)
{
return listItems.get(position);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final Typeface iconFont = FontManager.getTypeface(mContext, FontManager.FONTAWESOME);
if (holder instanceof VHHeader)
{
final VHHeader vhHeader = (VHHeader)holder;
}
else if (holder instanceof VHItem)
{
MyRecyclerViewItem currentItem = getItem(position-1);
final VHItem vhItem = (VHItem)holder;
vhItem.txtA.setText(currentItem.getContinent());
vhItem.txtB.setText(currentItem.getCountry());
vhItem.txtB.setVisibility(View.GONE);
vhItem.txtExpandCollapse.setText(R.string.fa_icon_chevron_down);
vhItem.txtExpandCollapse.setTypeface(iconFont);
//Add onPreDrawListener
vhItem.txtB.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
vhItem.txtB.getViewTreeObserver().removeOnPreDrawListener(this);
vhItem.txtB.setVisibility(View.GONE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
vhItem.txtB.measure(widthSpec, heightSpec);
mAnimator = vhItem.slideAnimator(0, vhItem.txtB.getMeasuredHeight());
return true;
}
});
vhItem.cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(vhItem.txtB.getVisibility() == View.GONE){
vhItem.expand();
} else {
vhItem.collapse();
}
}
});
}
}
#Override
public int getItemViewType(int position) {
if(isPositionHeader(position))
return TYPE_HEADER;
return TYPE_ITEM;
}
private boolean isPositionHeader(int position)
{
return position == 0;
}
// increasing getItemcount to 1. This will be the row of header.
#Override
public int getItemCount() {
return listItems.size()+1;
}
class VHHeader extends RecyclerView.ViewHolder{
Button btnCollapseAll, btnExpandAll;
public VHHeader(View headerView) {
super(headerView);
this.btnCollapseAll = headerView.findViewById(R.id.btn_collapseall);
this.btnExpandAll = headerView.findViewById(R.id.btn_expandall);
}
}
public class VHItem extends RecyclerView.ViewHolder{
CardView cardView;
RelativeLayout mRelativeLayout;
TextView txtExpandCollapse, txtA, txtB;
public VHItem(View itemView) {
super(itemView);
this.cardView = itemView.findViewById(R.id.cv);
this.mRelativeLayout = itemView.findViewById(R.id.tv_rv_relativelayout);
this.txtExpandCollapse = itemView.findViewById(R.id.tv_rv_expandcollapse);
this.txtA = itemView.findViewById(R.id.tv_rv_A);
this.txtB = itemView.findViewById(R.id.tv_rv_B);
}
private void expand() {
// set Visible
txtB.setVisibility(View.VISIBLE);
// change direction of chevron to 'up'
txtExpandCollapse.setText(R.string.fa_icon_chevron_up);
mAnimator.start();
}
private void collapse() {
// change direction of chevron to 'down'
txtExpandCollapse.setText(R.string.fa_icon_chevron_down);
int finalHeight = txtB.getHeight();
ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
mAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationEnd(Animator animator) {
//Height=0, but it set visibility to GONE
txtB.setVisibility(View.GONE);
}
#Override
public void onAnimationStart(Animator animator) {
}
#Override
public void onAnimationCancel(Animator animator) {
}
#Override
public void onAnimationRepeat(Animator animator) {
}
});
mAnimator.start();
}
public ValueAnimator slideAnimator(int start, int end) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//Update Height
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = txtB.getLayoutParams();
layoutParams.height = value;
txtB.setLayoutParams(layoutParams);
}
});
return animator;
}
}
}
You need to re-initialize mAnimator object.
Try below,
Defind one more member variable in VHItem class to keep the textB height
public class VHItem extends RecyclerView.ViewHolder{
CardView cardView;
RelativeLayout mRelativeLayout;
TextView txtExpandCollapse, txtA, txtB;
int textBHeight; // new variable
}
Then initialize it from onPreDraw method
public boolean onPreDraw() {
vhItem.txtB.getViewTreeObserver().removeOnPreDrawListener(this);
vhItem.txtB.setVisibility(View.GONE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
vhItem.txtB.measure(widthSpec, heightSpec);
vhItem.textBHeight = vhItem.txtB.getMeasuredHeight();
return true;
}
Then initialize the animator before starting
private void expand() {
txtB.setVisibility(View.VISIBLE);
txtExpandCollapse.setText(R.string.fa_icon_chevron_up);
mAnimator = slideAnimator(0,textBHeight);
mAnimator.start();
}
Change the object CardView for View.
Instead of generating the event on the cardview try to do the following on the inner class VHItem:
View view;
RelativeLayout mRelativeLayout;
TextView txtExpandCollapse, txtA, txtB;
public VHItem(View itemView) {
super(itemView);
this.view = itemView;
this.mRelativeLayout = itemView.findViewById(R.id.tv_rv_relativelayout);
this.txtExpandCollapse = itemView.findViewById(R.id.tv_rv_expandcollapse);
this.txtA = itemView.findViewById(R.id.tv_rv_A);
this.txtB = itemView.findViewById(R.id.tv_rv_B);
}
now, on the method onBindViewHolder:
vhItem.view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(vhItem.txtB.getVisibility() == View.GONE){
vhItem.expand();
} else {
vhItem.collapse();
}
}
});
In implementing swipe-to-delete functionality I accidentally made my forground view of the viewholder transparent. In doing so I've uncovered a bug with my ItemTouchHandler not changing the background or hiding the text when it is supposed to.
It appears to me that my ItemTouchHelper is not being applied to every view in my ReycleViewAdapter and/or the views are not being updated or they all share a single parent of some sort, after many hours of attempted debugging I'm at my wit's end, I've included a handy gif of the issue.
The ItemTouchHelper's onDrawChild() and onDrawChildOver() methods are passed to my fragment in order to handle the actions
Code:
AllLists_fragment.java:
public class showAllLists_fragment extends android.support.v4.app.Fragment implements RecycleItemTouchHelper.RecyclerItemTouchHelperListener {
private static final String TAG = "showAllLists";
private RecyclerView mAllItemsView = null;
private DataCommunication mData = null;
private ItemTouchHelper.SimpleCallback itemTouchHelper = null;;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.show_all_lists_frag, container, false);
this.mAllItemsView = (RecyclerView) view.findViewById(R.id.allItems);
this.itemTouchHelper = new RecycleItemTouchHelper(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT, this);
new ItemTouchHelper(this.itemTouchHelper).attachToRecyclerView(this.mAllItemsView);
this.mAllItemsView.setAdapter(this.mData.getAdapter());
this.mAllItemsView.setLayoutManager(new LinearLayoutManager(getActivity()){
//
#Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e) {
Log.d(TAG, "Inconsistent Exception Caught -See stack trace");
e.printStackTrace();
}
}
});
viewShadow();
return view;
}
#TargetApi(21)
public void viewShadow(){
this.mAllItemsView.setTranslationZ(100);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
this.mData = (DataCommunication) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement DataCommunication");
}
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser){
this.mData.getAdapter().notifyDataSetChanged();
this.mData.refresh();
}
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position) {
int noSwipeSize = mData.getAllToDo().size();
if (direction == ItemTouchHelper.LEFT) {
mData.deleteTask(mData.getAdapter().getResults().get(viewHolder.getAdapterPosition()));
mData.getAdapter().notifyItemRangeRemoved(viewHolder.getAdapterPosition(), noSwipeSize);
} else if (direction == ItemTouchHelper.RIGHT) {
mData.completeTask(mData.getAdapter().getResults().get(viewHolder.getAdapterPosition()));
mData.getAdapter().notifyItemRangeRemoved(viewHolder.getAdapterPosition(), noSwipeSize);
}
}
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
final View foregroundView = ((com.lab1.ac01220.bloomv2.ViewHolder) viewHolder).getForeground();
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
if(dX < 0){
this.mData.getViewHolder().getDeleteText().setVisibility(View.VISIBLE);
this.mData.getViewHolder().getCompleteText().setVisibility(View.INVISIBLE);
this.mData.getViewHolder().getBackground().setBackgroundColor(getResources().getColor(R.color.colorAccent));
} else if(dX >0){
this.mData.getViewHolder().getCompleteText().setVisibility(View.VISIBLE);
this.mData.getViewHolder().getDeleteText().setVisibility(View.INVISIBLE);
this.mData.getViewHolder().getBackground().setBackgroundColor(getResources().getColor(R.color.colorComplete));
}
}
ItemTouchHelper.SimpleCallback.getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,actionState, isCurrentlyActive);
}
#Override
public void onChildDrawOver(Canvas c, RecyclerView recyclerView, ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
final View foregroundView = ((com.lab1.ac01220.bloomv2.ViewHolder) viewHolder).getForeground();
ItemTouchHelper.SimpleCallback.getDefaultUIUtil().onDrawOver(c, recyclerView, foregroundView, dX, dY,actionState, isCurrentlyActive);
}
}
RecycleitemTouchHelper.java
public RecycleItemTouchHelper(int dragDirs, int swipeDirs, RecyclerItemTouchHelperListener listener) {
super(dragDirs, swipeDirs);
this.listener = listener;
}
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
this.listener.onSwiped(viewHolder, direction, viewHolder.getAdapterPosition());
}
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
this.listener.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
#Override
public void onChildDrawOver(Canvas c, RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder, float dX, float dY,
int actionState, boolean isCurrentlyActive) {
this.listener.onChildDrawOver(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
#Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
final View foregroundView = ((ViewHolder) viewHolder).getForeground();
getDefaultUIUtil().clearView(foregroundView);
}
#Override
public int convertToAbsoluteDirection(int flags, int layoutDirection) {
return super.convertToAbsoluteDirection(flags, layoutDirection);
}
public interface RecyclerItemTouchHelperListener {
void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position);
void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive);
void onChildDrawOver(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive);
}
}
RecycleViewAdapter.java:
public class RecycleViewAdapter extends RecyclerView.Adapter<ViewHolder>{
private LayoutInflater inflater = null;
private List<GlobalLists> results = null;
private DataCommunication mData = null;
public RecycleViewAdapter(Context context, List<GlobalLists> results){
this.inflater = LayoutInflater.from(context);
this.results = results;
this.mData = (DataCommunication)context;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.custom_row, parent, false);
ViewHolder holder = new ViewHolder(view);
this.mData.setViewHolder(holder);
return holder;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
GlobalLists item = this.results.get(position);
holder.setRowTitle(item.getTitle());
//Grab the first 14 characters of the contents if the row is not null
if(item.getContents() != null){
StringBuffer currentContent = new StringBuffer(item.getContents().substring(0, Math.min(item.getContents().length(),28)));
if(item.getContents().length() > 28) currentContent.append("...");
holder.setRowContent(currentContent.toString());
}
}
public void reloadAdapterData(List<GlobalLists> refresh){
this.results = refresh;
}
#Override
public int getItemCount() {
return this.results == null || this.results.isEmpty() ? 0 : this.results.size();
}
public List<GlobalLists> getResults(){
return this.results;
}
}
The problem was that I was simply setting the first ViewHolder to be a global variable and only anacting changes on that. Instead I should have been getting the current View being draw in onChildDraw and performing functions on that:
Was:
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
final View foregroundView = ((com.lab1.ac01220.bloomv2.ViewHolder) viewHolder).getForeground();
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
if(dX < 0){
this.mData.getViewHolder().getDeleteText().setVisibility(View.VISIBLE);
this.mData.getViewHolder().getCompleteText().setVisibility(View.INVISIBLE);
this.mData.getViewHolder().getBackground().setBackgroundColor(getResources().getColor(R.color.colorAccent));
} else if(dX >0){
this.mData.getViewHolder().getCompleteText().setVisibility(View.VISIBLE);
this.mData.getViewHolder().getDeleteText().setVisibility(View.INVISIBLE);
this.mData.getViewHolder().getBackground().setBackgroundColor(getResources().getColor(R.color.colorComplete));
}
}
ItemTouchHelper.SimpleCallback.getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,actionState, isCurrentlyActive);
}
Should be:
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
com.lab1.ac01220.bloomv2.ViewHolder current = (com.lab1.ac01220.bloomv2.ViewHolder) viewHolder;
final View foregroundView = ((com.lab1.ac01220.bloomv2.ViewHolder) viewHolder).getForeground();
if (isCurrentlyActive) {
if(dX < 0){
current.getDeleteText().setVisibility(View.VISIBLE);
current.getCompleteText().setVisibility(View.INVISIBLE);
current.getBackground().setBackground(getResources().getDrawable(R.drawable.ic_rounded_rectangle_accent));
} else if(dX >0){
current.getCompleteText().setVisibility(View.VISIBLE);
current.getDeleteText().setVisibility(View.INVISIBLE);
current.getBackground().setBackground(getResources().getDrawable(R.drawable.ic_rounded_rectangle_complete));
}
}
ItemTouchHelper.SimpleCallback.getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,actionState, isCurrentlyActive);
}