Search code examples
androidasynchronousandroid-viewpagerandroid-recyclerviewgreenrobot-eventbus

Asynchronous Firebase Task with RecyclerView in ViewPager


  • I'm trying to load the same RecylerView data from the Firebase, and later change simple things in the data
  • Problem: in case of 2 pages No data is appearing on the first page, in case of 3 pages the first pages is not appearing and the startup and if I moved to the 3rd one and then back to the 1st, data appears + if I left the phone for a while so that it goes to idle and then on screen on (it hits onResume I think), it shows the data but with data duplicated (Just noticed that now)
  • NB: I'm using EventBus GreenBot Library to pass the DataSnapshot that contains the RecyclerView data
  • NB: the RecyclerVIew is in a class and the ViewPager is in another Class

EventBus Code

EventBus.getDefault().postSticky
           (new ViewPagerAndDataSnapshotSender(dataSnapshot.child("Examples"), viewPager));

Where ViewPagerAndDataSnapshotSender is a data object class I have created to send both the DataSnapshot to use it and the viewPager to notify it with changes

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
    adapter.notifyDataSetChanged();
}

@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(ViewPagerAndDataSnapshotSender ss) {
    fetchExamples(ss.getDataSnapshot(), ss.getViewPager());
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}

ViewPagerAdapter

class ViewPagerAdapter extends FragmentPagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();

    ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

    @Override
    public Fragment getItem(int position) {
        return new ExampleRecyclerViewFragment(); // the same recycler view I want to return every pagee
    }

    @Override
    public int getCount() {
        return mFragmentTitleList.size();
    }

    void addFragment(String title) {
        //mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mFragmentTitleList.get(position);
    }
}

The invocation of ViewPager (in onCreate)

viewPager = (ViewPager) rootView.findViewById(R.id.viewpager);
TabLayout tabLayout = (TabLayout) rootView.findViewById(R.id.tabs);
setupViewPager(viewPager);
tabLayout.setupWithViewPager(viewPager);
fetchData();  // a method in which the EventBus data are set in asynchronous Firebase method

setupViewPager Method

private void setupViewPager(ViewPager viewPager) {
        ViewPagerAdapter adapter = new ViewPagerAdapter(getFragmentManager());
        adapter.addFragment("Fragment");
        adapter.addFragment("Activity");
        //adapter.addFragment("SASDF");
        viewPager.setAdapter(adapter);
    }

ExampleRecyclerViewFragment (The RecyclerView) I think the error is here in onCreate

RecyclerView exampleRecyclerView = (RecyclerView)                
rootView.findViewById(R.id.ExampleRecyclerView);
exampleList = new ArrayList<>();
adapter = new ExampleAdapter(exampleList);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
llm.setSmoothScrollbarEnabled(true);
exampleRecyclerView.setScrollbarFadingEnabled(true);
exampleRecyclerView.setLayoutManager(llm);
exampleRecyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();

The Asynchronous by which the data get called

public void fetchExamples(final DataSnapshot Examples, final ViewPager viewPager2) {
    //noinspection StatementWithEmptyBody
    if (Examples.hasChild("Method 2")) {  // If has multi example

    } else {  // If has only one example

        Examples.getRef().child("Method 1").addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                example.setStepName(String.valueOf(dataSnapshot.getKey()));

                for (DataSnapshot childSnapshot : dataSnapshot.child("Code").getChildren()) {
                    example.addCode(String.valueOf(childSnapshot.getValue()));
                    //Log.i("CodeValue", String.valueOf(childSnapshot.getValue()));
                }

                for (DataSnapshot childSnapshot : dataSnapshot.child("Explaination").getChildren()) {
                    example.addExplanation(String.valueOf(childSnapshot.getValue()));
                    //Log.i("ExplanationValue", String.valueOf(childSnapshot.getValue()));
                }
                example.addExample();
                exampleList.add(example.getExampleObject());
                viewPager2.getAdapter().notifyDataSetChanged(); // viewPager by EventBus
                viewPager.getAdapter().notifyDataSetChanged(); // viewPager by static
                adapter.notifyDataSetChanged();
            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {

            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onCancelled(FirebaseError firebaseError) {

            }
        });
    }
}

Any Ideas about what I have missed in the code?

Thanks in advance, any other needed Class's code just let me know


Solution

  • From code snippet you have shared I guessed you are missing this 2 things

    No Data Problem: - You should show loading animation till examples are fetched from firebase.
    Once data is fetched from the server only then you should add Fragments in your ViewPager. You are adding fragments with empty ArrayList. Swiping to page 3 and come back to 1 or put the phone to idle give enough time for network call to completes and RecyclerView render data.

    Alternatively, you can call fetch example in individual fragments. Show loading animation till request completion and only then assign adapter to RecyclerView.

    Data Duplication Problem: - You should clear older list data after successful completion of network call and then refill it with new data. You are adding new data in same list leading to duplication of data. If the request has failed, show some error message but keep older data.

    Please share entire class so that I can pinpoint exactly what is wrong.