Am I using RecyclerView correctly? Whenever I update the data, the is one moment in which the view still displays the old data, despite having modified the dataset and having called the relevant notify method. On top of that, I don't see any animations, so I must be doing something wrong.
Here is the relevant snippet of code:
private void refreshData() {
Utils.hideSoftKeyboard(this);
if (!Utils.isOnline(getApplicationContext())) {
Toast.makeText(getApplicationContext(), R.string.toast_no_conn, Toast.LENGTH_SHORT).show();
return;
}
String stopNumber = mStopEdit.getText().toString();
if (stopNumber.isEmpty()) {
Toast.makeText(getApplicationContext(), R.string.toast_no_stop, Toast.LENGTH_SHORT).show();
return;
}
mResultNoStop.setVisibility(View.GONE);
mResults.setVisibility(View.GONE);
mProgressCircle.setVisibility(View.VISIBLE);
if (!mDataset.isEmpty()) {
int size = mDataset.size();
mDataset.clear();
mAdapter.notifyItemRangeRemoved(0, size);
}
FiveT.getStopData(stopNumber, mRequestQueue, new FiveT.StopDataClientListener() {
#Override
public void onResponse(ApiResult result) {
mProgressCircle.setVisibility(View.GONE);
if (result.getStopResults().size() == 0) {
mResultNoStop.setVisibility(View.VISIBLE);
Toast.makeText(getApplicationContext(), R.string.toast_no_data, Toast.LENGTH_SHORT).show();
return;
}
int i = 0;
mStopName.setText(result.getStopName());
for (StopResult res : result.getStopResults()) {
mDataset.add(res);
mAdapter.notifyItemInserted(i++);
}
mResults.setVisibility(View.VISIBLE);
}
});
}
EDIT: I initialize the RecyclerView like this:
mDataset = new ArrayList<StopResult>();
mRecyclerView = (RecyclerView) findViewById(R.id.results_recycler_view);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new RecyclerViewAdapter(mDataset, getApplicationContext());
mRecyclerView.setAdapter(mAdapter);
Based on the question's comments:
What you're missing there, in RecyclerView's initialisation, is the set of a item animator.
According to your code:
mDataset = new ArrayList<StopResult>();
mRecyclerView = (RecyclerView) findViewById(R.id.results_recycler_view);
mRecyclerView.setItemAnimator(new MyItemAnimator());
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new RecyclerViewAdapter(mDataset, getApplicationContext());
mRecyclerView.setAdapter(mAdapter);
You can refer to this library in order to find an animation that it is best for your needs.
Related
We have a traditional ListView interfaced with a RecyclerView Adapter and a Model class. When we log in to Firebase we navigate to the ListView nothing is loaded in the ListView. We then go back to the login activity and repeat the process
Then all the data is loaded from a very simple DB child node Table List
We tired the Firebase RecyclerAdapter with no luck we may have had the wrong UI or version. Our question is why do we need to make two trips to the list view to load the data? We will post the ListView code NO Errors are generated We did try placing the code in an OnStart method no luck with that
db = FirebaseDatabase.getInstance().getReference();
dbRef = db.child("Table List");
dbRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child : dataSnapshot.getChildren())
listArrayList.add(child.getValue(String.class));
sz = listArrayList.size();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
sz = listArrayList.size();
if(sz == 0){
tvNoData.setVisibility(View.VISIBLE);
tvNoData.setText("No Quiz's Found\n\nClick On Make New Quiz");
mRecyclerView = findViewById(R.id.recycleview);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new TableTrackerAdapter(this,listArrayList);
mRecyclerView.setAdapter(mAdapter);
}
if(sz > 0){
tvNoData.setVisibility(View.INVISIBLE);
mRecyclerView = findViewById(R.id.recycleview);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new TableTrackerAdapter(this,listArrayList);
mRecyclerView.setAdapter(mAdapter);
}
The reason for that is because Firebase database is asynchronous, in other words the recyclerview is getting populated with an empty arraylist before firebase is finished loading data, I faced similar problem before.
Just move everything that depends on your arraylist inside firebase datachange, and the problem will be fixed like this.
public void setupRecyclerView(){
if(sz == 0){
tvNoData.setVisibility(View.VISIBLE);
tvNoData.setText("No Quiz's Found\n\nClick On Make New Quiz");
mRecyclerView = findViewById(R.id.recycleview);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(MainActivity.this);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new TableTrackerAdapter(MainActivity.this,listArrayList);
mRecyclerView.setAdapter(mAdapter);
}
if(sz > 0){
tvNoData.setVisibility(View.INVISIBLE);
mRecyclerView = findViewById(R.id.recycleview);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(MainActivity.this);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new TableTrackerAdapter(MainActivity.this,listArrayList);
mRecyclerView.setAdapter(mAdapter);
}
}
db = FirebaseDatabase.getInstance().getReference();
dbRef = db.child("Table List");
dbRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot != null){
for (DataSnapshot child : dataSnapshot.getChildren()){
listArrayList.add(child.getValue(String.class));
}
sz = listArrayList.size();
setupRecyclerView();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I am using a Staggered Grid Layout to show data to recycler view from database.I have faced a problem where after deleting an item from db as well as remove position from adapter, I got some item rendering issues.Like the scattered all over the place.
Here is my code
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_note_favorite);
ButterKnife.bind(this);
toolbar = (Toolbar) findViewById(R.id.fav_note_toolbar);
setSupportActionBar(toolbar);
noteAdapter = new NoteAdapter(noteModelList, NoteFavoriteActivity.this);
layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(noteAdapter);
touchListenerRecycler();
loadDataAll();
}
private void loadAll() {
noteModelList.clear();
DBManagerFav dbManagerFav = DBManagerFav.getInstance(NoteFavoriteActivity.this);
dbManagerFav.openDataBase();
noteModelList = dbManagerFav.getAllNoteList();
Log.i(TAG, " size : " + noteModelList.size());
noteAdapter = new NoteAdapter(noteModelList, NoteFavoriteActivity.this);
recyclerView.setAdapter(noteAdapter);
noteAdapter.notifyDataSetChanged();
dbManagerFav.closeDataBase();
}
private void deleteOperation() {
DBManagerFav dbManagerFav = DBManagerFav.getInstance(NoteFavoriteActivity.this);
dbManagerFav.openDataBase();
NoteModel noteModel = new NoteModel();
noteModel.setId(noteModelList.get(adapterClickedPosition).getId());
int status = dbManagerFav.deleteNote(noteModelList.get(adapterClickedPosition).getId());
if (status > 0) {
noteAdapter.removeAt(adapterClickedPosition);
}
dbManagerFav.closeDataBase();
loadDataAll();
}
//this belongs to adapter
public void removeAt(int position) {
Log.d(TAG, " removing at position : " + position);
noteModelList.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, noteModelList.size());
notifyDataSetChanged();
}
I have attached two screenshots before and after
Before deleting an item
after deleting an item
Can you point me out what else do I need ?
You can put inside loadDataAll(){
layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
//........
}
Voila..
I have a Recycler view with GridLayoutManager attached. By Default, it will display ONE COLUMN and if user will click on button, it will displays in TWO COLUMN.
adapter = new ProductAdapter(getActivity(), productList);
gridLayoutManager = new GridLayoutManager(getActivity(), 1);
setLayoutManager(true);
below is function where i am changing columns
private void setLayoutManager(boolean isList) {
if (isList) {
adapter.VIEW_TYPE = 0 ;
gridLayoutManager.setSpanCount(1);
//product_recycler_view.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
product_recycler_view.setLayoutManager(gridLayoutManager);
} else {
adapter.VIEW_TYPE = 1 ;
gridLayoutManager.setSpanCount(2);
product_recycler_view.setLayoutManager(gridLayoutManager);
}
product_recycler_view.setAdapter(adapter);
adapter.notifyDataSetChanged();
// product_recycler_view.notify();
// product_recycler_view.notifyAll();
System.out.println("clicked isList "+ isList);
}
try this
private void setLayoutManager(boolean isList) {
if (isList) {
product_recycler_view.setLayoutManager(new LinearLayoutManager(this));
} else {
product_recycler_view.setLayoutManager(gridLayoutManager);
}
product_recycler_view.setAdapter(adapter);
adapter.notifyDataSetChanged();
// product_recycler_view.notify();
// product_recycler_view.notifyAll();
System.out.println("clicked isList "+ isList);
}
Hy, I want to display the resuilt of SQLite.net query with my RecyclerView in my Xamarin.Android app. When excecuting the query synchronous everythin works fine. but when I excecute it async, the Recyclerview won't show up. I checked the result of the query and it contains all the expected entries.
This works:
songsEntryArray = MusicShareDatabase.Instance.Database.Table<SongsEntry>().ToArray();
mRecyclerView = rootView.FindViewById<RecyclerView>(Resource.Id.allsongs_recyclerview);
mLayoutManager = new LinearLayoutManager(this.Activity);
mRecyclerView.SetLayoutManager(mLayoutManager);
mAdapter = new AllSongsAdapter(songsEntryArray);
mAdapter.ItemClick += OnItemClick;
mRecyclerView.SetAdapter(mAdapter);
mAdapter.NotifyDataSetChanged();
And this doesn't work:
var query = MusicShareDatabase.Instance.DatabaseAsync.Table<SongsEntry>();
query.ToListAsync().ContinueWith(t =>
{
mRecyclerView = rootView.FindViewById<RecyclerView>(Resource.Id.allsongs_recyclerview);
mLayoutManager = new LinearLayoutManager(this.Activity);
mRecyclerView.SetLayoutManager(mLayoutManager);
mAdapter = new AllSongsAdapter(t.Result.ToArray());
mAdapter.ItemClick += OnItemClick;
mRecyclerView.SetAdapter(mAdapter);
mAdapter.NotifyDataSetChanged();
});
I think that ContinueWith(Action action) is running on the background thread.
You need to run this code on the UIThread with method Activity.RunOnUiThread(Action action);.
Code
query.ToListAsync().ContinueWith(t =>
{
Activity.RunOnUiThread(() => {
mRecyclerView = rootView.FindViewById<RecyclerView>(Resource.Id.allsongs_recyclerview);
mLayoutManager = new LinearLayoutManager(this.Activity);
mRecyclerView.SetLayoutManager(mLayoutManager);
mAdapter = new AllSongsAdapter(t.Result.ToArray());
mAdapter.ItemClick += OnItemClick;
mRecyclerView.SetAdapter(mAdapter);
mAdapter.NotifyDataSetChanged();
});
});
I am using a RecyclerView to show the data that is retrieved after parsing from the web services. The problem is notifydatasetchanged is not working at all. For solving that thing, I am setting the adapter again.
I am having two problems using this technique:
There is a screen blink while setting a new adapter again.
Also the recycler view points to first item of the recycler view. (I just want to append the new data at the end of the existing list.Array list size is updating. But the only problem, the list is started from the zeroth position.)
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_book_mag_list_child, container, false);
recyclerView = (RecyclerView) view.findViewById(R.id.bookMagRecyclerViewList);
bookMagDataList = new ArrayList<>();
adapter = new BookMagListRecyclerViewAdapter(getActivity(), bookMagDataList);
recyclerView.setAdapter(adapter);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
//linearLayoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setOnScrollListener(new EndlessRecyclerScrollListener(linearLayoutManager) {
#Override
public void onLoadMore(int current_page) {
Log.d("Do Something", "Do Something" + current_page);
params.setStart("" + current_page);
dataModel.init(params);
}
});
return view;
}
#Override
public void update() {
if (dataModel.getFragmentData(subScreenId) != null) {
bookMagDataList = dataModel.getFragmentData(subScreenId);
Log.d("MyDataListSize: ", "" + bookMagDataList.size());
adapter = new BookMagListRecyclerViewAdapter(getActivity(), bookMagDataList);
recyclerView.setAdapter(adapter);
}
}
Please check what is the issue and let me know if it can be solved.
The problem is in your update function. You are overriding the bookMagDataList list with new instance everytime your update. You should have use .addAll() function to add in new element. Then call adapter.notifyDataSetChanged() after that. Make changes as below.
#Override
public void update() {
if (dataModel.getFragmentData(subScreenId) != null) {
bookMagDataList.addAll(dataModel.getFragmentData(subScreenId));
Log.d("MyDataListSize: ", "" + bookMagDataList.size());
adapter.notifyDataSetChanged();
}
}
since u didnt posted your adapter code u have to use styx approach:
#Override
public void update() {
if (dataModel.getFragmentData(subScreenId) != null) {
bookMagDataList.addAll(dataModel.getFragmentData(subScreenId));
Log.d("MyDataListSize: ", "" + bookMagDataList.size());
adapter.notifyDataSetChanged();
}
}
answer is given by styx so credits to him
my solution is below
if (adapter == null) {
adapter = new BookMagListRecyclerViewAdapter(getActivity(), bookMagDataList);
recyclerView.setAdapter(adapter);
} else {
adapter.bookMagDataList.clear();
adapter.bookMagDataList = bookMagDataList;
myAdapter.notifyDataSetChanged()
}
Don't instantiate adapter again, only you should add new data in arrayList then invoke notifyDatasetChanged()
like--
bookMagDataList.add("YOUR ITEM");
adpter.notifyDatasetChanged();
you have to do like below in update method,
if(adapter == null){
adapter = new BookMagListRecyclerViewAdapter(getActivity(), bookMagDataList);
recyclerView.setAdapter(adapter);
}else{
myAdapter.notifyDataSetChanged();
}