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.
Related
I'm desperate.
I have a nested recycler view. Each outer element has an array of inner elements. A different adapter has been created for the inner elements. I am creating an array of external elements of class "KairosWithEvents", each of which contains internal elements of class "Event". Everything is displayed well. When elements are added, everything is also updated. For testing, I created two objects of the "KairosWithEvent" class. In the first object I have placed two objects of the "Event" class, and in the second - three objects. But when I want to change the EditText value, the keyboard appears. And the last element of the second object appears in the first object. How can I fix it? Objects are not moved or duplicated, but showed incorrectly.
This is what a nestled recycler view looks like initially.
And this is what a nested recycler view looks like after the keyboard appears. The "Эвент5" element is duplicated to the first element for some reason.
Here's my code: Outer Adapter:
public class EventAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static List<Item> items;
private static RecyclerViewClickInterface listener;
private RecyclerView.RecycledViewPool sharedPool = new RecyclerView.RecycledViewPool();
private Context context;
public EventAdapter() {
items = new ArrayList<>();
this.context = context;
}
public EventAdapter(List<Item> newItems){
items = newItems;
}
class KairosViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
private TextView title;
private CheckBox iv;
private RecyclerView rv;
public KairosViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.tv);
iv = itemView.findViewById(R.id.iv);
rv = itemView.findViewById(R.id.rvSteaks);
itemView.setBackgroundColor(Color.parseColor("#91b3f2"));
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
#RequiresApi(api = Build.VERSION_CODES.N)
void bind(KairosWithEvents kairosWithEvents){
title.setText(kairosWithEvents.kairos.kairosId + " = " + kairosWithEvents.kairos.title);
LinearLayoutManager layoutManager = new LinearLayoutManager(
rv.getContext(),
LinearLayoutManager.VERTICAL,
false);
List<Item> itemsEvents = new ArrayList<>();
kairosWithEvents.events.forEach(i -> itemsEvents.add(new Item(Constants.EVENT_KAIROS, i)));
SubAdapter childAdapter = new SubAdapter(itemsEvents);
rv.setLayoutManager(layoutManager);
rv.setAdapter(childAdapter);
rv.setRecycledViewPool(sharedPool);
}
#Override
public void onClick(View view) {
int position = getAdapterPosition();
if (listener != null && position != RecyclerView.NO_POSITION){
listener.onItemKairosWithEvents( ((KairosWithEvents) items.get(position).object) );
}
}
#Override
public boolean onLongClick(View view) {
return false;
}
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
switch (viewType){
case Constants.KAIROS:
return new KairosViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item_ex, parent, false));
}
return null;
}
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)){
case Constants.KAIROS:
KairosWithEvents kairos = ((KairosWithEvents) items.get(position).object);
((KairosViewHolder) holder).bind(kairos);
break;
}
}
#Override
public int getItemCount() {
return items.size();
}
#Override
public int getItemViewType(int position) {
return items.get(position).type;
}
}
Inner Adapter:
public class SubAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private static List<Item> items;
public SubAdapter(List<Item> newItems) {
items = newItems;
}
static class EventViewHolder extends RecyclerView.ViewHolder {
private TextView title;
public EventViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.tv);
itemView.setBackgroundColor(Color.parseColor("#ffc8a8"));
}
void bind(Event event){
title.setText(event.eventId + " = " + event.title);
}
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
switch (viewType) {
case Constants.EVENT_KAIROS:
return new EventViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item_sub, parent, false));
}
return null;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)){
case Constants.EVENT_KAIROS:
Event event = (Event) items.get(position).object;
((EventViewHolder) holder).bind(event);
break;
}
}
#Override
public int getItemCount() {
return items.size();
}
#Override
public int getItemViewType(int position) {
return items.get(position).type;
}
}
In SubAdapter, remove the static keyword from your items field:
private static List<Item> items;
Should be this instead:
private List<Item> items;
You should make the same change to your outer adapter, but since there's only one of them at a time it doesn't wind up causing issues.
How do you use DiffCallback to load a newList in RecyclerView when DiffUtil ItemCallback is being used.
I would like to give the user the option to return different size lists from the database when the user selects a different size I want the RecyclerView to update.
RecyclerViewAdatper
RecyclerViewAdapter extends ListAdapter<WordEntity, RecyclerViewAdapter.ViewHolder> {
private RecyclerViewAdapter() {
super(DIFF_CALLBACK);
}
private static final DiffUtil.ItemCallback<WordEntity> DIFF_CALLBACK = new DiffUtil.ItemCallback<WordEntiti>() {
#Override
public boolean areItemsTheSame...
#Override
public boolean areContentsTheSame...
};
#Override
public viewHolder onCreateViewHolder...
#Override
public void onVindViewHolder ...
class ViewHolder extends RecyclerView.ViewHolder ...
public void updateWordList(List<WordEntity> words) {
final WordDiffCallBack diffCallBack = new WordDiffCallBack(list???, words);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallBack);
this.list???.clear();
this.addAll(words);
diffResult.dispatcheUpdatesTo(this);
}
WordsDiffCallBack
private final List<WordEntity> mOldList;
private final List<WordEntity> mNewList;
public WordsDiffCallBack(List<WordEntity> oldList, List<WordEntity> newList) {
this.mOldList = oldList;
this.mNewList = newList;
}
#Override
public int getOldListSize() {
return mOldList.size();
}
#Override
public int getNewListSize() {
return mNewList.size();
}
#Override
public boolean areItemsTheSame(int OldItemPostion, int newItemPosition) ...
#Override boolean areContentsTheSame(int oldItemPosition, int newItemPosition)...
#Override getChangePayload(int oldItemPosition, int newItemPosition) ...
}
I want the RecycelView to update automatically when the size of the list gets changed by the user. How do I call the old list from the ListAdapter and will that even update the RecyclerView
You can create a template just like shown in this video in youTube:
https://www.youtube.com/watch?v=y31fzLe2Ajw
Here is an example of adapter.
public class CartFragAdapter extends RecyclerView.Adapter<CartFragAdapter.CartFragViewHolder> {
private static final String TAG = "debinf PurchaseAdap";
private static final DiffUtil.ItemCallback<ProductsObject> DIFF_CALLBACK = new DiffUtil.ItemCallback<ProductsObject>() {
#Override
public boolean areItemsTheSame(#NonNull ProductsObject oldProduct, #NonNull ProductsObject newProduct) {
Log.i(TAG, "areItemsTheSame: old is "+oldProduct.getCode()+" ; new is "+newProduct.getCode());
return oldProduct.getCode().equals(newProduct.getCode());
}
#Override
public boolean areContentsTheSame(#NonNull ProductsObject oldProduct, #NonNull ProductsObject newProduct) {
Log.i(TAG, "areContentsTheSame: old is "+oldProduct.getPrice()+" ; new is "+newProduct.getPrice());
return oldProduct.getPrice() == newProduct.getPrice();
}
};
private AsyncListDiffer<ProductsObject> differ = new AsyncListDiffer<ProductsObject>(this, DIFF_CALLBACK);
#NonNull
#Override
public CartFragViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_purchase, parent, false);
return new CartFragViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull CartFragViewHolder holder, int position) {
final ProductsObject purchaseList = differ.getCurrentList().get(position);
holder.mCode.setText(purchaseList.getCode());
holder.mPrice.setText(String.valueOf(purchaseList.getPrice()));
holder.mDescription.setText(purchaseList.getDescription());
}
#Override
public int getItemCount() {
Log.i(TAG, "getItemCount");
return differ.getCurrentList().size();
}
public void submitList(List<ProductsObject> products){
Log.i(TAG, "submitList: products.size is "+products.size());
differ.submitList(products);
}
public class CartFragViewHolder extends RecyclerView.ViewHolder {
public TextView mCode, mPrice, mDescription;
public CartFragViewHolder(#NonNull View itemView) {
super(itemView);
mCode = (TextView) itemView.findViewById(R.id.item_productCode);
mPrice = (TextView) itemView.findViewById(R.id.item_productPrice);
mDescription = (TextView) itemView.findViewById(R.id.item_productDescription);
}
}
}
In MainActivity you call adapter like this:
CartFragAdapter adapter = new CartFragAdapter();
adapter.submitList(inputData);
I hope it helps!
observe this code
here's my diffutil callback
public class MyDiffUtilCallback extends DiffUtil.Callback {
List<String> oldlist;
List<String > newlist;
public MyDiffUtilCallback(List<String> oldlist, List<String> newlist) {
this.oldlist = oldlist;
this.newlist = newlist;
}
#Override
public int getOldListSize() {
return oldlist.size();
}
#Override
public int getNewListSize() {
return newlist.size();
}
#Override
public boolean areItemsTheSame(int olditempostion, int newitemPostion) {
return olditempostion==newitemPostion;
}
#Override
public boolean areContentsTheSame(int olditempostion, int newitemPostion) {
return olditempostion==newitemPostion;
}
#Nullable
#Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
return super.getChangePayload(oldItemPosition, newItemPosition);
}
}
here's the recyclerview adpater which uses diffutilcallback
public class recyclerviewAdapter extends RecyclerView.Adapter<recyclerviewAdapter.Viewholder> {
List<String> datasource;
public recyclerviewAdapter(List<String> datasource) {
this.datasource = datasource;
}
#Override
public Viewholder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.demorow,viewGroup,false);
return new Viewholder(itemView);
}
#Override
public void onBindViewHolder(#NonNull Viewholder viewholder, int i) {
viewholder.tv_demo_data_row.setText(datasource.get(i));
}
#Override
public int getItemCount() {
return datasource.size();
}
public static class Viewholder extends RecyclerView.ViewHolder {
TextView tv_demo_data_row;
public Viewholder(#NonNull View itemView) {
super(itemView);
tv_demo_data_row=itemView.findViewById(R.id.tv_demo_data_row);
}
}
//DIFF CALLBACK STATE
public void insertdata(List<String> insertList){
/**
* Insert list insert data to list
*/
MyDiffUtilCallback diffUtilCallback = new MyDiffUtilCallback(datasource,insertList);
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffUtilCallback);
datasource.addAll(insertList);
diffResult.dispatchUpdatesTo(this);
}
public void updateList(List<String> newList){
/**
* update list clear old data and update new data
*/
MyDiffUtilCallback diffUtilCallback = new MyDiffUtilCallback(datasource,newList);
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffUtilCallback);
datasource.clear();
datasource.addAll(newList);
diffResult.dispatchUpdatesTo(this);
}}
and here i can update list in activity on button clickevent
insert_data.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//inserting the data
List<String> insertlist = new ArrayList<>(); //asign old list
for(int i=0;i<2;i++){
insertlist.add(UUID.randomUUID().toString()); // insert new list
adapter.insertdata(insertlist);
faster_recyclerview.smoothScrollToPosition(adapter.getItemCount()-1); //auto scroll to last postion
}
}
});
replace List of string with your modelclass
And in addition to the above (using DiffUtils), if you use Clicks to change RecyclerView's content, make
ViewHolder implements View.OnClickListener
and use this to set OnClickListener in onBindViewHolder
.setOnClickListener(holder);
and get your current position by
this.getAdapterPosition()
private class HistoryRecyclerAdapter extends RecyclerView.Adapter<HistoryRecyclerAdapter.ViewHolder> {
private List<HistoryEntry> entries;
HistoryRecyclerAdapter(List<HistoryEntry> entries) {
this.entries = entries;
}
public void updateContainer(List<HistoryEntry> list){
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
#Override
public int getOldListSize() { return entries.size(); }
#Override
public int getNewListSize() { return list.size(); }
#Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return entries.get(oldItemPosition).getId()==list.get(newItemPosition).getId();
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
HistoryEntry old = entries.get(oldItemPosition);
HistoryEntry neww = list.get(newItemPosition);
return old.getExpression().equals(neww.getExpression())
&& old.getResult().equals(neww.getResult());
}
}, false);//detectMoves=true потом позволит вызвать анимацию если строка просто была перемещена
entries = list;
diffResult.dispatchUpdatesTo(this);
}
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
//some Views
final Button delBtn;
ViewHolder(View view){
super(view);
//some Views
delBtn = (Button) view.findViewById(R.id.delete_btn);
}
#Override
public void onClick(View v) {
switch (v.getId()){
//some cases
case R.id.delete_btn:
historyManager.delete(entries.get(this.getAdapterPosition()));
break;
}
}
}
#NonNull
#Override
public HistoryRecyclerAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.history_row, parent, false);
return new HistoryRecyclerAdapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(HistoryRecyclerAdapter.ViewHolder holder, final int position) {
//some code
holder.delBtn.setOnClickListener(holder);
}
#Override
public int getItemCount() {
return entries.size();
}
}
Early I used final int position in onBindViewHolder and don't understand what's wrong...)))
Here is snapet of my project (How to view list of my providers):
public class ProviderAdapter extends ListAdapter<Provider, RecyclerView.ViewHolder> {
private final GeneralListener<Provider> listener;
public ProviderAdapter(GeneralListener<Provider> listener) {
super(DIFF_CALLBACK);
this.listener = listener;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new ViewHolder(ItemProviderBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
((ViewHolder) holder).bind(getItem(position));
}
class ViewHolder extends RecyclerView.ViewHolder {
ItemProviderBinding binding;
public ViewHolder(ItemProviderBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
void bind(Provider data) {
binding.image.setImageURI(data.img_url);
binding.txtName.setText(data.name);
binding.txtMealTypes.setText(data.meal_types);
binding.txtAddress.setText(data.address);
binding.btnBack.setOnClickListener(view -> {
listener.onClick(Actions.VIEW, data);
});
}
}
private static final DiffUtil.ItemCallback<Provider> DIFF_CALLBACK = new DiffUtil.ItemCallback<Provider>() {
#Override
public boolean areItemsTheSame(#NonNull Provider oldItem, #NonNull Provider newItem) {
return oldItem.id == newItem.id;
}
#Override
public boolean areContentsTheSame(#NonNull Provider oldItem, #NonNull Provider newItem) {
return oldItem.name.equals(newItem.name)
&& oldItem.meal_types.equals(newItem.meal_types)
&& oldItem.address.equals(newItem.address);
}
};
}
To use it:
ProviderAdapter adapter = new ProviderAdapter(new GeneralListener<Provider>() {
#Override
public void onClick(Actions action, Provider provider) {
if (action == Actions.VIEW) {
// TODO: View provider details
}
}
});
recyclerView.setAdapter(adapter);
adapter.submitList(providerList);
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.
Prompt, faced with scrollToPosition speed. I RecyclerView 900 positions and need to quickly establish a position as scrollToPosition very slow, c ListView such problems were not setSelection working out perfectly. It is possible to accelerate the scrollToPosition?
public class EventAdapter extends RecyclerView.Adapter<EventAdapter.Holder> {
final static public String TAG = "EventAdapter";
Context context;
List<Event> items;
public EventAdapter(List<Event> items) {
this.items = items;
}
#Override
public EventAdapter.Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list_event, parent, false);
context = parent.getContext();
return new EventAdapter.Holder(v);
}
#Override
public void onBindViewHolder(EventAdapter.Holder holder, int position) {
holder.setModel(context, items.get(position), position);
}
#Override
public int getItemCount() {
return items.size();
}
public class Holder extends RecyclerView.ViewHolder {
public View block;
Holder(View view) {
super(view);
block = (View) view.findViewById(R.id.cv);
}
public void setModel(Context context, Event model, int position) {
}
}
}
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
rv.setLayoutManager(linearLayoutManager);
rv.setHasFixedSize(true);
linearLayoutManager.scrollToPosition(index);
There are couple of ways to do it:
1) Try smoothScrollToPosition as : [will certainly work]
Toast.makeText(this, "Scroll...", Toast.LENGTH_SHORT).show();
myList.smoothScrollToPosition(0);
2) Also read about scrollToPositionWithOffset as this may help too
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);