0

I had tried to retrieve data from firebase database and show in recyclerview. Everything is going perfect. Now in my firebase database has lots of node for recyclerview and each node has image link and now i just seen that recyclerview only show when all images are loaded from firebase database first. Database has a string and long both types of values. But No any text values display until all images are not loaded. Here i shows what am i tried. So the question is how to show recyclerview step by step. if Text("string") is loaded than why it waiting for images loading?

mAdapter = new PostAdapter(MainActivity.this);
query = PostRef
                    .orderByChild("timestamp")
                    .limitToLast(mPostsPerPage);
query.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                List<Post> userModels = new ArrayList<>();


                for (DataSnapshot userSnapshot : dataSnapshot.getChildren()) {
                    String o=userSnapshot.getKey();
                    userModels.add(userSnapshot.getValue(Post.class));
                }

                mAdapter.addAll(userModels);

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
            }
        });

this is my adapter

public class PostAdapter extends RecyclerView.Adapter<PostHolder>
{
    List<Post> mPost;
    Context mContext;
    Boolean likecheck=false;

    public PostAdapter(Context c) {
        this.mPost  = new ArrayList<>();
        mContext=c;
    }

    @NonNull
    @Override
    public PostHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        return new PostHolder(LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.all_post_layout, viewGroup, false));

    }
@Override
    public void onBindViewHolder(@NonNull final PostHolder postHolder, final int i) {
        postHolder.setData(mPost.get(i));
        final String PostKey=mPost.get(i).getPostid();
        FirebaseAuth mAuth=FirebaseAuth.getInstance();
        final String currentUserID=mAuth.getCurrentUser().getUid();
        final DatabaseReference post=FirebaseDatabase.getInstance().getReference().child("Posts");
post.child(PostKey).child("postimg").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot)
            {
                if (dataSnapshot.exists())
                {
                    for (DataSnapshot dataSnapshot1:dataSnapshot.getChildren())
                    {
                        String postimagelink =dataSnapshot1.getValue().toString();
                        postimagelist.add(postimagelink);
                    }


                    String[] urls =postimagelist.toArray(new String[postimagelist.size()]);
                    postHolder.mPager.setAdapter(new SlidingImage_Adapter(mContext,urls));

                    postHolder.indicator.setViewPager(postHolder.mPager);

                    final float density = mContext.getResources().getDisplayMetrics().density;
                    postHolder.indicator.setRadius(5 * density);

                    postHolder.NUM_PAGES = urls.length;



                    postHolder.indicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener()
                    {

                        @Override
                        public void onPageSelected(int position) {
                            postHolder.currentPage = position;
                        }

                        @Override
                        public void onPageScrolled(int pos, float arg1, int arg2) {
                        }

                        @Override
                        public void onPageScrollStateChanged(int pos) {
                        }
                    });
                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });
@Override
    public int getItemCount() {
        return mPost.size();
    }
    public void addAll(List<Post> newPost) {
        int initialSize = mPost.size();
        mPost.addAll(newPost);
        notifyItemRangeInserted(initialSize, newPost.size());
    }

    public String getLastItemId() {
        return mPost.get(mPost.size() - 1).getPostid();
    }

}

viewholder

public class PostHolder extends RecyclerView.ViewHolder
{
    AutoLinkTextView description;
    TextView postfullname;
ViewPager mPager;
        int currentPage = 0;
        int NUM_PAGES = 0;
        CirclePageIndicator indicator;
        Context context;
public PostHolder(@NonNull View itemView) {
        super(itemView);

         postfullname = itemView.findViewById(R.id.user_post_full_name);
         description = itemView.findViewById(R.id.user_post_description);

         mPager = (ViewPager) itemView.findViewById(R.id.pager);
         indicator = (CirclePageIndicator)itemView.findViewById(R.id.indicator);

    }
    public void setData(Post post)
    {
        description.setText(post.getDescription());
        postfullname.setText(post.getFirstname()+" "+post.getLastname());
    }
}

enter image description here

tailor
  • 373
  • 4
  • 19

2 Answers2

0

Ok, I see a couple of problems with your adapter.

First

 public void addAll(List<Post> newPost) {
        int initialSize = mPost.size();
        mPost.addAll(newPost);
        notifyItemRangeInserted(initialSize, newPost.size());
    }

Here, you are passing a list of Post as a parameter, you are using mPost.size() wich will not do anything there, the addAll method can be replaced with just add and the newPost.size() could be empty as well as the mPost.size()

