How to deal with async nature of firebase database - java

Im facing this issue due to the asynchronous nature of Firebase database in my quiz app.Question score contains the current user and its score .Ranking contains the users and their corresponding marks.Question score has the score stored in it but when i am updating the ranking database with the score ... The value of marks in Ranking object is being reset to zero.How should i approach this?
I've already tried using Ranking Callback to update the database but no luck.
public class RankingFragment extends Fragment {
View myFragment;
RecyclerView rankingList;
LinearLayoutManager layoutManager;
FirebaseRecyclerAdapter<Ranking, RankingViewHolder> adapter;
FirebaseDatabase database;
DatabaseReference questionScore, rankingTbl;
String k;
int sum=0;
public static RankingFragment newInstance() {
RankingFragment rankingFragment = new RankingFragment();
return rankingFragment;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
database = FirebaseDatabase.getInstance();
questionScore = database.getReference("Question_Score");
rankingTbl = database.getReference("Ranking");
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
myFragment = inflater.inflate(R.layout.fragment_ranking, container, false);
rankingList = (RecyclerView) myFragment.findViewById(R.id.rankingList);
layoutManager = new LinearLayoutManager(getActivity());
rankingList.setHasFixedSize(true);
layoutManager.setReverseLayout(true);
layoutManager.setStackFromEnd(true);
rankingList.setLayoutManager(layoutManager);
updateScore(Common.currentUser.getUsername());
return myFragment;
}
private void updateScore(final String username)
{
questionScore.orderByChild("user").equalTo(username).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot data : dataSnapshot.getChildren()) {
QuestionScore ques = data.getValue(QuestionScore.class);
sum+=Integer.parseInt(ques.getScore());
}
Ranking ranking = new Ranking(username, sum);
rankingTbl.child(ranking.getUsername()).setValue(ranking);
adapter = new FirebaseRecyclerAdapter<Ranking, RankingViewHolder>(
Ranking.class, R.layout.layout_ranking, RankingViewHolder.class, rankingTbl.orderByChild("marks")
) {
#Override
protected void populateViewHolder(final RankingViewHolder viewHolder, Ranking model, int position) {
viewHolder.txt_name.setText(model.getUsername());
viewHolder.txt_score.setText(String.valueOf(model.getMarks()));
}
};
finish();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
private void finish() {
adapter.notifyDataSetChanged();
rankingList.setAdapter(adapter);
}
}
The marks of the ranking database are storing 0 and thus the view holder is also populated with zero whereas it should store the score of the current user(stored in "sum+=Integer.parseInt(ques.getScore());"

You are assigning the value from QuestionScore to sum instead it should be like adding all the QuestionScore values
for (DataSnapshot data : dataSnapshot.getChildren()) {
QuestionScore ques = data.getValue(QuestionScore.class);
sum+=Integer.parseInt(ques.getScore()); // <--- changed = to +=
}

Related

Firebase Recycler View firstly showing blank page but when power off my mobile & open then showing data perfectly, no errors

I'm creating app with Firebase implementation. Now I'm stuck with showing list of data using recyclerView. After using my code I have empty activity without any error in Logcat but when turn off my mobile power button and again start then data show on recycleView. Also one thing when I go another activity and open this activity then again show blank page. I need someone's help :(
This is my fragment:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_message, container, false);
// Initialize
recyclerView = view.findViewById(R.id.recycleView);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
list = new ArrayList<>();
// For retrieve Chat User ID
databaseReference = FirebaseDatabase.getInstance().getReference().child("Chat");
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
list.clear();
for (DataSnapshot ds : snapshot.getChildren()) {
receiveID = ds.getKey();
// Restore Data on model class name UserDataInfo
databaseReference = FirebaseDatabase.getInstance().getReference().child("UsersData").child(receiveID);
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
String userName = String.valueOf(snapshot.child("userName").getValue());
String userNumber = String.valueOf(snapshot.child("userPhone").getValue());
String userType = String.valueOf(snapshot.child("userType").getValue());
String userImage = String.valueOf(snapshot.child("userName").getValue());
info = new UserDataInfo(userImage, userType, userNumber, userName, receiveID);
list.add(info);
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
// End
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
adapter = new messageListAdapter(list, getContext());
recyclerView.setAdapter(adapter);
}
My Adapter:
public class messageListAdapter extends RecyclerView.Adapter<messageListAdapter.ViewHolder> {
List<UserDataInfo> infoList;
Context context;
public messageListAdapter(List<UserDataInfo> infoList, Context context) {
this.infoList = infoList;
this.context = context;
}
#NonNull
#Override
public messageListAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.message_list_show_admin, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull messageListAdapter.ViewHolder holder, int position) {
final UserDataInfo userDataInfo = infoList.get(position);
holder.Name.setText(userDataInfo.getUserName());
holder.Number.setText(userDataInfo.getUserPhone());
holder.whichType.setText(userDataInfo.getUserType());
holder.linearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, "ID: " + userDataInfo.getReceiveID(), Toast.LENGTH_SHORT).show();
}
});
}
#Override
public int getItemCount() {
return infoList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
ImageView Image;
TextView Name, Number, whichType;
LinearLayout linearLayout;
public ViewHolder(#NonNull View itemView) {
super(itemView);
Image = itemView.findViewById(R.id.UserImage);
Name = itemView.findViewById(R.id.Name);
Number = itemView.findViewById(R.id.Number);
whichType = itemView.findViewById(R.id.WhichType);
linearLayout = itemView.findViewById(R.id.Linear_layout);
}
}
}
my Firebase DataBase setup:
Database image
Database image

