13

My structure

enter image description here

So I have an app in which users upload posts in my adapter. I can retrieve the post description and the post picture, but when I try to retrieve the poster's name the app seems to crash, to retrieve the name with every post there is a child added to the database with the uploader's id. This is my class file:

public String image_thumb;
public String user_id;
public String image_url;
public String desc;

public BlogPost(){}


public BlogPost(String user_id, String image_url, String desc, String image_thumb) {
    this.user_id = user_id;
    this.image_url = image_url;
    this.desc = desc;
    this.image_thumb = image_thumb;
}



public String getUser_id() {
    return user_id;
}

public void setUser_id(String user_id) {
    this.user_id = user_id;
}

public String getImage_url() {
    return image_url;
}

public void setImage_url(String image_url) {
    this.image_url = image_url;
}

public String getDesc() {
    return desc;
}

public void setDesc(String desc) {
    this.desc = desc;
}

public String getImage_thumb() {
    return image_thumb;
}

public void setImage_thumb(String image_thumb) {
    this.image_thumb = image_thumb;
}

and this is some of my adapter:

public void onBindViewHolder(final ViewHolder holder, int position) {

    String desc_data = blog_list.get(position).getDesc();
    holder.setDescText(desc_data);//this works

    String image_url = blog_list.get(position).getImage_url();
    holder.setBlogImage(image_url);//this works

    String user_id = blog_list.get(position).getUser_id();
    firebaseDatabase.child("Users").child(user_id).addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            if(dataSnapshot.exists()){
                String userName = dataSnapshot.child("name").getValue().toString();

                holder.setUserName(userName);
            }
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });
    public void setUserName(String name){
        blogUserName = mView.findViewById(R.id.blog_user_name);
        blogUserName.setText(name);
    }

What I basically wanna do is look inside the user_id for the name and retrieve it inside my TextView

Gastón Saillén
  • 12,319
  • 5
  • 67
  • 77
R.Youssef
  • 169
  • 1
  • 1
  • 7

4 Answers4

15

In order to make it work correctly, I recommend you to do some changes in you model class as well as in your code. Your model class should look like:

public class BlogPost {
    public String imageThumb, userId, imageUrl, desc;

    public BlogPost() {}

    public BlogPost(String imageThumb, String userId, String imageUrl, String desc) {
        this.imageThumb = imageThumb;
        this.userId = userId;
        this.imageUrl = imageUrl;
        this.desc = desc;
    }

    public String getImageThumb() {return imageThumb;}
    public String getUserId() {return userId;}
    public String getImageUrl() {return imageUrl;}
    public String getDesc() {return desc;}
}

Please see the naming convention of the fields and getters.

In order to make it work, don't forget the remove the old data and add fresh one.

Assuming your have a .XML file for your activity that contains a RecyclerView which looks like this:

<android.support.v7.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/recycler_view"/>

And a .XML file for your item file, which looks like this:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/image_thumb_text_view" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/user_id_text_view" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/image_url_text_view" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/desc_text_view" />
</LinearLayout>

To display your data in a RecyclerView using a FriebaseRecyclerAdapter, please follow the next steps:

First, you need to find the RecyclerView in your activity and set the LinearLayoutManager like this:

RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));

Then you need to create the root reference of your Firebase database and a Query object like this:

DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
Query query = rootRef.child("Users");

Then you'll have to create a FirebaseRecyclerOptions object like this:

FirebaseRecyclerOptions<BlogPost> firebaseRecyclerOptions = new FirebaseRecyclerOptions.Builder<BlogPost>()
        .setQuery(query, BlogPost.class)
        .build();

In your activity class, create a holder class that looks like this:

private class BlogPostHolder extends RecyclerView.ViewHolder {
    private TextView imageThumbtextView, userIdTextView, imageUrlTextView, descTextView;

    BlogPostHolder(View itemView) {
        super(itemView);
        imageThumbtextView = itemView.findViewById(R.id.image_thumb_text_view);
        userIdTextView = itemView.findViewById(R.id.user_id_text_view);
        imageUrlTextView = itemView.findViewById(R.id.image_url_text_view);
        descTextView = itemView.findViewById(R.id.desc_text_view);
    }

    void setBlogPost(BlogPost blogPost) {
        String imageThumb = blogPost.getImageThumb();
        imageThumbtextView.setText(imageThumb);
        String userId = blogPost.getUserId();
        userIdTextView.setText(userId);
        String imageUrl = blogPost.getImageUrl();
        imageUrlTextView.setText(imageUrl);
        String desc = blogPost.getDesc();
        descTextView.setText(desc);
    }
}

Then create an adapter which is declared as global:

