My Code
FloatingActionButton button;
button = findViewById(R.id.floatbtn);
if (button != null) {
button.setOnClickListener(new android.view.View.OnClickListener() {
#Override
public void onClick(android.view.View v) {
startActivity(new Intent(Userlist.this, AddTextActivity.class));
recyclerView.removeAllViews();
recyclerView.removeAllViewsInLayout();
}
});
}
recyclerView = findViewById(R.id.userList);
database = FirebaseDatabase.getInstance().getReference("Messages");
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
list = new ArrayList<>();
myAdapter = new MyAdapter(this, list);
recyclerView.setAdapter(myAdapter);
database.addValueEventListener(new ValueEventListener() {
#SuppressLint("NotifyDataSetChanged")
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
Text text = dataSnapshot.getValue(Text.class);
list.add(text);
}
myAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
How do i fix recyclerview shows database entrys twice after new entry
I try to delete to remove all Views when Activity is closing. This didnt work.
Can you help me with this problem?
Why is this happening at all?
Related
Basically the app it's just a simple to do list, but when I add new task to do, it appears on firebase, but not on my app, giving the error
"E/RecyclerView: No adapter attached; skipping layout".
I tried to fix it following the answers here on stackoverflow but none of them helped me, probably I was wrong to adapt the adjustments to my code. I'm just learning and this is my first project, I was following a tutorial to do it.
Anyway, this is the MainActivity.java:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonadd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
Intent a = new Intent(MainActivity.this, AggiungiPiattoAttivity.class);
startActivity(a);
}
});
//working with data
ourdoes = findViewById(R.id.ourdoes);
ourdoes.setLayoutManager(new LinearLayoutManager(this));
list = new ArrayList<MyDoes>();
//get data from firebase
reference = FirebaseDatabase.getInstance(URL).getReference().child("AppLista");
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//set code to retrive data and replace layout
for (DataSnapshot dataSnapshot1: dataSnapshot.getChildren())
{
MyDoes p = dataSnapshot1.getValue(MyDoes.class);
list.add(p);
}
doesAdapter = new DoesAdapter(MainActivity.this, list);
ourdoes.setAdapter(doesAdapter);
doesAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
//set code to show an error
Toast.makeText(getApplicationContext(), "No Data", Toast.LENGTH_SHORT).show();
}
});
}
This is the class where I add a new to do task:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aggiungi_piatto_attivity);
buttonaddnewpiatto.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//insert data to database
reference = FirebaseDatabase.getInstance(URL).getReference().child("BoxDoese").child("Does" + doesNum);
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
dataSnapshot.getRef().child("titledoes").setValue(addnamenewpiattosetted.getText().toString());
dataSnapshot.getRef().child("descdoes").setValue(adddescnewpiattosetted.getText().toString());
dataSnapshot.getRef().child("qualcosaalpostodiunimmagine").setValue(addrecipenewpiattosetted.getText().toString());
Intent a = new Intent( AggiungiPiattoAttivity.this, MainActivity.class);
startActivity(a);
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
});
This is my adapter class:
public class DoesAdapter extends RecyclerView.Adapter<DoesAdapter.MyViewHolder> {
Context context;
ArrayList<MyDoes> myDoes;
public DoesAdapter(){
}
public DoesAdapter(Context c, ArrayList<MyDoes> p ){
context = c;
myDoes=p;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_does, viewGroup, false));
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder myViewHolder, int i) {
myViewHolder.titledoes.setText(myDoes.get(i).getTitledoes());
myViewHolder.qualcosaalpostodiunimmagine.setText(myDoes.get(i).getQualcosaalpostodiunimmagine());
myViewHolder.descdoes.setText(myDoes.get(i).getDescdoes());
}
#Override
public int getItemCount() {
return myDoes.size();
}
class MyViewHolder extends RecyclerView.ViewHolder{
TextView titledoes, descdoes, qualcosaalpostodiunimmagine;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
titledoes = (TextView) itemView.findViewById(R.id.titledoes);
qualcosaalpostodiunimmagine = (TextView) itemView.findViewById(R.id.qualcosaalpostodiunimmagine);
descdoes = (TextView) itemView.findViewById(R.id.descdoes);
}
}
}
I also have other classes but to not put too much code I avoid to put them, they should not be relevant.
Create an empty adapter the same time you set LayoutManager for the RecyclerView: Save it as field of your class:
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
doesAdapter = new DoesAdapter(MainActivity.this, list);
recyclerView.setAdapter(DoesAdapter);
When data is ready, populate the adapter and notify:
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
/// origin code here
for (DataSnapshot dataSnapshot1: dataSnapshot.getChildren())
{
MyDoes p = dataSnapshot1.getValue(MyDoes.class);
list.add(p);
}
// reset data in adapter and not re-creating adapter:
doesAdapter.setItems(list);
doesAdapter.notifyDataSetChanged();
// instead of doesAdapter = new DoesAdapter(MainActivity.this, list);
recyclerView.setAdapter(DoesAdapter);
}
Source: https://stackoverflow.com/a/58251986/12596713
The problem that I am having is that when a user adds a comment the entire list refreshes because I have notifyDataSetChanged(); set on the CommentAdapter. Everything jumps, and refreshes and I want it to be smoother, so I decided to use notifyItemInserted(); instead of notifyDataSetChanged();, but it isn't doing anything different.
notifyItemInserted(); should only update the last item or the newest item added to the list, so why is everything being refreshed/updated...?
Can someone tell me how to fix this? Only want last item added to list to be "added"/"updated"/whatever, not the entire list because if many people start commenting everything is always reloading...
In my readComments(); what I have now is mCommentAdapter.notifyItemInserted(mCommentList.size() - 1);, and what I had before was mCommentAdapter.notifyDataSetChanged();, but they are having the same effect. How can I fix this?
CommentsActivity
public class CommentsActivity extends AppCompatActivity {
private CommentAdapter mCommentAdapter;
private List<Comment> mCommentList;
private RecyclerView mRecyclerView;
EditText mAddComment;
ImageView mImageProfile;
TextView mPost;
String mPostId;
String mPublisherId;
String mNotificationId;
FirebaseUser mFirebaseUser;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comments);
mRecyclerView = findViewById(R.id.recycler_view);
mRecyclerView.setHasFixedSize(true);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(linearLayoutManager);
mCommentList = new ArrayList<>();
mCommentAdapter = new CommentAdapter(this, mCommentList, mPostId);
mRecyclerView.setAdapter(mCommentAdapter);
mAddComment = findViewById(R.id.add_comment);
mImageProfile = findViewById(R.id.image_profile);
mPost = findViewById(R.id.post_comment);
mFirebaseUser = FirebaseAuth.getInstance().getCurrentUser();
mPost.setOnClickListener(v -> {
if (mAddComment.getText().toString().equals("")) {
Toast.makeText(CommentsActivity.this, "Can't send empty comments", Toast.LENGTH_SHORT).show();
} else {
addCommentNotification(mPublisherId, mPostId);
}
});
getImage();
readComments();
}
private void getImage() {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Users").child(mFirebaseUser.getUid());
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
if (user != null)
Glide.with(getApplicationContext()).load(user.getImageurl()).into(mImageProfile);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
private void readComments() {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Comments").child(mPostId);
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
mCommentList.clear();
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Comment comment = snapshot.getValue(Comment.class);
mCommentList.add(comment);
}
mCommentAdapter = new CommentAdapter(CommentsActivity.this, mCommentList, mPostId);
mRecyclerView.setAdapter(mCommentAdapter);
mCommentAdapter.notifyItemInserted(mCommentList.size() - 1);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
}
I am guessing you should be using a ChildEventListener:
Your readComments() must be like this:
private void readComments() {
//your reference
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Comments").child(mPostId);
//the child listener
ChildEventListener listener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
// A new comment has been added
Comment comment = dataSnapshot.getValue(Comment.class);
mCommentList.add(comment);
//Notify adapter
mCommentAdapter.notifyItemInserted(mCommentList.size() - 1);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
//attach the listener
ref.addChildEventListener(listener);
}
I am retrieving Product Information from Firebase, but the issue is that I can't select any specific ID that is stored in Firebase.
I tried different solutions for the problem including get(position) but it's getting the position of the product in the RecyclerView
This the ProductsAdapter onBindViewHolder function.
Changes are added using *'s
#Override
public void onBindViewHolder(#NonNull ProductViewHolder holder, int position) {
Product productCurrent = mProducts.get(position);
holder.product_title.setText(toCapitalize(productCurrent.getpTitle()));
holder.product_pice.setText("$: " + productCurrent.getpPrice());
Picasso.with(mContext)
.load(productCurrent.getpImageUrl())
.placeholder(R.mipmap.ic_launcher)
.fit()
.centerCrop()
.into(holder.product_image);
holder.parentLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(mContext, ProductPreview.class);
intent.putExtra("pImageUrl", productCurrent.getpImageUrl());
intent.putExtra("pTitle", toCapitalize(productCurrent.getpTitle()));
intent.putExtra("pPrice", productCurrent.getpPrice());
intent.putExtra("pDescription", toCapitalize(productCurrent.getpDescription()));
Toast.makeText(mContext, " " + position, Toast.LENGTH_SHORT).show();
mContext.startActivity(intent);
}
});
}
This Code is from the ProductsActivity having the RecyclerView
private ProductsAdapter mAdapter;
private ProgressBar mProgressBar;
DatabaseReference mDatabaseReference;
private ArrayList<Product> mProducts;
***ArrayList<ProductKeys> mProductKeys;***
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_products);
mRecyclerView = findViewById(R.id.products_recycler_view);
mRecyclerView.setHasFixedSize(true);
GridLayoutManager gridLayoutManager = new GridLayoutManager(getApplicationContext(),2);
mRecyclerView.setLayoutManager(gridLayoutManager);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mProducts = new ArrayList<>();
***mProductKeys = new ArrayList<>();***
mDatabaseReference = FirebaseDatabase.getInstance().getReference("products");
mDatabaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapShot : dataSnapshot.getChildren())
{
Product product = postSnapShot.getValue(Product.class);
mProducts.add(product);
***mProductKeys.add(postSnapShot.getKey());***
}
mAdapter = new ProductsAdapter(ProductsActivity.this, mProducts);
mRecyclerView.setAdapter(mAdapter);
mProgressBar.setVisibility(View.INVISIBLE);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Toast.makeText(ProductsActivity.this, databaseError.getMessage(), Toast.LENGTH_SHORT).show();
mProgressBar.setVisibility(View.INVISIBLE);
}
});
Getting Error:
(com.*)
in ArrayList cannot be applied
to
(java.lang.String)
Please help me out of this... Thanks in Advance
If you're trying to determine the Firebase Database ID of the item at position in the onBindViewHolder method, you will have to maintain that mapping yourself. You can easily do this by extracting both the getKey() as well as the getValue() from the snapshot in onDataChange and keeping lists of both of them.
Something like this:
mProductKeys = new ArrayList<String>();
mDatabaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapShot : dataSnapshot.getChildren())
{
Product product = postSnapShot.getValue(Product.class);
mProducts.add(product);
mProductKeys.add(postSnapShot.getKey());
}
mAdapter = new ProductsAdapter(ProductsActivity.this, mProducts);
mRecyclerView.setAdapter(mAdapter);
mProgressBar.setVisibility(View.INVISIBLE);
}
And then in the onBindViewHolder you can look up the key for the item that was clicked on in mProductKeys.
Ok, I tried different things and #Frank's answer gave me some idea to do this. Just added one Line and its done. :) No Need to add ArrayList<String>.
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapShot : dataSnapshot.getChildren())
{
Product product = postSnapShot.getValue(Product.class);
product.setpKey(postSnapShot.getKey());
mProducts.add(product);
}
mAdapter = new ProductsAdapter(ProductsActivity.this, mProducts);
mRecyclerView.setAdapter(mAdapter);
mProgressBar.setVisibility(View.INVISIBLE);
}
And Added the String pKey to my Product.class with Setter and Getter.
I've problem storing data into ListView from database on Firebase, I'm using the method below it runs when I click on button,
here is the method I use
private void retrieveMyDataMethod(){
databaseReference = FirebaseDatabase.getInstance().getReference();
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String name = dataSnapshot.child(uid).child("name").getValue(String.class);
String address = dataSnapshot.child(uid).child("address").getValue(String.class);
String[] data = new String[]{
name, address
};
List<String> data_list = new ArrayList<String>(Arrays.asList(data));
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data_list);
listViewRe.setAdapter(arrayAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
To solve this, please use the following lines of code:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
Query query = rootRef.orderByChild("name");
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
ArrayList<String> list = new ArrayList<>();
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String name = ds.child("name").getValue(String.class);
list.add(name);
}
ListView listView = (ListView) findViewById(R.id.list_view);
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(getApplicationContext(), android.R.layout.simple_list_item_1, list);
listView.setAdapter(arrayAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage()); //Don't ignore errors!
}
};
query.addListenerForSingleValueEvent(valueEventListener);
The output in your ListView will be:
cmon
here
I am having problems with repeated items in RecyclerView. Whenever I upload to my firebase and open the main activity again there are repeated itmes in the recyclerView from the previous load. How do I remove repeated items from my recyclerView.
Code for my Main Activity is
private RecyclerView mRecyclerView;
private RecyclerViewAdapter mAdapter;
private DatabaseReference mDatabaseReference;
private List<Upload> mUploads;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_posts);
setupBottomNavigation();
mRecyclerView = findViewById(R.id.recycler_view);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mUploads = new ArrayList<>();
mDatabaseReference = FirebaseDatabase.getInstance().getReference("uploads");
mDatabaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot postSnapshot : dataSnapshot.getChildren())
{
Upload upload = postSnapshot.getValue(Upload.class);
mUploads.add(upload);
}
mAdapter = new RecyclerViewAdapter(MainActivity.this, mUploads);
mRecyclerView.setAdapter(mAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Toast.makeText(MainActivity.this, databaseError.getMessage(), Toast.LENGTH_SHORT).show();
}
});
Button btn_post = (Button) findViewById(R.id.post_Main);
btn_post.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent_posts = new Intent(MainActivity.this, Posts.class);
startActivity(intent_posts);
}
});
}
Code from my Adapter class is
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.rViewHolder> {
private Context mContext;
private List<Upload> mUploads;
public RecyclerViewAdapter(Context context, List<Upload> uploads){
mContext = context;
mUploads = uploads;
}
#Override
public rViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.card_view_recycler_view, parent, false);
return new rViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull rViewHolder viewHolder, int position) {
Upload uploadCurrent = mUploads.get(position);
viewHolder.textViewDescription.setText(uploadCurrent.getDescription());
Picasso.get().load(uploadCurrent.getImageUrl()).into(viewHolder.imageView);
}
#Override
public int getItemCount() {
return mUploads.size();
}
public class rViewHolder extends RecyclerView.ViewHolder{
public TextView textViewDescription;
public ImageView imageView;
public rViewHolder(View itemView){
super(itemView);
textViewDescription = itemView.findViewById(R.id.r_description);
imageView = itemView.findViewById(R.id.r_image_view);
}
}
i think this happens because firebase store data offline, try below way to initialise instance
FirebaseDatabase.getInstance().setPersistenceEnabled(false);
should work.
Try to clear your List before addind data to it as shown below.
private RecyclerView mRecyclerView;
private RecyclerViewAdapter mAdapter;
private DatabaseReference mDatabaseReference;
private List<Upload> mUploads;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_posts);
setupBottomNavigation();
mRecyclerView = findViewById(R.id.recycler_view);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mUploads = new ArrayList<>();
mDatabaseReference = FirebaseDatabase.getInstance().getReference("uploads");
mDatabaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot postSnapshot : dataSnapshot.getChildren())
{
Upload upload = postSnapshot.getValue(Upload.class);
mUploads.clear();
mUploads.add(upload);
}
mAdapter = new RecyclerViewAdapter(MainActivity.this, mUploads);
mRecyclerView.setAdapter(mAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Toast.makeText(MainActivity.this, databaseError.getMessage(), Toast.LENGTH_SHORT).show();
}
});
Button btn_post = (Button) findViewById(R.id.post_Main);
btn_post.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent_posts = new Intent(MainActivity.this, Posts.class);
startActivity(intent_posts);
}
});
}
Try clearing your ArrayList in this case mUploads before adding items to it i.e.,
mDatabaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
// Clear ArrayList
mUploads.clear();
for(DataSnapshot postSnapshot : dataSnapshot.getChildren())
{
Upload upload = postSnapshot.getValue(Upload.class);
mUploads.add(upload);
}
mAdapter = new RecyclerViewAdapter(MainActivity.this, mUploads);
mRecyclerView.setAdapter(mAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Toast.makeText(MainActivity.this, databaseError.getMessage(), Toast.LENGTH_SHORT).show();
}
});
This works well on my end and I hope it helps.
Reference here.
If you uninstall the app and install it again and there are duplicate items in the list the first time you run the app after installing, then there are duplicate items in the backend Firebase. If there are duplicate items only the second time you run the app then your code is responsible.
The code you have showed looks good to me. There have to be something else your not showing like by mistake you have duplicate addValueEventListener or you are feeding the Adapter a second time with the List<Upload> mUploads
As many suggest here that you do the mUploads.clear(); is not necessary since you populating the array from the onCreate(), witch only run once during Activity lifecykle
I would move the private List<Upload> mUploads; inside the onCreate() as a local variable if your not using it elsewhere