Android: custom ListView doesn't get populated immediately

I would like to create a custom ListView populated by a custom ArrayAdapter. The code works fine, but the ListView is not showing data immediately. In order to have the data shown I need to click on a random EditText of the same page, close the Keyboard and magically the ListView shows data.
I don't know if it this can have an impact but the adapter is used in a fragment,which retrieve the data from Firebase during the OnCreate method.
Below my code, I removed everything not necessary for this topic and simplified the array.
This is my Class of elements inside the array:
public class ListItem {
private String Description;
private String Price;
public ListItem (String Description, String Price) {
this.Description = Description;
this.Price = Price;
}
public String getDescription() { return Description; }
public String getPrice() {
return Price;
}
public void setDescription(String description) {
Description = description;
}
public void setPrice(String price) {
Price = price;
}
}
The adapter is the following:
public class CustomListAdapter extends ArrayAdapter<ListItem> {
private Context mContext;
private Integer mResource;
private static class ViewHolder {
TextView txt_description_item;
TextView txt_price_item;
}
public CustomListAdapter(#NonNull Context context, int resource, #NonNull ArrayList<ListItem> objects) {
super(context, resource, objects);
mContext = context;
mResource = resource;
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
String Description = getItem(position).getDescription();
String Price = getItem(position).getPrice();
LayoutInflater inflater = LayoutInflater.from(mContext);
convertView = inflater.inflate(mResource, parent, false);
ViewHolder viewHolder = new ViewHolder();
viewHolder.txt_description_item = convertView.findViewById(R.id.txt_description_item);
viewHolder.txt_price_item = convertView.findViewById(R.id.txt_price_item);
viewHolder.txt_description_item.setText(Description);
viewHolder.txt_price_item.setText(Price);
return convertView;
}
}
Finally this is the Fragment code:
public class MyFragment extends Fragment {
private ListView view_list_items;
private FirebaseDatabase database;
private DatabaseReference refDatabase;
private static final String USER = "user";
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.fragment_myprofile, container, false);
view_list_items = v.findViewById(R.id.list_items);
database = FirebaseDatabase.getInstance();
refDatabase = database.getReference(USER);
refDatabase.child("items").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
final ArrayList<ListItem> arr_items = new ArrayList<>();
for (DataSnapshot ds : dataSnapshot.child("itemlist1").getChildren()) {
final String description = ds.getKey();
final String price = ds.getValue(String.class);
ListItem listItem = new ListItem(description,price);
arr_items.add(listItem);
}
CustomListAdapter adapter = new CustomListAdapter(getContext(),R.layout.layout_myItemList, arr_items);
view_list_items.setAdapter(adapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
EDIT:
I solved the issue by setting the adapter inside the 'for' cycle. Maybe it's not the most elegant solution but it works fine
for (DataSnapshot ds : dataSnapshot.child("itemlist1").getChildren()) {
final String description = ds.getKey();
final String price = ds.getValue(String.class);
ListItem listItem = new ListItem(description,price);
arr_items.add(listItem);
CustomListAdapter adapter = new
CustomListAdapter(getContext(),R.layout.layout_myItemList, arr_items);
view_list_items.setAdapter(adapter);
}
The firebase database is asynchronous i.e, the compiler doesn't wait for the code inside onDataChange() or onCancelled() to return a value. It would return all the values apart from those inside those 2 methods immediately, while for those 2 methods it would return the value when the value is available from the database. Below code might help you in better understanding
refDatabase.child("items").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
System.out.println("Inside onDataChange method");
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
System.out.println ("Outside onDataChange method");
The output will be like:
Outside onDataChange method
Inside onDataChange method
Notice that comes Outside onDataChange method first. This is the reason why your listView doesn't show data immediately.
What you can do to keep the user engaged is, add a spinner in your view which will be visible till the data is fetched from the database. To do this, try this code.
database = FirebaseDatabase.getInstance();
refDatabase = database.getReference(USER);
spinner.setVisibility(View.VISIBLE);
refDatabase.child("items").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
spinner.setVisibility(View.INVISIBLE);
//Your code goes here
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
//Error code goes here
}
});

