I'm trying to implement swipe to archive note in RecyclerView.
It was working fine but after I added these codes to refresh the RecyclerView from onResume(), Swiping although does archive the Note, but the item doesn't get removed and stays at a state you can see in image below:
This is what I do in onResume() :
#Override
protected void onResume() {
super.onResume();
notes = noteDAO.getAllNotes();
noteAdapter = new NoteAdapter(notes,this);
recyclerView.setAdapter(noteAdapter);
}
ItemTouchHelper onSwiped():
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
noteAdapter.deleteItem(position,rv);
}
deleteItem method in Adapter:
public void deleteItem(int position, RecyclerView rv) {
noteDAO = DBInjector.provideNoteDao(context);
recentlyDeletedNote = notes.get(position);
recentlyDeletedNotePosition = position;
recentlyDeletedNote.setArchive(true);
notes.remove(position);
noteDAO.archiveNote(recentlyDeletedNote);
notifyItemRemoved(position);
}
I have tried a lot of solutions, the only reason why it is not updating the view is that on updating recycler views, views are getting updated especially when ItemTouchHelper is used. As I didn't have much choice I used
recreate()
function for refreshing the whole activity and the error went off.
PS: This is not the ideal solution, it is just workaround fix.
Related
I've got a button and a recycler view. The button refreshes the list. I want to animate one of the text views in my recycler view when it gets updated. Not the whole recyclerview, not the whole row - just one view (in every row).
I tried putting the animation in onBindViewHolder. But this starts the animation on scrolling and when i add a list entry:
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
Animation animation = AnimationUtils.loadAnimation(context, R.anim.rv_animation_clockwise);
((ViewHolderItem)holder).tv.startAnimation(animation);
Then i tried adding a TextChangedListener to my text view. But this has the same effect as putting it straight into onBindViewHolder:
((ViewHolderItem)holder).tv.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
My last attempt was using findViewHolderForAdapterPosition. But it just doesn't do anything.
This is the refresh method which my button calls. It's in the RecyclerViewAdapter.
recyclerview is an instance variable which i set in onAttachedToRecyclerView:
RecyclerView recyclerView;
#Override
public void onAttachedToRecyclerView(#NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
this.recyclerView = recyclerView;
}
void refresh (List<Entry> al){
this.al = al;
notifyDataSetChanged();
((ViewHolderItem)recyclerView.findViewHolderForAdapterPosition(0)).tv.startAnimation(animation);
bump
You should implement your own ItemAnimator and set it to your RecyclerView. There is some useful information here: https://hackmd.io/#nesquena/r1IEQ-jAl?type=view
Thanks, i'll check out ItemAnimator later.
What i ended up doing yesterday was to add in a small delay between my adapter.refresh() and the animation. Like they suggested here. Weirdly this delay seems to be necessary even if notifyDataSetChanged(); is called after the animation. I don't think it's a good solution but it works for now.
final Animation animation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.rv_animation_clockwise);
recyclerView.postDelayed(new Runnable()
{
#Override
public void run()
{
for (int i = 0; i < al.size(); i++) {
if (recyclerView.findViewHolderForAdapterPosition(i)!=null) {
((RecyclerViewAdapter.ViewHolderItem) recyclerView.findViewHolderForAdapterPosition(i)).tv.startAnimation(animation);
}
}
}
},50);
I'm using firebase-ui to retrieve from firebase and I want to remove from displaying all the status that are equal to unlive I don't want to delete it from firebase database but only removed it from displaying on the recyclerview tried also doing the solution Cannot call this method while RecyclerView is computing a layout or scrolling when try remove item from recyclerview here still encountering the error. Please help I'm stuck here for days. Can't find any solution. Thanks
mAdapter = new FirebaseRecyclerAdapter<Hotels_Model, Adapter_HotelsHolder>(mOptions) {
#Override
protected void onBindViewHolder(Adapter_HotelsHolder holder,int position, Hotels_Model model) {
String status = model.getStatus();
if (status.equals("unlive")) {
mAdapter.notifyItemRemoved(holder.getAdapterPosition());
mAdapter.notifyItemRangeChanged(holder.getAdapterPosition(), getItemCount());
}
}
This is my error if I scroll to the position where the status is unlive
Cannot call this method while RecyclerView is computing a layout or scrolling android.support.v7.widget.RecyclerView
So you can do it easily in that way:
mAdapter = new FirebaseRecyclerAdapter<Hotels_Model, Adapter_HotelsHolder>(mOptions) {
#Override
protected void onBindViewHolder(Adapter_HotelsHolder holder,int position, Hotels_Model model) {
String status = model.getStatus();
if (status.equals("unlive")) {
holder.yourRowView.setVisibility(View.GONE);
}
}
You don't need to remove your items from recyclerview.
I try to update just one specific item after a "basic action" (like a tap on one item) in my recycler view, but the method notifyItemChanged seems to doesn't work as expected.
Actually, the method onBindViewHolder is correctly called, and datas that I want to change in my item is correctly done. BUT, I see nothing changing in my view. I don't understand why...
My code :
- MyFragment
private void initRecyclerView() {
_recyclerView = (RecyclerView) _rootView.findViewById(R.id.recyclerView);
_recyclerView.setHasFixedSize(true);
_layoutManager = new LinearLayoutManager(getActivity());
_recyclerView.setLayoutManager(_layoutManager);
_adapter = new MyAdapter(datas, this, getContext());
_recyclerView.setAdapter(_adapter);
}
When an item is selected, I call "_adapter.update(position)" in my fragment. And so in my adapter I've this :
-Adapter
public void updateItem(int position) {
notifyItemChanged(position);
}
After this call I can see that the method OnBindViewHolder is correctly call, but nothing is changed on the view :(
EDIT : code of onBindViewHolder :
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
MyOBject object = datas.get(position);
holder.text1.setText(object.getValue1());
holder.text2.setText(object.getValue2());
if (object.getLastTimeItemClicked() != null && object.getLastTimeItemClicked().compareTo(object.getLastTimeNewContent()) > 0) {
Log.d("test", "item clicked, content changed")
holder._backgroundItem.setCardBackgroundColor(_context.getResources().getColor(bgItemRead)); // I see this log after a tap on one item, so this code is working!
}
}
EDIT 2 : I'm using tabs, maybe there is something to manage with that ?! (In each fragments linked to each tabs, there is a recycler view etc managed by a fragment)
I use smoothScrollToPosition to scroll RecyclerView. It does scroll every time a new entry is inserted; but to the top, not to the bottom, which is the direction i want.
list_chat = (RecyclerView) findViewById(R.id.list_chat);
//Set up Layout Manager
linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setStackFromEnd(true);
list_chat.setLayoutManager(linearLayoutManager);
//set adapter
list_chat.setAdapter(adapter);
//set scroll
list_chat.post(new Runnable() {
#Override
public void run() {
list_chat.smoothScrollToPosition(adapter.getItemCount());
}
});
The adapter is from Firebase
adapter = new FirebaseRecyclerAdapter<ChatItem, ChatRecylerViewHolder>(ChatItem.class,R.layout.chat_item
,ChatRecylerViewHolder.class,queryChat ) {
#Override
protected void populateViewHolder(ChatRecylerViewHolder viewHolder, ChatItem model, int position) {
viewHolder.tvAuthorChat.setText(model.chatAuthor);
viewHolder.tvContentChat.setText(model.chatContent);
}
};
You do notice you are using linearLayoutManager.setStackFromEnd(true); this mean you first position is at the bottom. I suggest you a better option.
RecycleView didn't work the way listView work, you can scroll it with your layout manager something like this
linearLayoutManager.scrollToPositionWithOffset(position,offset);
Which position is the position you want to scroll to, offset is the offset within the current position. You could just use with one parameter as well.
linearLayoutManager.scrollToPosition(position);
Ok. I found the answer.
First, the old problem with my question: i thought list_chat.post is called whenever an item is inserted (turn out that is wrong). The reason for it keeps scrolling top is linearLayoutManager.setStackFromEnd(true);
Thus, the question comes down to Where to call the scrolling ?
The answer is : Since adapter manages data, it makes sense to guess that adapter will notify the insertion.
Here is the code
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
#Override
public void onItemRangeInserted(int positionStart, int itemCount) {
super.onItemRangeInserted(positionStart, itemCount);
list_chat.smoothScrollToPosition(adapter.getItemCount());
}
});
I have come across an issue when using RecyclerViews. Basically during the initial load of a RecyclerView (after I start an activity) there is a small delay before items appear.
After experimenting for a while I found a way to remove this delay by wrapping my adapter in another class as follows:
public class AdapterWrapper extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private RecyclerView.Adapter<RecyclerView.ViewHolder> mAdapter;
public AdapterWrapper(RecyclerView.Adapter<RecyclerView.ViewHolder> adapter) {
mAdapter = adapter;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return mAdapter.onCreateViewHolder(parent, viewType);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
mAdapter.onBindViewHolder(holder, position);
}
#Override
public int getItemCount() {
return mAdapter.getItemCount();
}
#Override
public int getItemViewType(int position) {
return mAdapter.getItemViewType(position);
}
}
Then in my activity I have this:
protected void onCreate(Bundle savedInstanceState) {
...
setUpRecyclerView();
...
}
public void setUpRecyclerView() {
mAdapter = new MyAdapter(this, mCursor);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
//mRecyclerView.setAdapter(mAdapter); // This causes a small delay.
mRecyclerView.setAdapter(new AdapterWrapper(mAdapter)); // This doesn't
}
This seems really weird to me and I have no idea why the behaviour is different. Has anybody got any potential theory to explain this?
Extra information:
-I'm using a cursor loader to provide data to my adapter.
-My adapter is subclassed using a CursorRecyclerAdapter found at http://quanturium.github.io
You have to override every method, not just implement the abstract methods.
When you call your overriden methods on AdapterWrapper it's getting passed to the wrapped object. Every other method is going to your wrapper and not getting passed on.
If im right, your premise may be a red herring. Delays like this can be—and usually are—because you are performing updates to the ui from a background thread.
We had a similar issue and finally tracked it dowm to that being rhe case. Moving the code back to the main thread, everything became instant again.
Hope this helps!