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) {
}
});
Related
Actually, I make a 1st query which returns a number of objects.
As we could with a standard recyclerView, I would like to filter dynamically these objects with an EditText after the view is created, and update the view dynamically.
For example, if my first query returns and displays 10 objects, the user will be able to search one objet through the 10 objects by making a request with an EditText. The view will display only this object.
Query query = collectionRef.whereArrayContains("customerNearMe", getCurrentUser().getUid());
private void sendToAdapter(Query query) {
FirestoreRecyclerOptions<Restaurant> options = new FirestoreRecyclerOptions.Builder<Restaurant>()
.setQuery(query, Restaurant.class)
.build();
adapter = new RestaurantAdapter(options);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(adapter);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL);
recyclerView.addItemDecoration(new SimpleItemDecorator(getContext()));
adapter.notifyDataSetChanged();
}
#Override
public void onStart() {
super.onStart();
adapter.startListening();
}
#Override
public void onDestroy() {
super.onDestroy();
adapter.stopListening();
}
Actually, I know how to do it with a simple RecyclerView
private void filter(String text) {
ArrayList<ExampleItem> filteredList = new ArrayList<>();
for (ExampleItem item : mExampleList) {
if (item.getText1().toLowerCase().contains(text.toLowerCase())) {
filteredList.add(item);
}
}
mAdapter.filterList(filteredList);
}
but I am a newbie with Firestore.
Just trying to show in a RecyclerView the info from my database.
everything works fine. The objects are displayed.
but logcat says:
E/RecyclerView: No adapter attached; skipping layout
This is my the code (DisplayImages.java)
public class DisplayImages extends AppCompatActivity {
DatabaseReference databaseReference;
RecyclerView recyclerView;
RecyclerView.Adapter adapter;
ProgressDialog progressDialog;
List<ImageUploadInfo> list = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display_images);
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(DisplayImages.this));
progressDialog = new ProgressDialog(DisplayImages.this);
progressDialog.setMessage("Loading Images From Firebase.");
progressDialog.show();
// Setting up Firebase image upload folder path in databaseReference.
// The path is already defined in MainActivity.
databaseReference = FirebaseDatabase.getInstance().getReference(Main.Database_Path);
databaseReference.addValueEventListener(new ValueEventListener() {
#override
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot postSnapshot : snapshot.getChildren()) {
ImageUploadInfo imageUploadInfo = postSnapshot.getValue(ImageUploadInfo.class);
list.add(imageUploadInfo);
}
adapter = new RecyclerViewAdapter(getApplicationContext(), list);
recyclerView.setAdapter(adapter);
progressDialog.dismiss();
}
#Override
public void onCancelled(DatabaseError databaseError) {
progressDialog.dismiss();
}
});
}
And If it makes any difference, this is what i wrote in MainActivity about the DataBase_Path :
public class Main extends AppCompatActivity implements View.OnClickListener {
DatabaseReference databaseReference;
public static final String Database_Path = "All_Image_Uploads_Database";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
databaseReference = FirebaseDatabase.getInstance().getReference(Database_Path);
As you can see I have attached an adapter for Recycleview. so why do I keep getting this error?
i have read other questions related to same problem but none helps.
Yyou need to set the adapter after you set the layour manager, otherwise you'll get the error message you're seeing.
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
//don't set adapter here
recyclerView.setLayoutManager(new LinearLayoutManager(DisplayImages.this));
//set adapter here
Moreover, you can set the adapter with an empty list first and then when you receive the callback from firebase you can update the list and call notifyDatasetChanged() on the adapter. The problem you see is due to the fact that you're not actually setting the adapter until very later in the process (when your firebase call comes back)
Like this:
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(DisplayImages.this));
adapter = new RecyclerViewAdapter(getApplicationContext(), list);
recyclerView.setAdapter(adapter);
databaseReference.addValueEventListener(new ValueEventListener() {
#override
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot postSnapshot : snapshot.getChildren()) {
ImageUploadInfo imageUploadInfo = postSnapshot.getValue(ImageUploadInfo.class);
list.add(imageUploadInfo);
}
adapter.notifyDatasetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
progressDialog.dismiss();
}
});
I don't see anywhere you are creating an adapter and setting that adapter to RecyclerView. You need to create an adapter and set adapter like following:
mAppAdapter = new AppAdapter(mModel); // or whatever constructor you want
recyclerView.setAdapter(mAppAdapter);
EDIT:
You are setting adapter to RecyclerView inside an asynchronous method. So your program will have a non deterministinc amount of time where the adapter is not actually set to recyclerView. To get rid off the warning you get you should initialize and set the adapter above the async method and only update data of the adapter inside the async method
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..
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();
});
});
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.