ArrayList becomes null after addValueEventListener

I am trying to show some items in a RecyclerView and such items are contained in an ArrayList populated with RealtimeDatabase (Firebase) data. This is done every time some changes are detected in the db as follows. The problem is that lstCompany becomes null soon after the addValueEventListener. How can I keep it fulfilled?
public class FragmentCompanies extends Fragment {
View v;
private RecyclerView recyclerView;
private List<Company> lstCompany;
DatabaseReference db;
public FragmentCompanies(){}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
db = FirebaseDatabase.getInstance().getReference();
db.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
lstCompany = new ArrayList<>();
DataSnapshot ds = dataSnapshot.child("Company");
for (DataSnapshot company: ds.getChildren()){
Map<String, String> currCompany = (Map) company.getValue();
Company c = new Company(currCompany.get("name"), currCompany.get("overview"), currCompany.get("imageURL"));
lstCompany.add(c);
System.out.println(lstCompany);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
System.out.println(lstCompany);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState){
v = inflater.inflate(R.layout.fragment_companies,container,false);
System.out.println(lstCompany);
//bind the adapter to the recyclerView
recyclerView = v.findViewById(R.id.companies_recyclerview);
CompaniesRecyAdapter recyAdapter = new CompaniesRecyAdapter(getContext(), lstCompany);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setAdapter(recyAdapter);
return v;
}
}
The last two println operations return null and it should not happen.
intialize the arraylist in onCreate, and when add data in on onDataChange and notify the adapter that data has been changed
public class FragmentCompanies extends Fragment {
View v;
private RecyclerView recyclerView;
private List<Company> lstCompany;
DatabaseReference db;
public FragmentCompanies(){}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState){
v = inflater.inflate(R.layout.fragment_companies,container,false);
lstCompany = new ArrayList<>();
System.out.println(lstCompany);
//bind the adapter to the recyclerView
recyclerView = v.findViewById(R.id.companies_recyclerview);
CompaniesRecyAdapter recyAdapter = new CompaniesRecyAdapter(getContext(), lstCompany);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setAdapter(recyAdapter);
db = FirebaseDatabase.getInstance().getReference();
db.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
DataSnapshot ds = dataSnapshot.child("Company");
for (DataSnapshot company: ds.getChildren()){
Map<String, String> currCompany = (Map) company.getValue();
Company c = new Company(currCompany.get("name"), currCompany.get("overview"), currCompany.get("imageURL"));
lstCompany.add(c);
System.out.println(lstCompany);
System.out.println(c.getCompanyName);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
System.out.println(lstCompany);
return v;
}
}

Adding data in array list is failed

I want to add an object called funny_momments_row to an ArrayList called data, but it is not adding. I am checking that data is empty or not using isEmpty method afterwards. The code is running fine until this line:
funny_momments_row current = new funny_momments_row(dataSnapshot.child("like").getValue().toString(),dataSnapshot.child("url").getValue().toString());
Here is the complete class:
public class funny_moments extends Fragment {
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference("funnymomments");
ArrayList<funny_momments_row> data;
TextView tesgting;
private RecyclerView mRecyclerView;
public funny_moments() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_funny_moments, container, false);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recylerview);
tesgting = (TextView) view.findViewById(R.id.test);
data = new ArrayList<>();
getData();
// Inflate the layout for this fragment
return view;
}
public void getData() {
funny_momments_row lu = new funny_momments_row("1", "hi");
myRef.child("video 0").setValue(lu);
myRef.child("video 0").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot videoSnapshot: dataSnapshot.getChildren()) {
funny_momments_row current = new funny_momments_row(dataSnapshot.child("like").getValue().toString(), dataSnapshot.child("url").getValue().toString());
data.add(current);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
if (data.isEmpty()) {
tesgting.setText("hi");
}
}
}
Try this because videoSnapsot is the data you need to add in the array list.
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot videoSnapshot: dataSnapshot.getChildren()) {
data.add(videoSnapshot.getValue(funny_momments_row.class));
//Create getter/setter method in funny_momments_row class if needed.
}
}
And, for funny_momments_row, java naming convention, please.

Android Dont know how to fix this index out of bounds exception FirebaseRecyclerview