Second

@Override
    public int getItemCount() {
        return mPost.size();
    }

You should also handle if the list is empty

@Override
    public int getItemCount() {
        if(mPost.size() > 0){
          return mPost.size();
         }else{
           return 0;
           }
    }

Third

All your firebase code inside your onBindViewHolder is wrong because while you are binding the data into each row, you are also trying to get each time the values with firebase. Doing this will lend to multiple firebase calls to get the data for just 1 row instead of getting all the data that you want to show.

The solution of this is to do all your firebase logic in your main activity and pass the data to the adapter to set the values.

To solve this with a more cleaner approach, pass your Array as a parameter in your Adapter Constructor, delete the addAll method and do this.

 public PostAdapter(Context c, List<Post> mPost) {
        this.mPost  = mPost
        mContext=c;
    }

Then as I said, delete all the firebase code from your onBindViewHolder and place it in your MainActivity

With the constructor changed of your Adapter, you should now use your data fetched from firebase like this to work with your adapter.

query = PostRef
                    .orderByChild("timestamp")
                    .limitToLast(mPostsPerPage);
query.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                List<Post> userModels = new ArrayList<>();


                for (DataSnapshot userSnapshot : dataSnapshot.getChildren()) {
                    String o=userSnapshot.getKey();
                    userModels.add(userSnapshot.getValue(Post.class));
                }

                mAdapter = new PostAdapter(MainActivity.this,userModels)

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
            }
        });

If you want to notify your adapter about any change. Just make a global declaration of the adapter like

private PostAdapter adapter;

and then just use

adapter.notifySetDataChanged(); 

Make sure the instance has been executed at least once.

mAdapter = new PostAdapter(MainActivity.this,userModels);
Gastón Saillén
  • 12,319
  • 5
  • 67
  • 77
  • you are binding the data into each row, you are also trying to get each time the values with firebase. Doing this will lend to multiple firebase calls to get the data for just 1 row instead of getting all the data that you want to show. its necessary because nodes are link with unique id – tailor Jul 04 '19 at 17:36
  • you means i should remove firebase listner from onbindviewholder? – tailor Jul 04 '19 at 17:39
  • Here i set postHolder.mPager and it is itemview how to set in mainactivity – tailor Jul 04 '19 at 17:45
  • yes, you don't need to bind one listener per node, since you get all that data inside your ArrayList and then pass that data to the adapter, the adapter will then be the responsible to access that array data and bind each array object to each recyclerview row – Gastón Saillén Jul 04 '19 at 17:52
  • You can use interfaces to use components from your adapter within your mainactivity, take a look https://stackoverflow.com/questions/52886536/how-to-create-a-interface-in-main-activity-and-pass-the-data-from-adapter-to-mai – Gastón Saillén Jul 04 '19 at 17:53
  • simple example: suppose if post has 3 comment(comment node has three child)..,for this i have to use listner for getting total child comment of each post...,if i am wrong please correct it – tailor Jul 04 '19 at 17:58
  • i added image for clarification – tailor Jul 04 '19 at 18:04
  • When you do this `post.child(PostKey).child("postimg")` inside your onBindViewHolder, you are getting the post image every time a view is inflated, instead of that, you should get all the data you want to work with the post inside your main activity and then pass that array of data inside your adapter – Gastón Saillén Jul 04 '19 at 18:28
  • is there any example for this work in manactivity and pass in adapter – tailor Jul 04 '19 at 18:32
0

Firebase has released firebaseRecyclerViewAdapter class. This will do a lot of the work for you.

the adapter takes 4 input arguments:

  • Object class
  • List item layout resource
  • Viewholder Class
  • Query/firebase reference

the populateViewHolder method is all that will be required

FirebaseRecyclerViewAdapter<Object, ObjectViewHolder> adapter = new FirebaseRecyclerViewAdapter<Object, ObjectViewHolder>
(Object.class, android.R.layout.*list_item_layout*, ObjectViewHolder.class, objectQuery) {
    public void populateViewHolder(ObjectViewHolder ObjectViewHolder, Object Object) {

    //populate your views, example:
        ObjectViewHolder.textview.setText(object.getTextOne());

    }
};

Then set the adapter:

recycler.setAdapter(mAdapter);

More info on this Firebase RecyclerView Adapter

Haider Malik
  • 1,581
  • 1
  • 20
  • 23