I have a Note-App. In the Main Activity the Notes are going to be listed with a RecyclerView. To apply changes in the database I use FirestoreRecyclerOptions.
But I have a big problem. First, everything works fine. When the ListItems are changed once or more than one time everything gets confusing.
Then these things happen:
RecyclerViewItems are invisible (https://www.dropbox.com/s/76e0pi3bgmqdk4u/empty_list.mp4?dl=0)
RecyclerViewItem doesn't show
(https://www.dropbox.com/s/5ai92ypev1509qg/dont_show.mp4?dl=0)
RecyclerViewItem doesn't remove (https://www.dropbox.com/s/3k2k3ryecfwnqil/dosnt_remove.mp4?dl=0)
In the Videos, you can see all Items will work correctly after you scroll in the recycler view. I don't know why? Maybe the recyclerview than gets really reloaded?
Here is how I set up the RecyclerView and when I call the Adapter(Snippet of MainActivity):
public class MainActivity extends AppCompatActivity implements RecyclerItemTouchHelper.RecyclerItemTouchHelperListener {
private FirebaseFirestore db;
public NoteListAdapter adapter;
private RecyclerView recyclerView;
private RecyclerView.LayoutManager recyclerViewLayoutManager;
private ListenerRegistration firestoreListener;
private RecyclerItemTouchHelper.RecyclerItemTouchHelperListener listener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.rvNoteList);
db = FirebaseFirestore.getInstance();
setUpRecyclerView();
}
private void setUpRecyclerView() {
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder().setPersistenceEnabled(true).build();
db.setFirestoreSettings(settings);
Query query = db.collection("users").document(firebase_user_uid).collection("notes");
FirestoreRecyclerOptions<Note> response = new FirestoreRecyclerOptions.Builder<Note>().setQuery(query, Note.class).build();
adapter = new NoteListAdapter(response, MainActivity.this);
recyclerViewLayoutManager = new LinearLayoutManager(MainActivity.this, LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(recyclerViewLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.addItemDecoration(new CustomRecyclerViewDivider(this, LinearLayoutManager.VERTICAL, 16));
recyclerView.setAdapter(adapter);
ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new RecyclerItemTouchHelper(0, ItemTouchHelper.LEFT, MainActivity.this);
new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerView);
adapter.checkIflayoutMustImprove();
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position) {
adapter.removeItem(viewHolder.getAdapterPosition());
}
private void deleteNote(String id) {
db.collection("users").document(firebase_user_uid).collection("notes")
.document(id)
.delete()
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
mFirebaseAnalytics.logEvent(NOTE_DELETED_EVENT, null);
}
});
adapter.notifyDataSetChanged();
}
#Override
public void onStart() {
super.onStart();
adapter.startListening();
}
#Override
public void onStop() {
super.onStop();
adapter.stopListening();
}}
Here is the ItemRecyclerTouchHelper:
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(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 = ((NoteViewHolder) 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 = ((NoteViewHolder) viewHolder).viewForeground;
getDefaultUIUtil().onDrawOver(c, recyclerView, foregroundView, dX, dY,
actionState, isCurrentlyActive);
}
#Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
final View foregroundView = ((NoteViewHolder) 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 = ((NoteViewHolder) 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);
}}
I am sitting at this problem for two weeks and I really need help. I hope anyone can help me. I even don't know what the problem is. Is the problem the RecyclerView or Firebase or a simple bug? When you need any other files let me know.
Thanks for helping me.
Edit NoteListAdapter
public class NoteListAdapter extends FirestoreRecyclerAdapter{
private Context context;
private RelativeLayout no_data_layout;
private Animation fadeIn, fadeOut;
public NoteListAdapter(#NonNull FirestoreRecyclerOptions<Note> options, #NonNull Context context) {
super(options);
this.context = context;
}
#Override
protected void onBindViewHolder(#NonNull NoteViewHolder holder, int position, #NonNull Note note) {
holder.bind(context, note, getSnapshots().getSnapshot(position).getId(), note.getNote_image_url());
checkIflayoutMustImprove();
//setAlarms
if (note.getRemindeDate() != null) {
if (note.getRemindeDate().before(new Date())) {
note.setRemindeDate(null);
}
if (note.getRemindeDate() != null) {
Intent i = new Intent(context, TodoNotificationService.class);
i.putExtra(TodoNotificationService.TODOUUID, note.getId());
i.putExtra(TodoNotificationService.TODOTEXT, note.getTitle());
i.putExtra(TodoNotificationService.TODOCONTENT, note.getContent());
MainActivity.createAlarm(context, i, getSnapshots().getSnapshot(position).getId().hashCode(), note.getRemindeDate().getTime());
}
}
if (note.getRemindeDate() == null) {
Intent i = new Intent(context, TodoNotificationService.class);
int id = getSnapshots().getSnapshot(position).getId().hashCode();
MainActivity.deleteAlarm(context, i, id);
}
}
#Override
public int getItemCount() {
return getSnapshots().size();
}
#NonNull
#Override
public NoteViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_note_view, parent, false);
return new NoteViewHolder(view);
}
public void removeItem(int position) {
getSnapshots().getSnapshot(position).getReference().delete();
notifyItemRemoved(position);
checkIflayoutMustImprove();
}}
So thanks for all your help, but no one couldn't answer this. I coded an immersiveMode() in my App. He sets the screen to fullscreen even with any interactions. I noticed that all bugs I ever had with recyclerview were gone as I removed the immersiveMode() methods. I couldn't say why, but now it works. But thanks to everyone. :)
Put this line
adapter.notifyDataSetChanged();
inside your
addOnCompleteListener()
so your onCompleteListener will look like this
addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
mFirebaseAnalytics.logEvent(NOTE_DELETED_EVENT, null);
adapter.notifyDataSetChanged();
}
});
if that doesn't work than you should publish you adapter class also.
Related
I want to open the data of a Firebase subcollection in a new RecyclerView. The Main Activity has a RecyclerView filled with the data from the first collection and a second activity should be opened through a click on one of the items.This second activity would have the second RecyclerView. My main Activity works fine and I can see all my data. But when I click on one of the items, my app crashes.
This is my MainActivity
private FirebaseFirestore db = FirebaseFirestore.getInstance();
private CollectionReference notebookRef = db.collection("Notebook");
private KisteAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FloatingActionButton buttonAddKiste = findViewById(R.id.button_add_kiste);
buttonAddKiste.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, NewKisteActivity.class));
}
});
setUpRecyclerView();
}
private void setUpRecyclerView(){
Query query = notebookRef.orderBy("priority", Query.Direction.DESCENDING);
FirestoreRecyclerOptions<Kiste> options = new FirestoreRecyclerOptions.Builder<Kiste>()
.setQuery(query, Kiste.class)
.build();
adapter = new KisteAdapter(options);
final RecyclerView recyclerView = findViewById(R.id.recycler_view_kiste);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT |ItemTouchHelper.RIGHT) {
#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) {
adapter.deleteKiste(viewHolder.getAdapterPosition());
final DocumentReference docRef = adapter.getSnapshots().getSnapshot(viewHolder.getAdapterPosition()).getReference();
final Kiste kiste = adapter.getSnapshots().getSnapshot(viewHolder.getAdapterPosition()).toObject(Kiste.class);
Snackbar.make(recyclerView, "Item deleted", Snackbar.LENGTH_LONG)
.setAction("Undo", new View.OnClickListener() {
#Override
public void onClick(View v) {
docRef.set(kiste);
}
}).show();
}
#Override
public void onChildDraw(#NonNull Canvas c, #NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
new RecyclerViewSwipeDecorator.Builder(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
.addBackgroundColor(ContextCompat.getColor(MainActivity.this, R.color.colorAccent))
.addActionIcon(R.drawable.ic_delete_)
.create()
.decorate();
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
}).attachToRecyclerView(recyclerView);
adapter.setOnKisteClickListen(new KisteAdapter.onItemClickListener() {
#Override
public void onKisteClick(DocumentSnapshot documentSnapshot, int position) {
final DocumentReference docRef = documentSnapshot.getReference();
final CollectionReference colRef = documentSnapshot.getReference().collection("A");
final Intent intent = new Intent(MainActivity.this,SecondaryActivity.class);
colRef.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
if (queryDocumentSnapshots.isEmpty()){
colRef.add(new Item("DFB","SGRDG",5));
/*intent.putExtra("Document Reference",docRef.getId());
intent.putExtra("Collection Reference",colRef.getId());*/
startActivity(intent);
}
else {
startActivity(intent);
}
}
});
}
});
}
#Override
protected void onStart() {
super.onStart();
adapter.startListening();
}
#Override
protected void onStop() {
super.onStop();
adapter.stopListening();
}
}
this is the Class Kiste, which the first RecyclerView gets
public class Kiste {
private String title;
private int priority;
public Kiste(){
}
public Kiste(String title, int priority){
this.title= title;
this.priority = priority;
}
public String getTitle() {
return title;
}
public int getPriority() {
return priority;
}
}
And this is the adapter
public class KisteAdapter extends FirestoreRecyclerAdapter<Kiste, KisteAdapter.KisteHolder> {
private onItemClickListener listener;
public KisteAdapter(#NonNull FirestoreRecyclerOptions<Kiste> options) {
super(options);
}
#Override
protected void onBindViewHolder(#NonNull KisteHolder holder, int position, #NonNull Kiste model) {
holder.textViewTitle.setText(model.getTitle());
holder.textViewPriority.setText(String.valueOf(model.getPriority()));
}
#NonNull
#Override
public KisteHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.kiste_item,parent,false);
return new KisteHolder(v);
}
public void deleteKiste(int position){
getSnapshots().getSnapshot(position).getReference().delete();
}
class KisteHolder extends RecyclerView.ViewHolder{
TextView textViewTitle;
TextView textViewPriority;
public KisteHolder(#NonNull View itemView) {
super(itemView);
textViewTitle = itemView.findViewById(R.id.text_view_title);
textViewPriority = itemView.findViewById(R.id.text_view_priority);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && listener!= null){
listener.onKisteClick(getSnapshots().getSnapshot(position),position);
}
}
});
}
}
public interface onItemClickListener {
void onKisteClick(DocumentSnapshot documentSnapshot, int position);
}
public void setOnKisteClickListen (onItemClickListener listener) {this.listener = listener;}
}
Since that worked for me, I tried doing the same with the subcollection (As I was still testing if it would work, I just used one DocumentPath that I knew existed ) but as I said above the app keeps crashing.
Here is the secondActivity
public class SecondaryActivity extends AppCompatActivity {
private FirebaseFirestore db = FirebaseFirestore.getInstance();
private CollectionReference colRef = db.collection("Notebook").document(
"JGBzWBwCASivfAjuF8xE").collection("A");
private ItemAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_secondary);
FloatingActionButton buttonAddItem = findViewById(R.id.button_add_item);
setupRecyclerView();
}
private void setupRecyclerView(){
Query query2 = colRef.orderBy("priority", Query.Direction.DESCENDING);
FirestoreRecyclerOptions<Item> options = new FirestoreRecyclerOptions.Builder<Item>()
.setQuery(query2, Item.class)
.build();
adapter = new ItemAdapter(options);
final RecyclerView recyclerView = findViewById(R.id.recycler_view_item);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
}
#Override
protected void onStart() {
super.onStart();
adapter.startListening();
}
#Override
protected void onStop() {
super.onStop();
adapter.stopListening();
}
}
the Class Item:
public class Item {
//private Bitmap imageItem;
private String itemName;
private String itemQuantity;
private int priority;
public Item(){
}
public Item( String itemName, String itemQuantity, int priority){
//this.imageItem = imageViewItem;
this.itemName = itemName;
this.itemQuantity = itemQuantity;
this.priority = priority;
}
/*public Bitmap getImageItem() {
return imageItem;
}*/
public String getItemName() {
return itemName;
}
public String getItemQuantity() {
return itemQuantity;
}
public int getPriority() {
return priority;
}
}
and the adapter :
public class ItemAdapter extends FirestoreRecyclerAdapter<Item, ItemAdapter.ItemHolder> {
public ItemAdapter(#NonNull FirestoreRecyclerOptions<Item> options) {
super(options);
}
#Override
protected void onBindViewHolder(#NonNull ItemHolder holder, int position, #NonNull Item model) {
//holder.imageViewItem.setImageBitmap(model.getImageItem());
holder.textViewTitle.setText(model.getItemName());
holder.textViewQuantity.setText(model.getItemQuantity());
holder.textViewPriority.setText(String.valueOf(model.getPriority()));
}
#NonNull
#Override
public ItemHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_item,parent,false);
return new ItemHolder(v);
}
class ItemHolder extends RecyclerView.ViewHolder{
//ImageView imageViewItem;
TextView textViewTitle;
TextView textViewQuantity;
TextView textViewPriority;
public ItemHolder(#NonNull View itemView){
super(itemView);
//imageViewItem.findViewById(R.id.image_view_item);
textViewTitle.findViewById(R.id.text_view_item_name);
textViewQuantity.findViewById(R.id.text_view_item_quantity);
textViewPriority.findViewById(R.id.text_view_priority2);
}
}
}
I would really appreciate it if someone could help me figure out what the problem is.
Thanks for pointing out the error in the console, now it's much easier for us to see where the problem is without the need of reading your whole code :D
You're getting an error in line 47 of your ItemAdapter class. I can't see exactly which line it is, but I'm certain the error is inside your ViewHolder.
class ItemHolder extends RecyclerView.ViewHolder{
//ImageView imageViewItem;
TextView textViewTitle;
TextView textViewQuantity;
TextView textViewPriority;
public ItemHolder(#NonNull View itemView){
super(itemView);
//imageViewItem.findViewById(R.id.image_view_item);
THESE LINES OF CODE -----------------------------------------------------------------
textViewTitle.findViewById(R.id.text_view_item_name);
textViewQuantity.findViewById(R.id.text_view_item_quantity);
textViewPriority.findViewById(R.id.text_view_priority2);
-------------------------------------------------------------------------------------
}
}
replace them with:
this.textViewTitle = (TextView) itemView.findViewById(R.id.text_view_item_name); // and so on...
You can check out this example:
https://www.javatpoint.com/android-recyclerview-list-example
I'am creating a conference application for which i get the conference event details as JSON.iam using retrofit for network calling.
based on the JSON data,iam populating my recyclerview accordingly.basically it is a horizontal recyclerview inside a vertical recyclerview.what iam trying to achieve is to group all the events that fall on the same time in horizontal recyclerview and others on vertical recycler view.please help me in acheiving this.below is my code for vertical and horizontal adapters
VERTICAL ADAPTER
public class VerticalAdapter extends RecyclerView.Adapter {
private ArrayList<EventModel> mEventList;
private Context mContext;
private Map<Integer, Parcelable> scrollStatePositionsMap = new HashMap<>();
private static final String TAG = VerticalAdapter.class.getSimpleName();
private ArrayList<EventModel> tempList;
public VerticalAdapter(ArrayList<EventModel> mEventList, Context mContext) {
this.mEventList = mEventList;
this.mContext = mContext;
this.tempList = new ArrayList<>();
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
VerticalItemBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.vertical_item, parent, false);
return new EventViewHolder(binding);
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(mContext);
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
((EventViewHolder) holder).mBinding.horizontalGridView.setLayoutManager(linearLayoutManager);
((EventViewHolder) holder).mBinding.horizontalGridView.setAdapter(new HorizontalAdapter(mEventList, mContext));
((EventViewHolder) holder).setPosition(position);
if (scrollStatePositionsMap.containsKey(position)) {
((EventViewHolder) holder).mBinding.horizontalGridView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
((EventViewHolder) holder).mBinding.horizontalGridView.getViewTreeObserver().removeOnPreDrawListener(this);
((EventViewHolder) holder).mBinding.horizontalGridView.getLayoutManager().onRestoreInstanceState(scrollStatePositionsMap.get(position));
return false;
}
});
}
}
#Override
public int getItemCount() {
return mEventList.size();
}
private class EventViewHolder extends RecyclerView.ViewHolder {
public int position;
private VerticalItemBinding mBinding;
public EventViewHolder(VerticalItemBinding binding) {
super(binding.getRoot());
this.mBinding = binding;
mBinding.horizontalGridView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
scrollStatePositionsMap.put(position, recyclerView.getLayoutManager().onSaveInstanceState());
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
});
}
public void setPosition(int position) {
this.position = position;
}
}
}
HORIZONTAL ADAPTER
public class HorizontalAdapter extends RecyclerView.Adapter {
private ArrayList<EventModel> mEventList;
private Context mContext;
private static String TAG = HorizontalAdapter.class.getSimpleName();
public HorizontalAdapter(ArrayList<EventModel> mEventList, Context mContext) {
this.mEventList = mEventList;
this.mContext = mContext;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
HorizontalItemBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.horizontal_item, parent, false);
return new EventViewHolder(binding);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
StartsOn startsOn = mEventList.get(position).getStartsOn();
EndsOn endsOn = mEventList.get(position).getEndsOn();
((EventViewHolder) holder).mBinding.textTime.setText(startsOn.getDate().substring(11, 16));
((EventViewHolder) holder).mBinding.textEndtime.setText(endsOn.getDate().substring(11, 16));
((EventViewHolder) holder).mBinding.setEvents(mEventList.get(position));
}
#Override
public int getItemCount() {
return mEventList.size();
}
private class EventViewHolder extends RecyclerView.ViewHolder {
private HorizontalItemBinding mBinding;
public EventViewHolder(HorizontalItemBinding binding) {
super(binding.getRoot());
this.mBinding = binding;
}
}
}
MY NETWORK CALL
NetworkInterface networkInterface = retrofit.create(NetworkInterface.class);
Call<ArrayList<EventModel>> call = networkInterface.getEvents(query);
Log.d(TAG, "populateFirstEvent: making call to "+query);
call.enqueue(new Callback<ArrayList<EventModel>>() {
#Override
public void onResponse(Call<ArrayList<EventModel>> call, Response<ArrayList<EventModel>> response) {
if (response.isSuccessful()) {
mBinding.verticalList.setVisibility(View.VISIBLE);
mBinding.emptyView.setVisibility(View.GONE);
mEventList.addAll(response.body());
mBinding.verticalList.setAdapter(new HorizontalAdapter(mEventList,getApplicationContext()));
mBinding.verticalList.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
}
}
#Override
public void onFailure(Call<ArrayList<EventModel>> call, Throwable t) {
Log.d(TAG, "onFailure: " + t.getLocalizedMessage());
mBinding.verticalList.setVisibility(View.GONE);
mBinding.emptyView.setVisibility(View.VISIBLE);
}
});
You could use single RecycleView and single Adapter. Use GridLayoutManager as layout manager for your RecylcerView. Take a look on its constructor
GridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout)
Take HORIZONTAL as orientation and spanCount is number of rows - "group all the events that fall on the same time". So you should sort your events. If there different events number for different rows - fill gaps with some fake empty events.
You should put RecycleView into ScrollView to make it scroll vertically.
I have a recyclerview. It loads when it scrolls. But when I scroll it from top to bottom continuously, it crashes (or closes) after a while .
Error :
Cannot scroll to position a LayoutManager set. Call setLayoutManager with a non-null argument.
E: No adapter attached; skipping layout
A: art/runtime/indirect_reference_table.cc:76] Check failed: table_mem_map_.get() != nullptr ashmem_create_region failed for 'indirect ref table': Too many open files
E: Cannot scroll to position a LayoutManager set. Call setLayoutManager with a non-null argument.
A: art/runtime/indirect_reference_table.cc:76] Check failed: table_mem_map_.get() != nullptr ashmem_create_region failed for 'indirect ref table': Too many open files
BookSearchResultListAdapter.java
public class BookSearchResultListAdapter extends RecyclerView.Adapter<BookSearchResultListAdapter.ViewHolder> implements View.OnClickListener{
private ArrayList<ProductModel> productList;
private Fragment fragment;
private RecyclerView recyclerView;
private int position = 0;
public BookSearchResultListAdapter(Fragment fragment, ArrayList<ProductModel> productList, RecyclerView recyclerView) {
this.productList = productList;
this.fragment = fragment;
this.recyclerView = recyclerView;
}
#Override
public void onClick(View v) {
int position = recyclerView.getChildLayoutPosition(v);
((BookSearchResultAdapterListener)fragment).onProductItemClicked(productList.get(position));
}
private void setOnScrollListener() {
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if(recyclerView.getLayoutManager() != null){
LinearLayoutManager lm= (LinearLayoutManager) recyclerView.getLayoutManager();
int Lastposition = lm.findLastCompletelyVisibleItemPosition();
if(Lastposition == productList.size()-1 && Lastposition != position){
position = lm.findLastCompletelyVisibleItemPosition();
((BookSearchResultAdapterListener)fragment).onListScrolled();
}
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
});
}
public void addCollectionToList(ArrayList<ProductModel> productList){
this.productList.addAll(productList);
notifyItemRangeChanged(0, productList.size() - 1);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public TextView productName;
public TextView manufacturerName;
public TextView publisherName;
public ImageView image;
public ImageView isShelvedIcon;
public ViewHolder(View view) {
super(view);
}
}
#Override
public BookSearchResultListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.component_library_product, parent, false);
ViewHolder holder = new ViewHolder(v);
holder.productName = (TextView) v.findViewById(R.id.productName);
holder.manufacturerName = (TextView)v.findViewById(R.id.pageNumber);
holder.publisherName = (TextView)v.findViewById(R.id.publisherName);
holder.image = (ImageView)v.findViewById(R.id.productImage);
holder.isShelvedIcon = (ImageView)v.findViewById(R.id.isShelvedIcon);
holder.productName.setTypeface(App.MUSEO_300);
holder.manufacturerName.setTypeface(App.MUSEO_300);
holder.publisherName.setTypeface(App.MUSEO_300);
holder.isShelvedIcon.setVisibility(View.GONE);
v.setOnClickListener(this);
setOnScrollListener();
return holder;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
final ProductModel productModel = productList.get(position);
holder.productName.setText(productModel.getName());
holder.manufacturerName.setText(productModel.getManufacturer());
holder.publisherName.setText(productModel.getPublisher());
Picasso.Builder builder = new Picasso.Builder(fragment.getContext());
Picasso picasso = builder.build();
picasso.with(fragment.getContext()).load(productModel.getImage()).into(holder.image);
}
#Override
public int getItemCount() {
return productList.size();
}
public interface BookSearchResultAdapterListener {
void onProductItemClicked(ProductModel productModel);
void onListScrolled();
}
}
I found solution.It is related with Picasso library. Just change the code below :
Picasso.Builder builder = new Picasso.Builder(fragment.getContext());
Picasso picasso = builder.build();
picasso.with(fragment.getContext()).load(productModel.getImage()).into(holder.image);
to :
Picasso.with(fragment.getContext()).load(productModel.getImage()).into(holder.image);
hey guys Im trying to use swipe inside onBindViewHolder because my items are from the database but I think it doesnt seem to work cuz my app is crashing. Im using this custom cursor adapter for my recyclerview https://gist.github.com/skyfishjy/443b7448f59be978bc59 here is my code.
#Override
public void onBindViewHolder(ItemViewHolder viewHolder, Cursor cursor) {
mItems = cursor;
final int id = cursor.getInt(cursor.getColumnIndex(MyDBHandler.COLUMN_ID));
final String title = cursor.getString(cursor.getColumnIndex(MyDBHandler.COLUMN_TITLE_REMINDER));
final String desc = cursor.getString(cursor.getColumnIndex(MyDBHandler.COLUMN_DESC_REMINDER));
final String date = cursor.getString(cursor.getColumnIndex(MyDBHandler.COLUMN_DATE_REMINDER));
viewHolder.title.setText(title);
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, String.valueOf(id), Toast.LENGTH_SHORT).show();
}
});
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 swipeDir) {
int itemPosition = viewHolder.getAdapterPosition();
notifyItemRemoved(itemPosition);
dbHandler.deleteReminder(id);
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
itemTouchHelper.attachToRecyclerView(this.myRecyclerview);
}
Is there anyway I can do swiping to dismiss my items in recyclerview?
Remove the following chunk of code from onBindViewHolder and add in your activity or fragment from where you are initializing adapter.
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 swipeDir) { // type cast your view holder
// CusrsorViewHolder cViewHolder = (CursorViewHolder)viewHolder;
int itemPosition = viewHolder.getAdapterPosition();
notifyItemRemoved(itemPosition);
dbHandler.deleteReminder(cViewHolder.id);
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
itemTouchHelper.attachToRecyclerView(this.myRecyclerview);
//put the above code before the following method in your activity or fragment
//this.RecylerView.setAdapter(adapter)
Set your id in onBindViewHolder
#Override
public void onBindViewHolder(ItemViewHolder viewHolder, Cursor cursor) {
mItems = cursor;
final int id = cursor.getInt(cursor.getColumnIndex(MyDBHandler.COLUMN_ID));
viewHolder.id = id;
}
thanks to #Waleed Sarwar for helping me solve it.
I was using https://gist.github.com/skyfishjy/443b7448f59be978bc59 for my recyclerviewcursoradapter.
MainActivity.java
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 swipeDir) {
RemindersAdapter.ItemViewHolder itemViewHolder = (RemindersAdapter.ItemViewHolder)viewHolder;
int itemPosition = itemViewHolder.getAdapterPosition();
adapter.notifyItemRemoved(itemPosition);
// get the id of an item via itemViewHolder.id
dbHandler.deleteReminder(itemViewHolder.id);
// update cursor upon deleting do avoid
// the card from coming back upon swipe
adapter.swapCursor(dbHandler.getAllReminders());
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
itemTouchHelper.attachToRecyclerView(listReminder);
RemindersAdapter.java
#Override
public void onBindViewHolder(ItemViewHolder viewHolder, Cursor cursor) {
final int id = cursor.getInt(cursor.getColumnIndex(MyDBHandler.COLUMN_ID));
final String title = cursor.getString(cursor.getColumnIndex(MyDBHandler.COLUMN_TITLE_REMINDER));
final String desc = cursor.getString(cursor.getColumnIndex(MyDBHandler.COLUMN_DESC_REMINDER));
final String date = cursor.getString(cursor.getColumnIndex(MyDBHandler.COLUMN_DATE_REMINDER));
viewHolder.title.setText(title);
// pass id to viewholder to get in swipe
viewHolder.id = id;
}
public class ItemViewHolder extends RecyclerView.ViewHolder{
public int id;
TextView title;
public ItemViewHolder(View itemView) {
super(itemView);
title = (TextView) itemView.findViewById(R.id.reminderTitle);
}
}
Im using adapter.swapCursor(dbHandler.getAllReminders()); from my adapter which was a method from abstract class CursorRecyclerViewAdapter that makes the cursor updated upon deleting because without it, the deleted item will be recreated by onBindViewHolder.
Use the below code to delete the item from the list. orderlist is the RecyclerView variable.
SwipeDismissRecyclerViewTouchListener touchListener =
new SwipeDismissRecyclerViewTouchListener(
orderlist,
new SwipeDismissRecyclerViewTouchListener.DismissCallbacks() {
#Override
public boolean canDismiss(int position) {
return true;
}
#Override
public void onDismiss(RecyclerView recyclerView, int[] reverseSortedPositions) {
for (int position : reverseSortedPositions) {
/*db.deleteOrderProduct(ordereditems.get(position).getOrder_pro_Code());
if(position>1){
listposition = position-1;
}*/
//ordereditems.remove(position);
//adapter.notifyItemRemoved(position);
//adapter.notifyDataSetChanged();
//displayOrderList();
DeleteOrderById(position);
}
adapter.notifyDataSetChanged();
}
});
orderlist.setOnTouchListener(touchListener);
I have an ArrayList<String> with 6 items {"1", "2", "3", "4", "5", "6"}.
I am representing these items with a CardView. I want to remove the items with swiping left.
It looks like this: http://imgur.com/cuWoiB3
When I swipe right from number 2, it looks like this: http://imgur.com/5E9fwP0
If I remove Number 5, it removes number 2. If I remove number 3, it removes number 1. It seems to be completely random.
Am I missing something here? How do I fix this?
Relevant code:
My adapter:
public class ChoresAdapter extends RecyclerView.Adapter<ChoresAdapter.ChoreViewHolder>{
private ArrayList<String> chores;
public ChoresAdapter(ArrayList<String> chores){
this.chores = chores;
}
#Override
public ChoreViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.single_cardview, parent, false);
v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
ChoreViewHolder pvh = new ChoreViewHolder(v);
return pvh;
}
#Override
public void onBindViewHolder(ChoreViewHolder holder, int position) {
holder.choreName.setText(this.chores.get(position));
}
#Override
public int getItemCount() {
return this.chores.size();
}
public void removeAt(int position) {
this.chores.remove(position);
notifyItemRemoved(position);
notifyDataSetChanged();
}
public static class ChoreViewHolder extends RecyclerView.ViewHolder {
CardView cv;
public static TextView choreName;
public static TextView personAge;
public static ImageView personPhoto;
ChoreViewHolder(View itemView) {
super(itemView);
cv = (CardView)itemView.findViewById(R.id.cv);
choreName = (TextView)itemView.findViewById(R.id.chore_name);
}
}
}
Relevant code in my fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_chores, container, false);
RecyclerView rv = (RecyclerView)view.findViewById(R.id.rv);
rv.setHasFixedSize(true);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
rv.setLayoutManager(llm);
chores = new ArrayList<>();
chores.add("1");
chores.add("2");
chores.add("3");
chores.add("4");
chores.add("5");
chores.add("6");
adapter = new ChoresAdapter(chores);
SwipeableRecyclerViewTouchListener swipeTouchListener =
new SwipeableRecyclerViewTouchListener(rv,
new SwipeableRecyclerViewTouchListener.SwipeListener() {
#Override
public boolean canSwipe(int position) {
return true;
}
#Override
public void onDismissedBySwipeLeft(RecyclerView recyclerView, int[] reverseSortedPositions) {
for (int position : reverseSortedPositions) {
adapter.removeAt(position);
}
}
#Override
public void onDismissedBySwipeRight(RecyclerView recyclerView, int[] reverseSortedPositions) {
for (int position : reverseSortedPositions) {
adapter.removeAt(position);
}
}
});
rv.addOnItemTouchListener(swipeTouchListener);
rv.setAdapter(adapter);
return view;
}
Its bit late, I just fall into the same situation and got answer, Just use
chores.remove(position);
adapter.notifyDataSetChanged();
You dont need to do that on the adapter class.
You can use below code (ItemTouchHelper.SimpleCallback from Android support V7) to remove the cards from RecyclerView using swipe gesture. You need to remove element from your ArrayList as well when you swipe it from RecyclerView.
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 swipeDir) {
chores.remove(viewHolder.getAdapterPosition());
adapter.notifyItemRemoved(viewHolder.getAdapterPosition());
}
#Override
public void onMoved(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, int fromPos, RecyclerView.ViewHolder target, int toPos, int x, int y) {
super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y);
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
itemTouchHelper.attachToRecyclerView(recList);