I get this error and I dont know how to fix it
java.lang.IndexOutOfBoundsException: Index: 2, Size: 2
at java.util.ArrayList.get(ArrayList.java:411)
at com.tijdelijk.firebasetest.Start$1.populateViewHolder(Start.java:69)
at com.tijdelijk.firebasetest.Start$1.populateViewHolder(Start.java:64)
I am working with firebase database and based on CategoryId I put the items in a arraylist for the SubCategories my code:
public class CategoryFragment extends Fragment {
View myFragment;
RecyclerView listCategory;
RecyclerView.LayoutManager layoutManager;
FirebaseRecyclerAdapter<Category, CategoryViewHolder> adapter;
FirebaseDatabase database;
DatabaseReference categories;
DatabaseReference subCategory;
public static CategoryFragment newInstance() {
CategoryFragment fragment = new CategoryFragment();
return fragment;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
database = FirebaseDatabase.getInstance();
categories = database.getReference("Category");
subCategory = database.getReference("SubCategory");
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
myFragment = inflater.inflate(R.layout.fragment_category, container, false);
listCategory = (RecyclerView) myFragment.findViewById(R.id.listCategory);
listCategory.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(container.getContext());
listCategory.setLayoutManager(layoutManager);
loadCategories();
return myFragment;
}
private void loadCategories() {
adapter = new FirebaseRecyclerAdapter<Category, CategoryViewHolder>(
Category.class,
R.layout.category_layout,
CategoryViewHolder.class,
categories
) {
#Override
protected void populateViewHolder(CategoryViewHolder viewHolder, final Category model, int position) {
viewHolder.category_name.setText(model.getName());
Picasso.with(getActivity())
.load(model.getImage())
.into(viewHolder.category_image);
viewHolder.setItemClickListener(new ItemClickListener() {
#Override
public void onClick(View view, int position, boolean isLongClick) {
Intent startGame = new Intent(getActivity(), Start.class);
Common.categoryId = adapter.getRef(position).getKey();
loadSubCategory(Common.categoryId);
startActivity(startGame);
}
});
}
};
adapter.notifyDataSetChanged();
listCategory.setAdapter(adapter);
}
private void loadSubCategory(String categoryId) {
//Clear list if there are old subCategory
if (Common.subCategoryList.size() > 0) {
Common.subCategoryList.clear();
}
subCategory.orderByChild("CategoryId").equalTo(categoryId)
.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
SubCategory ques = postSnapshot.getValue(SubCategory.class);
Common.subCategoryList.add(ques);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
in this activity I want to display also a recyclerview but this time based on the arraylist I got from the categoryfragment here is my code:
public class Start extends AppCompatActivity {
FirebaseDatabase database;
DatabaseReference subCategory;
FirebaseRecyclerAdapter<SubCategory, SubCategoryViewHolder> adapter;
RecyclerView listSubCategory;
RecyclerView.LayoutManager layoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
database = FirebaseDatabase.getInstance();
subCategory = database.getReference("SubCategory");
listSubCategory = findViewById(R.id.listSubCategory);
listSubCategory.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(getBaseContext());
listSubCategory.setLayoutManager(layoutManager);
loadSubCategories();
adapter.notifyDataSetChanged();
listSubCategory.setAdapter(adapter);
}
private void loadSubCategories() {
adapter = new FirebaseRecyclerAdapter<SubCategory, SubCategoryViewHolder>(
SubCategory.class,
R.layout.subcategory_layout,
SubCategoryViewHolder.class,
subCategory
) {
#Override
protected void populateViewHolder(SubCategoryViewHolder viewHolder, SubCategory model, int position) {
viewHolder.subcategory_nlname.setText(Common.subCategoryList.get(position).getLatijnseNaam());
viewHolder.subcategory_ltname.setText(Common.subCategoryList.get(position).getNederlandseNaam());
viewHolder.setItemClickListener(new ItemClickListener() {
#Override
public void onClick(View view, int position, boolean isLongClick) {
Intent startGame = new Intent(Start.this, Start.class);
Common.categoryId = adapter.getRef(position).getKey();
startActivity(startGame);
}
});
}
};
}
}
here is my viewholder:
public class SubCategoryViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener{
public TextView subcategory_nlname;
public TextView subcategory_ltname;
private ItemClickListener itemClickListener;
public SubCategoryViewHolder(View itemView) {
super(itemView);
subcategory_ltname = itemView.findViewById(R.id.latijnse_naam);
subcategory_nlname = itemView.findViewById(R.id.nederlandse_naam);
// itemView.findViewById(R.id.btnPlay).setOnClickListener(this);
}
public void setItemClickListener(ItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
#Override
public void onClick(View view) {
itemClickListener.onClick(view,getAdapterPosition(),false);
}
}
IndexOutOfBoundException means that you are trying to access a value that is at an index out of the array.
In your case, you have an array of two values. So you have the keys 0 and 1. As said in the error, your code is trying to access the index 2, which does not exist.
You have to check that you are accessing an index that is in the range of values of the array.
Good day to you.

Categories