private FirebaseRecyclerAdapter<BlogPost, BlogPostHolder> firebaseRecyclerAdapter;

And instantiate it in your activity like this:

firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<BlogPost, BlogPostHolder>(firebaseRecyclerOptions) {
    @Override
    protected void onBindViewHolder(@NonNull BlogPostHolder blogPostHolder, int position, @NonNull BlogPost blogPost) {
        blogPostHolder.setBlogPost(blogPost);
    }

    @Override
    public BlogPostHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);

        return new BlogPostHolder(view);
    }
};
recyclerView.setAdapter(firebaseRecyclerAdapter);

In the end, don't forget to override the following two methods and start listening for changes:

@Override
protected void onStart() {
    super.onStart();
    firebaseRecyclerAdapter.startListening();
}

@Override
protected void onStop() {
    super.onStop();

    if (firebaseRecyclerAdapter!= null) {
        firebaseRecyclerAdapter.stopListening();
    }
}
Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • actually changing my class like you did helped :) thanks a lot. – R.Youssef Mar 20 '18 at 15:08
  • So I've tried this in two places, My Activity and my Fragment. This worked perfectly for my activity and I cant seem to figure out why it won't work for my fragment. @AlexMamo – Kristofer Mar 29 '18 at 21:13
  • @Kristofer Do you have an error? What is the behaviour? I suggest you add a fresh new question that contains an [mcve](https://stackoverflow.com/help/mcve), so me or other users can help you solve your issue. – Alex Mamo Mar 29 '18 at 21:21
  • I've posted it [link](https://stackoverflow.com/questions/49565349/why-my-recyclerview-shows-for-activity-but-not-for-fragment) @AlexMamo – Kristofer Mar 29 '18 at 21:32
  • Hi @AlexMamo, following the steps I find that my onBindViewHolder has a problem. It says that my "blogPostHolder.setBlogPost(blogPost) cannot be referenced from a static context, but making 'setBlogPost (Where the error is coming from) as static just makes my textview's inside the holder errors instead. Am I placing the onBindViewHolder in the wrong place? It's currently instead of my OnCreate instead of the activity I need the recycler. – Sean O Sep 07 '19 at 09:54
  • @SeanO This is a wroking example. So please post another fresh question using its own [MCVE](https://stackoverflow.com/help/mcve), so me and other Firebase developers can help you. – Alex Mamo Sep 07 '19 at 10:01
  • You forgot to add dependancy FirebaseUI database – Mahdi Safari Feb 05 '20 at 18:50
2

Its better to not include any firebase calls inside your ViewBinder for recycler view. What you can do is update your BlogPost and include a field name with getter and setter. Then inside your activity where you are adding the BlogPost to the recycler adapter you can fetch the user name add the name to the blog post and notify adapter of data change.

In your activity do this

// adding BlogPost to list
// Create BlogPost object with the data.
blogList.add(blogPostObject)
firebaseDatabase.child("Users").child(user_id).child("name").addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            if(dataSnapshot.exists()){
              blogPostObject.setName(dataSnapshot.getValue().toString());
              adapter..notifyItemChanged(indexOfblogPostObject);
            }
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });
Umar Hussain
  • 3,461
  • 1
  • 16
  • 38
1

The code below goes inside onBindviewhoder() under this line:

    String user_id = . . .. . . . 

I guess its the way you refer to your data and the way you attach the listener. Try to initialize the database ref like this:

  DatabaseReference firebaseDatabase= FirebaseDatabase.getInstance().getReference().child("Users").child(user_id);

and now try to listen like this:

   firebaseDatabase.addValueEventListener(new ValueEvent....{

    onDataChange(.....){

     //your same code

    }



   })
Hasan Bou Taam
  • 4,017
  • 2
  • 12
  • 22
0
 public void checklocation() {


        recyclerView=findViewById(R.id.recy);
        FirebaseDatabase database = FirebaseDatabase.getInstance();
        
        databaseReference = database.getReference();

        databaseReference.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull @NotNull DataSnapshot snapshot) {
                for (DataSnapshot snapshot1: snapshot.getChildren()){
                   Model_lats model_lats=snapshot1.child("location").getValue(Model_lats.class);
                    latslist.add(model_lats);

                }
                adapter_lats=new Adapter_lats(MapsActivity.this,latslist);
                recyclerView.setAdapter(adapter_lats);
                adapter_lats.notifyDataSetChanged();

            }

            @Override
            public void onCancelled(@NonNull @NotNull DatabaseError error) {
                Toast.makeText(MapsActivity.this, "cant read"+error, Toast.LENGTH_SHORT).show();
            }
        });
    }
Muhammad Zahab
  • 1,049
  • 10
  • 21