I am trying to develop a functionality like Gmail, where you can swipe to delete.
This is my Main Fragment with recycler view.
public class MainFragment extends Fragment implements RecyclerItemTouchHelper.RecyclerItemTouchHelperListener {
RecyclerView recyclerView;
CoordinatorLayout coordinatorLayout;
RecyclerViewAdapter recyclerViewAdapter = null;
private View view;
private Context context = null;
ArrayList<Note> notes = new ArrayList<>();
ArrayList<Note> allNotes;
FloatingActionButton fab;
boolean isLoading = false;
private MainViewModel mViewModel;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.main_fragment, container, false);
view = root;
recyclerView = view.findViewById(R.id.recyclerView);
ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new RecyclerItemTouchHelper(0, ItemTouchHelper.LEFT, this);
new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerView);
return root;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
coordinatorLayout = view.findViewById(R.id.mainContent)
populateNotesView();
}
#Override
public void onResume() {
super.onResume();
}
public void populateNotesView() {
MainViewModel obj = new MainViewModel();
allNotes = obj.getData(getContext());
notes.clear();
for (int i = 0; i < 10 && i < allNotes.size(); i++) {
notes.add(allNotes.get(i));
}
initAdapter(notes);
}
private void initAdapter(ArrayList<Note> notes) {
recyclerViewAdapter = new RecyclerViewAdapter(notes, getFragmentManager());
recyclerView.setAdapter(recyclerViewAdapter);
initScrollListener();
}
#Override
public void onDelete(final RecyclerView.ViewHolder viewHolder, int position) {
if (viewHolder instanceof RecyclerViewAdapter.ItemViewHolder) {
ArrayList<Note> notesList = allNotes;
String name = notesList.get(viewHolder.getAdapterPosition()).getText();
final Note deletedItem = notesList.get(viewHolder.getAdapterPosition());
final int deletedIndex = viewHolder.getAdapterPosition();
recyclerViewAdapter = new RecyclerViewAdapter(notesList, getFragmentManager());
recyclerViewAdapter.removeItem(viewHolder.getAdapterPosition(), getContext());
Snackbar snackbar = Snackbar.make(coordinatorLayout, name + " removed from cart!", Snackbar.LENGTH_LONG);
snackbar.setAction("UNDO", new View.OnClickListener() {
#Override
public void onClick(View view) {
recyclerViewAdapter.restoreItem(deletedItem, deletedIndex, getContext());
getFragmentManager().beginTransaction()
.replace(R.id.mainContent, MainFragment.newInstance())
.commitNow();
}
});
snackbar.setActionTextColor(Color.YELLOW);
snackbar.show();
//Reloading fragment
getFragmentManager().beginTransaction()
.replace(R.id.mainContent, MainFragment.newInstance())
.commitNow();
}
}
}
And this is my RecyclerItemTouchHelper
public class RecyclerItemTouchHelper extends ItemTouchHelper.SimpleCallback {
private RecyclerItemTouchHelperListener listener;
private boolean delete = false;
public RecyclerItemTouchHelper(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 true;
}
#Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (viewHolder != null) {
final View foregroundView = ((RecyclerViewAdapter.ItemViewHolder) viewHolder).viewForeground;
getDefaultUIUtil().onSelected(foregroundView);
}
}
#Override
public void onChildDrawOver(Canvas c, RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder, float dX, float dY,
int actionState, boolean isCurrentlyActive) {
final View foregroundView = ((RecyclerViewAdapter.ItemViewHolder) viewHolder).viewForeground;
getDefaultUIUtil().onDrawOver(c, recyclerView, foregroundView, dX, dY,
actionState, isCurrentlyActive);
}
#Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
final View foregroundView = ((RecyclerViewAdapter.ItemViewHolder) viewHolder).viewForeground;
getDefaultUIUtil().clearView(foregroundView);
}
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView,
final RecyclerView.ViewHolder viewHolder, float dX, float dY,
int actionState, boolean isCurrentlyActive) {
final View foregroundView = ((RecyclerViewAdapter.ItemViewHolder) viewHolder).viewForeground;
getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,
actionState, isCurrentlyActive);
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
listener.onDelete(viewHolder, viewHolder.getAdapterPosition());
}
#Override
public int convertToAbsoluteDirection(int flags, int layoutDirection) {
return super.convertToAbsoluteDirection(flags, layoutDirection);
}
public interface RecyclerItemTouchHelperListener {
void onDelete(RecyclerView.ViewHolder viewHolder, int position);
}
}
Now swiping a recyclerview item, doesn't call the onSwiped method every time. And I'm completely swiping the item.
EDIT: Any new item added is deleted successfully but already added items are not getting deleted.
EDIT 2: Adapter
public void removeItem(int position, Context context) {
MainViewModel obj = new MainViewModel();
obj.deleteData(context, mItemList.get(position).getText());
mItemList.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, mItemList.size());
}
public void restoreItem(Note item, int position, Context context) {
Util.newInstance().save(context, item.getText());
mItemList.add(position, item);
notifyItemInserted(position);
notifyItemRangeChanged(position, mItemList.size());
}
Any help is appreciated.
Thanks in advance.
The item isn't removed because you are using the original list in onDelete() by using below line of code:
ArrayList<Note> notesList = allNotes;
That means that whenever you delete an item, you just back to the original list.
The second issue is when you undo the deletion using the sanckbar action, you do the same thing by calling this:
populateNotesView();
To solve this, you need to remove the item from the original list you feed the RecyclerView adapter with, and just call notifyItemRemoved() for the removed position.
To do so, change onDelete() to be:
#Override
public void onDelete(final RecyclerView.ViewHolder viewHolder, int position) {
if (viewHolder instanceof RecyclerViewAdapter.ItemViewHolder) {
String name = allNotes.get(viewHolder.getAdapterPosition()).getText();
final Note deletedItem = allNotes.get(viewHolder.getAdapterPosition());
final int deletedIndex = viewHolder.getAdapterPosition();
allNotes.remove(deletedIndex);
recyclerViewAdapter.notifyItemRemoved(deletedIndex);
Snackbar snackbar = Snackbar.make(coordinatorLayout, name + " removed from cart!", Snackbar.LENGTH_LONG);
snackbar.setAction("UNDO", new View.OnClickListener() {
#Override
public void onClick(View view) {
allNotes.add(deletedIndex, deletedItem);
recyclerViewAdapter.notifyItemInserted(deletedIndex);
}
});
}
}
Related
I have a recycler view in my app that populates by volley, and I get some restaurants name and address from REST api. I wanted to have swipe to delete in my app so I used Item Touch helper, but I don`t know why I'm getting this error. I'm kinda new in android. Please help.
my RecyclerItemTouchHelper
public class RecyclerItemTouchHelper extends ItemTouchHelper.SimpleCallback {
private RecyclerItemTouchHelperListener listener;
public RecyclerItemTouchHelper(int dragDirs, int swipeDirs, RecyclerItemTouchHelperListener listener) {
super(dragDirs, swipeDirs);
this.listener = listener;
}
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
return true;
}
#Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (viewHolder != null) {
final View foregroundView = ((RestaurantAdapter.MyViewHolder) viewHolder).viewForeground;
getDefaultUIUtil().onSelected(foregroundView);
}
}
#Override
public void onChildDrawOver(Canvas c, RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder, float dX, float dY,
int actionState, boolean isCurrentlyActive) {
final View foregroundView = ((RestaurantAdapter.MyViewHolder) viewHolder).viewForeground;
getDefaultUIUtil().onDrawOver(c, recyclerView, foregroundView, dX, dY,
actionState, isCurrentlyActive);
}
#Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
final View foregroundView = ((RestaurantAdapter.MyViewHolder) viewHolder).viewForeground;
getDefaultUIUtil().clearView(foregroundView);
}
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder, float dX, float dY,
int actionState, boolean isCurrentlyActive) {
final View foregroundView = ((RestaurantAdapter.MyViewHolder) viewHolder).viewForeground;
getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,
actionState, isCurrentlyActive);
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
listener.onSwiped(viewHolder, direction, viewHolder.getAdapterPosition());
}
#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);
}
my recycler adapter class
public class RestaurantAdapter extends RecyclerView.Adapter<RestaurantAdapter.MyViewHolder> implements View.OnClickListener {
private List<Restaurant> restaurantList;
private Context context;
private int position;
public int getPosition() {return position;}
public void setPosition(int position) { this.position = position;}
public RestaurantAdapter(List<Restaurant> restaurantList, Context context){
this.restaurantList = restaurantList;
this.context = context;
}
#Override
public void onClick(View view){
}
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener {
TextView listviewName,listviewAddress;
ImageView icon,noteIcon;
public RelativeLayout viewBackground, viewForeground;
MyViewHolder(View view){
super(view);
listviewName = view.findViewById(R.id.listview_name);
listviewAddress = view.findViewById(R.id.listview_address);
icon = view.findViewById(R.id.type_ic);
noteIcon = view.findViewById(R.id.note_icon);
viewBackground = view.findViewById(R.id.view_background);
viewForeground = view.findViewById(R.id.view_foreground);
view.setOnCreateContextMenuListener(this);
}
#Override
public void onCreateContextMenu(ContextMenu menu,View v,ContextMenu.ContextMenuInfo menuInfo) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
menu.add(0, 1, 0, "Edit");
menu.add(0, 2, 1, "Remove");
menu.add(0, 3, 2, "Add Note");
menu.add(0, 4, 3, "All Notes");
}
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_layout,parent,false);
Animation animation = AnimationUtils.loadAnimation(context, R.anim.fadein);
itemView.startAnimation(animation);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull final MyViewHolder holder, int position) {
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
setPosition(holder.getAdapterPosition());
return false;
}
});
Restaurant restaurant = restaurantList.get(position);
holder.listviewName.setText(restaurant.getName());
holder.listviewAddress.setText(restaurant.getAddress());
switch (restaurant.getImage()) {
case RestaurantContract.EntryRestaurants.RESTAURANT_TYPE_DELIVERY:
holder.icon.setImageResource(R.drawable.phoneorderr);
break;
case RestaurantContract.EntryRestaurants.RESTAURANT_TYPE_SITDOWN:
holder.icon.setImageResource(R.drawable.sitdownn);
break;
case RestaurantContract.EntryRestaurants.RESTAURANT_TYPE_TAKEAWAY:
holder.icon.setImageResource(R.drawable.takeaway);
break;
}
holder.noteIcon.setImageResource(R.drawable.notepadicon);
if(restaurant.isHasNote()) {
holder.noteIcon.setVisibility(View.VISIBLE);
}else {
holder.noteIcon.setVisibility(View.INVISIBLE);
}
}
#Override
public int getItemCount() {
return restaurantList.size();
}
public void removeItem(int position){
restaurantList.remove(position);
notifyItemRemoved(position);
}
public void restoreItem(Restaurant restaurant, int position){
restaurantList.add(position,restaurant);
notifyItemInserted(position);
}
}
my mainActivity class
ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new RecyclerItemTouchHelper(0, ItemTouchHelper.LEFT, this);
new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(fastfoodRecyclerView);
and my logcat
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object android.view.View.getTag(int)' on a null object reference
at androidx.recyclerview.widget.ItemTouchUIUtilImpl.onDraw(ItemTouchUIUtilImpl.java:38)
at com.test.fastfoodfinder.Restaurant.RecyclerItemTouchHelper.onChildDraw(RecyclerItemTouchHelper.java:54)
at androidx.recyclerview.widget.ItemTouchHelper$Callback.onDraw(ItemTouchHelper.java:1989)
at androidx.recyclerview.widget.ItemTouchHelper.onDraw(ItemTouchHelper.java:561)
at androidx.recyclerview.widget.RecyclerView.onDraw(RecyclerView.java:4284)
at android.view.View.draw(View.java:19192)
I have a recycler view that displays a list of items.
Within each item, there is a title and another RecyclerView that display a list of items.
I want to access the click events of the items of the child RecyclerView.
Screenshot of the layout:
The parent RecyclerView:
public class DetailsGroupAdapter extends RecyclerView.Adapter<DetailsGroupAdapter.ViewHolder>{
private static OnItemClickListener listener;
private Context context;
private int lastPosition = -1;
RecyclerView mRecyclerView;
public interface OnItemClickListener {
void onItemClick(View itemView, int position);
void onGroupItemLongClick(View itemView, int groupPosition, int itemPosition);
void onGroupItemClick(View itemView, int groupPosition, int itemPosition);
}
public void setOnItemClickListener(OnItemClickListener listener) {
DetailsGroupAdapter.listener = listener;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView tvTitle;
public CardView cvContainer;
public RecyclerView rvItems;
public ViewHolder(final View itemView)
{
super(itemView);
tvTitle = itemView.findViewById(R.id.group_title);
cvContainer = itemView.findViewById(R.id.container);
rvItems = itemView.findViewById(R.id.rv_group_items);
//click listener setup
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Triggers click upwards to the adapter on click
if (listener != null)
listener.onItemClick(itemView, getLayoutPosition());
}
});
}
public void clearAnimation()
{
cvContainer.clearAnimation();
}
}
private List<DetailsGroup> itemList;
public DetailsGroupAdapter(List<DetailsGroup> itemList)
{
this.itemList = itemList;
}
#Override
public DetailsGroupAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View layoutView = inflater.inflate(R.layout.item_details_group, parent, false);
mRecyclerView = (RecyclerView) parent;
return new ViewHolder(layoutView);
}
#Override
public void onBindViewHolder(final DetailsGroupAdapter.ViewHolder viewHolder, int groupPosition){
final DetailsGroup detailsGroup = itemList.get(groupPosition);
if(detailsGroup.Title != null){
viewHolder.tvTitle.setText(detailsGroup.Title);
} else {
viewHolder.tvTitle.setVisibility(View.GONE);
}
final DetailsGroupItemsAdapter detailsGroupAdapter = new DetailsGroupItemsAdapter(detailsGroup.itemsList, groupPosition);
detailsGroupAdapter.setOnItemClickListener(new DetailsGroupItemsAdapter.OnItemClickListener() {
#Override
public void onItemClick(View itemView, int position) {
if (listener != null){
listener.onGroupItemClick(itemView, (int) itemView.getTag(), position);
}
}
#Override
public void onLongClick(View itemView, int position) {
if (listener != null){
listener.onGroupItemLongClick(itemView, (int) itemView.getTag(), position);
}
}
});
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context) {};
viewHolder.rvItems.setLayoutManager(linearLayoutManager);
viewHolder.rvItems.setAdapter(detailsGroupAdapter);
}
private void setAnimation(View viewToAnimate, int position)
{
if (position > lastPosition) {
AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f);
anim.setDuration(1000);
viewToAnimate.startAnimation(anim);
lastPosition = position;
}
}
#Override
public void onViewDetachedFromWindow(final ViewHolder holder)
{
((ViewHolder)holder).cvContainer.clearAnimation();
}
#Override
public long getItemId(int position) {
return itemList.get(position).hashCode();
}
#Override
public int getItemCount()
{
return itemList.size();
}
public void clear() {
itemList.clear();
notifyDataSetChanged();
}
public void addAll(List<DetailsGroup> list){
itemList.addAll(list);
notifyDataSetChanged();
}
}
The child RecyclerView:
public class DetailsGroupItemsAdapter extends RecyclerView.Adapter<DetailsGroupItemsAdapter.ViewHolder>{
private static OnItemClickListener listener;
private Context context;
private int lastPosition = -1;
private int groupPosition;
public interface OnItemClickListener {
void onItemClick(View itemView, int position);
void onLongClick(View itemView,int position);
}
public void setOnItemClickListener(OnItemClickListener listener) {
DetailsGroupItemsAdapter.listener = listener;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView tvTitle;
public TextView tvSubTitle;
public ImageView ivThumbnail;
public LinearLayout llContainer;
public ViewHolder(final View itemView)
{
super(itemView);
ivThumbnail = itemView.findViewById(R.id.thumbnail);
tvTitle = itemView.findViewById(R.id.title);
tvSubTitle = itemView.findViewById(R.id.subtitle);
llContainer = itemView.findViewById(R.id.container);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null)
listener.onItemClick(itemView, getLayoutPosition());
}
});
itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
if (listener != null)
listener.onLongClick(itemView, getLayoutPosition());
return false;
}
});
}
public void clearAnimation()
{
llContainer.clearAnimation();
}
}
private List<ItemDetail> itemList;
public DetailsGroupItemsAdapter(List<ItemDetail> itemList, int groupPosition)
{
this.itemList = itemList;
this.groupPosition = groupPosition;
}
#Override
public DetailsGroupItemsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View layoutView = inflater.inflate(R.layout.item_details, parent, false);
return new ViewHolder(layoutView);
}
#Override
public void onBindViewHolder(DetailsGroupItemsAdapter.ViewHolder viewHolder, int position){
viewHolder.itemView.setTag(groupPosition);
ItemDetail itemDetail = itemList.get(position);
if(itemDetail.Title != null){
viewHolder.tvTitle.setText(itemDetail.Title);
} else {
viewHolder.tvTitle.setVisibility(View.GONE);
}
if(itemDetail.Sub_Title != null){
viewHolder.tvSubTitle.setText(itemDetail.Sub_Title);
} else {
viewHolder.tvSubTitle.setVisibility(View.GONE);
}
if(itemDetail.Thumbnail_Enabled){
PicManipulationUtility.SetGenericPictureFromThumbnailType(viewHolder.ivThumbnail, itemDetail.Thumbnail_Type, itemDetail.Thumbnail);
} else {
viewHolder.ivThumbnail.setVisibility(View.GONE);
}
setAnimation(viewHolder.llContainer, position);
}
private void setAnimation(View viewToAnimate, int position)
{
if (position > lastPosition) {
AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f);
anim.setDuration(1000);
viewToAnimate.startAnimation(anim);
lastPosition = position;
}
}
#Override
public void onViewDetachedFromWindow(final ViewHolder holder)
{
((ViewHolder)holder).llContainer.clearAnimation();
}
#Override
public long getItemId(int position) {
return itemList.get(position).hashCode();
}
#Override
public int getItemCount()
{
return itemList.size();
}
public void clear() {
itemList.clear();
notifyDataSetChanged();
}
public void addAll(List<ItemDetail> list){
itemList.addAll(list);
notifyDataSetChanged();
}
}
The classes used:
ItemDetail and DetailsGroup are POJOs to convert JSON into objects.
What I managed to do so far to temporarily fix the problem is:
In onBindViewHolder of parent RecyclerView, I grabbed the group position.
Then I passed the group position of the parent down to the child
adapter initializer in (DetailsGroupItemsAdapter)
Finally, in the child onBindViewHolder I set the tag to be the parent
group position(viewHolder.itemView.setTag(groupPosition);)
So in the click event of the child, I can say:
listener.onGroupItemClick(itemView, (int) itemView.getTag(), position);
Thus having both the group position and the child position in the bubbled event in my Activity or Fragment.
But I know for sure that the passed group position can change for any reason such as updating group items or removing items.
I want to know if there is a more robust solution to implement the click events of nested items in this case.
I used this https://gist.github.com/riyazMuhammad/1c7b1f9fa3065aa5a46f, especially
CustomItemClickListener.java :
public interface CustomItemClickListener {
public void onItemClick(View v, int position);
}
(Parent) RecyclerView modifications :
public class ItemsListAdapter extends RecyclerView.Adapter<ItemsListAdapter.ViewHolder> {
ArrayList<ItemListSingleItem> data;
Context mContext;
CustomItemClickListener listener;
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.items_list_single_item, parent, false);
final ViewHolder mViewHolder = new ViewHolder(mView);
mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
listener.onItemClick(v, mViewHolder.getPosition());
}
});
return mViewHolder;
}
//[... the other "classical" parts of your code...]
}
Alone, it allowed me to have separate click events on child and parent
With some modifications, it led me to something similar to your "fix", I think, but maybe it'll give you some clues : I am giving the parent position and the parent listener to the child when constructing it, and then a click on the child produces the same behavior as a click on the parent
Child RecyclerView constructor :
public AppsAdapter(ArrayList<PackageInformation.InfoObject> dataSet, CustomOnItemClickListener customItemClickListener, int parentPos) {
appsDataSet = dataSet;
onItemClickListener = customItemClickListener;
parentPosition = parentPos;
}
Child RecyclerView onCreateViewHolder :
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
// Create a new view.
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_app, viewGroup, false);
// Create the ViewHolder to return
final AppsAdapter.ViewHolder mViewHolder = new AppsAdapter.ViewHolder(v);
// Add the Listener
v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) { onItemClickListener.onItemClick(v, parentPosition); }
});
return mViewHolder;
i want change the position of Text View using drag and drop functionally in android using Drag listener and animation. i am done a design using following code. i want to change the order of text view using Drag Listener. thanks i give a image of design and i want change position of text view using drag and drop
public void loadtable()
{
Sorting_Linear = (LinearLayout) findViewById(R.id.Sorting_Linear);
submit = (Button) findViewById(R.id.submit);
LinearLayout[] llRow = new LinearLayout[5];
final TextView[] outletnametxt = new TextView[5];
final ImageView[] imageButtonup = new ImageView[5];
final ImageView[] imageButtondown = new ImageView[5];
final LinearLayout[] Linearlayout = new LinearLayout[5];
for (int i = 0; i < 5; i++) {
llRow[i] = new LinearLayout(mContext);
LinearLayout.LayoutParams paramsllRow = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
paramsllRow.setMargins(0, 2, 0, 0);
llRow[i].setLayoutParams(paramsllRow);
llRow[i].setOrientation(LinearLayout.HORIZONTAL);
outletnametxt[i] = new TextView(mContext);
outletnametxt[i].setLayoutParams(nametxt);
outletnametxt[i].setText(olm_name[i]);
outletnametxt[i].setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
outletnametxt[i].setTextSize(20);
outletnametxt[i].setGravity(Gravity.CENTER);
outletnametxt[i].setBackgroundColor(mContext.getResources().getColor(android.R.color.white));
llRow[i].addView(outletnametxt[i]);
imageButtonup[i] = new ImageView(mContext);
imageButtonup[i].setLayoutParams(imagebtnup);
imageButtonup[i].setImageResource(R.drawable.arrowup);
imageButtondown[i] = new ImageView(mContext);
imageButtondown[i].setLayoutParams(imagebtndown);
imageButtondown[i].setImageResource(R.drawable.arrowdown);
Linearlayout[i] = new LinearLayout(mContext);
Linearlayout[i].setLayoutParams(linearlayout);
Linearlayout[i].setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
Linearlayout[i].setBackgroundColor(mContext.getResources().getColor(android.R.color.white));
Linearlayout[i].setOrientation(LinearLayout.VERTICAL);
Linearlayout[i].addView(imageButtonup[i]);
Linearlayout[i].addView(imageButtondown[i]);
llRow[i].addView(Linearlayout[i]);
Sorting_Linear.addView(llRow[i]);
}
Create below interface ItemTouchHelperAdapter
public interface ItemTouchHelperAdapter {
boolean onItemMove(int fromPosition, int toPosition);
void onItemDismiss(int position);}
Create second interface ItemTouchHelperViewHolder
public interface ItemTouchHelperViewHolder {
void onItemSelected();
void onItemClear();}
Create OnStartDragListener
public interface OnStartDragListener {
void onStartDrag(RecyclerView.ViewHolder viewHolder);}
Create below class
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
public static final float ALPHA_FULL = 1.0f;
private final ItemTouchHelperAdapter mAdapter;
public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
mAdapter = adapter;
}
#Override
public boolean isLongPressDragEnabled() {
return true;
}
#Override
public boolean isItemViewSwipeEnabled() {
return true;
}
#Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
// Set movement flags based on the layout manager
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
} else {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
}
}
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
if (source.getItemViewType() != target.getItemViewType()) {
return false;
}
// Notify the adapter of the move
mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition());
return true;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
// Notify the adapter of the dismissal
mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
}
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
// Fade out the view as it is swiped out of the parent's bounds
final float alpha = ALPHA_FULL - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
viewHolder.itemView.setAlpha(alpha);
viewHolder.itemView.setTranslationX(dX);
} else {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
}
#Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
// We only want the active item to change
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
if (viewHolder instanceof ItemTouchHelperViewHolder) {
// Let the view holder know that this item is being moved or dragged
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemSelected();
}
}
super.onSelectedChanged(viewHolder, actionState);
}
#Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
viewHolder.itemView.setAlpha(ALPHA_FULL);
if (viewHolder instanceof ItemTouchHelperViewHolder) {
// Tell the view holder it's time to restore the idle state
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemClear();
}
}}
Now just add this code to your recycler view adapter
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new RecyclerListAdapter(Activity_OutletSorting.this, onStartDragListener, olm_name,idopeningclosing);
recyclerView.setAdapter(adapter);
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter);
mItemTouchHelper = new ItemTouchHelper(callback);
mItemTouchHelper.attachToRecyclerView(recyclerView);
I have a RecyclerView and have implemented an onMove command called onItemMove. I'm using onItemMove to try and get the list elements to swap position when dragged around, but the list elements just hover over each, they don't swap. How can I correct this?
Note 1: onItemDismiss works fine; it swipes the item away and removes it from the list.
Note 2: I've tried to Override onItemMove, but it doesn't actually override its superclass.
List Adaptor Class: This contains the onItemMove command
public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ListViewHolder> {
private static final String TAG = "ListAdapter";
Context context;
private List<UserData> dataList = new ArrayList<>();
LayoutInflater inflater;
Listener listener;
DbHelper dbHelper;
public interface Listener {
void nameToChnge(String name);
}
public ListAdapter(Context context, List<UserData> dataList1) {
this.context = context;
this.dataList = dataList1;
this.listener= (Listener) context;
inflater = LayoutInflater.from(context);
}
#Override
public ListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View convertView = inflater.inflate(R.layout.recylerview_one, parent, false);
ListViewHolder viewHolder = new ListViewHolder(convertView);
return viewHolder;
}
#Override
public void onBindViewHolder(ListViewHolder holder, final int position) {
holder.tv_name.setText(dataList.get(position).name);
holder.tv_quantity.setText(dataList.get(position).quantity);
holder.tv_description.setText(dataList.get(position).description + "");
holder.relLayout.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
String s = dataList.get(position).id;
Integer stringo = Integer.parseInt(s);
Intent intent = new Intent(context, ItemEditActivity.class);
intent.putExtra("ItemNumber", stringo);
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return dataList.size();
}
class ListViewHolder extends RecyclerView.ViewHolder {
TextView tv_name, tv_quantity, tv_description;
RelativeLayout relLayout;
public ListViewHolder(View itemView) {
super(itemView);
tv_name = (TextView) itemView.findViewById(R.id.nameDisplay);
tv_quantity = (TextView) itemView.findViewById(R.id.quantityDisplay);
tv_description = (TextView) itemView.findViewById(R.id.descriptionDisplay);
relLayout = (RelativeLayout) itemView.findViewById(R.id.relLayout);
}
}
public void onItemDismiss(final int position) {
dataList.remove(position);
notifyItemRemoved(position);
}
public void onItemMove(int fromPosition, int toPosition) {
Collections.swap(dataList, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
}
}
ItemTouchHelper Class:
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback{
private final ListAdapter mAdapter;
public SimpleItemTouchHelperCallback(ListAdapter adapter) {
mAdapter = adapter;
}
#Ov
erride
public boolean isLongPressDragEnabled() {
return true;
}
#Override
public boolean isItemViewSwipeEnabled() {
return true;
}
#Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
#Override
public boolean onMove(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) {
mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
return true;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
}
}
Part of the Main Activity where I initialise the RecyclerView and
SimpleItemTouchHelperCallback:
public class MainActivity extends AppCompatActivity implements ListAdapter.Listener {
private static final String TAG = "MainActivity";
RecyclerView recyclerView;
DbHelper dbHelper;
ListAdapter adapter;
FloatingActionButton fab;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
introItem();
dbHelper = DbHelper.getInstance(getApplicationContext());
recyclerView= (RecyclerView) findViewById(R.id.rv_contactlist);
adapter = new ListAdapter(this, dbHelper.getAllUser());
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
ItemTouchHelper.Callback callback =
new SimpleItemTouchHelperCallback(adapter);
ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(recyclerView);
totalQuantity();
adapter.notifyDataSetChanged();
fabHideShow();
versionCheckMethod();
}
You just need to study the code of this sample and implement it: https://github.com/iPaulPro/Android-ItemTouchHelper-Demo/tree/master/app/src/main/java/co/paulburke/android/itemtouchhelperdemo
But basically what you need is to create two interfaces. The first one:
public interface ItemTouchHelperAdapter {
/**
* Called when an item has been dragged far enough to trigger a move. This is called every time
* an item is shifted, and not at the end of a "drop" event.
*
* #param fromPosition The start position of the moved item.
* #param toPosition Then end position of the moved item.
*/
void onItemMove(int fromPosition, int toPosition);
/**
* Called when an item has been dismissed by a swipe.
*
* #param position The position of the item dismissed.
*/
void onItemDismiss(int position);
}
And the second one:
public interface ItemTouchHelperViewHolder {
/**
* Implementations should update the item view to indicate it's active state.
*/
void onItemSelected();
/**
* state should be cleared.
*/
void onItemClear();
}
The in the SimpleItemTouchHelperCallback something like that:
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
private final ItemTouchHelperAdapter mAdapter;
public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
mAdapter = adapter;
}
#Override
public boolean isLongPressDragEnabled() {
return true;
}
#Override
public boolean isItemViewSwipeEnabled() {
return false;
}
#Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition());
return true;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
}
#Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemSelected();
}
super.onSelectedChanged(viewHolder, actionState);
}
#Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemClear();
}
}
Then you have to change the RecyclerViewAdapter, and implement there the onItemMove method as we said in the oder comments. And you should implemement also the onStartDragListener like so:
public interface OnStartDragListener {
/**
* Called when a view is requesting a start of a drag.
*
* #param viewHolder The holder of the view to drag.
*/
void onStartDrag(RecyclerView.ViewHolder viewHolder);
}
And then use it in your adapter onBindViewHolder (remember to implement all the interfaces in the class declaration) like this (You need to change this code based on your variable name):
holder.handleView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
mDragStartListener.onStartDrag(holder);
}
return false;
}
});
Where mDragListener is a OnStartDragListener variable.
Finally check this guide for more: http://valokafor.com/remember-drag-and-drop-position-with-recyclerview/
I have implemented a RecyclerView with inbox style swipe view. When swiped, I removed the list item using the method below:
public void removeItem(int position) {
countries.remove(position);
notifyItemRemoved(position);
}
Similarly when the FAB is pressed I add data using the method,
public void addItem(String country) {
countries.add(country);
notifyItemInserted(countries.size());
}
However, when I remove a data item by swiping, it is removed from the ArrayList and RecyclerView list, but when I add data by FAB the removed data is still displayed in the list. I checked the ArrayList data set. It is as intended.
In the above screenshot you can see the String Test is the newly added data. The data in last two row I already deleted. It gets randomly displayed.
The complete code of my Adapter and Activity.
public class DataAdapter extends RecyclerView.Adapter<DataAdapter.ViewHolder> {
private ArrayList<String> countries;
private TextView tv_country;
public DataAdapter(ArrayList<String> countries) {
this.countries = countries;
}
#Override
public DataAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.row_layout, viewGroup, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(DataAdapter.ViewHolder viewHolder, int i) {
tv_country.setText(countries.get(i));
}
#Override
public int getItemCount() {
return countries.size();
}
public void addItem(String country) {
countries.add(country);
notifyItemInserted(countries.size());
}
public void removeItem(int position) {
countries.remove(position);
notifyItemRemoved(position);
}
public class ViewHolder extends RecyclerView.ViewHolder{
public ViewHolder(View view) {
super(view);
tv_country = (TextView)view.findViewById(R.id.tv_country);
}
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private ArrayList<String> countries = new ArrayList<>();
private DataAdapter adapter;
private RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
}
private void initViews(){
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(this);
recyclerView = (RecyclerView)findViewById(R.id.card_recycler_view);
recyclerView.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(layoutManager);
adapter = new DataAdapter(countries);
recyclerView.setAdapter(adapter);
countries.add("Australia");
countries.add("India");
countries.add("United States of America");
countries.add("Germany");
countries.add("Russia");
adapter.notifyDataSetChanged();
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
if (direction == ItemTouchHelper.LEFT){
adapter.removeItem(position);
}
}
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
Paint p = new Paint();
Bitmap icon;
if(actionState == ItemTouchHelper.ACTION_STATE_SWIPE){
View itemView = viewHolder.itemView;
if(dX > 0){
p.setColor(Color.parseColor("#388E3C"));
c.drawRect((float) itemView.getLeft(), (float) itemView.getTop(), dX,(float) itemView.getBottom(), p);
icon = BitmapFactory.decodeResource(
getResources(), R.drawable.ic_edit_white);
float height = (float) itemView.getBottom() - (float) itemView.getTop();
float width = height / 3;
RectF dest = new RectF((float) itemView.getLeft() + width ,(float) itemView.getTop() + width,(float) itemView.getLeft()+ width+width,(float)itemView.getBottom() - width);
c.drawBitmap(icon,null,dest,p);
} else {
p.setColor(Color.parseColor("#D32F2F"));
c.drawRect((float) itemView.getRight() + dX, (float) itemView.getTop(),(float) itemView.getRight(), (float) itemView.getBottom(), p);
icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_delete_white);
float height = (float) itemView.getBottom() - (float) itemView.getTop();
float width = height / 3;
RectF dest = new RectF((float) itemView.getRight() - width - width ,(float) itemView.getTop() + width,(float) itemView.getRight() - width,(float)itemView.getBottom() - width);
c.drawBitmap(icon,null,dest,p);
}
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
itemTouchHelper.attachToRecyclerView(recyclerView);
}
#Override
public void onClick(View v) {
switch (v.getId()){
case R.id.fab:
adapter.addItem("Test");
Log.d("Raj",countries.toString());
break;
}
}
}
What I have tried
I have tried using notifyItemRangeChanged() like this:
public void removeItem(int position) {
countries.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, countries.size());
}
I can't be sure that this will solve your problem, but one thing that is incorrect is how you are binding your data. tv_country should not be a part of the DataAdapter; it should be a part of each individual ViewHolder. One of the reasons we use the ViewHolder pattern is to maintain an easy reference to the views in each row.
Your bind method should end up similar to:
public void onBindViewHolder(DataAdapter.ViewHolder viewHolder, int i) {
viewHolder.tv_country.setText(countries.get(i));
}
And be sure to make tv_country a public field of your inner ViewHolder class.
public class ViewHolder extends RecyclerView.ViewHolder{
public TextView tv_country;
public ViewHolder(View view) {
super(view);
tv_country = (TextView) view.findViewById(R.id.tv_country);
}
}
Not sure I understood what is happening a 100%, but why are you actually trying to animate yourself? If you implement getItemId() and hasStableIds() if I remember correctly, you just should tell the recyclerview which element was removed or added (like you actually do).
Inside the onSwiped method, add viewHolder.setIsRecyclable(false);