4

The story is that there are five fragments in the ViewPager and each fragment have to run an AsyncTask when it is appeared on ViewPager. I'm trying exactly the same as the correct answer here. I put my AsyncTask in onResumeFragment and it works fine. But the problem starts when I update the view after AsyncTask run successfully. Because the view(RecyclerView) I created inside onCreateView is being null when I update the dataset. I also tried setRetainInstance(true) inside onActivityCreated. It didn't work.

Here is my code,

MainActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //Tab & Pager

    viewPager = (ViewPager) findViewById(R.id.main_tab_content);
    adapter = new ViewPagerAdapter(getSupportFragmentManager());
    viewPager.setAdapter(adapter);
    addPagerListener();
}

private void addPagerListener(){
    viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {

        @Override
        public void onPageSelected(int newPosition) {
            FragmentLifecycle fragmentToShow = (FragmentLifecycle)adapter.getItem(newPosition);
            fragmentToShow.onResumeFragment();

            FragmentLifecycle fragmentToHide = (FragmentLifecycle)adapter.getItem(currentPosition);
            fragmentToHide.onPauseFragment();

            currentPosition = newPosition;
        }

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });
}

class ViewPagerAdapter extends FragmentPagerAdapter {
    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

    @Override
    public Fragment getItem(int position) {
        switch (position){
            case 0: return new HomeFragment();
            case 1: return new ArtistFragment();
            case 2: return new AlbumsFragment();
            case 3: return new CollectionsFragment();
            case 4: return new SavedFragments();
            default: return null;
        }
    }

    @Override
    public int getCount() {
        return 5;
    }

}

Here's my 2nd Fragment code, the rest fragments are blank so far.

public class ArtistFragment extends Fragment implements FragmentLifecycle{

@BindView(R.id.recyclerview_frag_artist)
RecyclerView mRecyclerView;

RecyclerView.Adapter mAdapter;
RecyclerView.LayoutManager mLayoutManager;

@BindView(R.id.radioBtnAll_frag_artist)
RadioButton radioAll;

@BindView(R.id.radioBtnMale_frag_artist)
RadioButton radioMale;

@BindView(R.id.radioBtnFemale_frag_artist)
RadioButton radioFemale;

ArrayList<String> vocalistNameList;
Context context;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_artists, container, false);
    ButterKnife.bind(this, rootView);
    mRecyclerView.setHasFixedSize(true);
    mLayoutManager = new LinearLayoutManager(rootView.getContext());
    mRecyclerView.setLayoutManager(mLayoutManager);
    vocalistNameList = getDummyDS();
    mAdapter = new MyListAdapter(vocalistNameList);
    mRecyclerView.setAdapter(mAdapter);
    radioAll.setChecked(true);
    return rootView;
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    setRetainInstance(true);
}

private ArrayList<String> getDummyDS(){
    ArrayList<String> dataset = new ArrayList<>();
    dataset.add("test");
    dataset.add("test");
    dataset.add("test");
    dataset.add("test");
    dataset.add("test");
    dataset.add("test");
    dataset.add("test");
    return dataset;
}

private void getVocalistList(Gender gender){
    RequestParams params = new RequestParams();
    params.put("action","query");
    params.put("list","allcategories");
    params.put("acprefix","Vocalist-");
    params.put("format","json");
    params.put("aclimit","5000");

    MyRestClient.get("", params, new JsonHttpResponseHandler(){
        ProgressDialog progress;
        @Override
        public void onStart() {
            progress = ProgressDialog.show(AppConstants.activity, "Please wait",
                        "Loading data..", true);
            progress.setCancelable(true);
        }

        @Override
        public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
            progress.dismiss();
            vocalistNameList = new ArrayList<String>();
            try {
                JSONArray vocalListJsonArray = response.getJSONObject("query").getJSONArray("allcategories");
                for(int i=0; i<vocalListJsonArray.length(); i++){
                    String vocalListName = vocalListJsonArray.getJSONObject(i).getString("*").replace("Vocalist-","");
                    vocalistNameList.add(vocalListName);
                }
                mAdapter = new MyListAdapter(vocalistNameList);
                mRecyclerView.setAdapter(mAdapter); // <= RecyclerView is null and app is crashing
                mAdapter.notifyDataSetChanged();
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
            progress.dismiss();
            Log.i("failure", "failed "+statusCode);
        }
    });
}

@Override
public void onPauseFragment() {
    Toast.makeText(AppConstants.applicationContext, "onPauseFragment():", Toast.LENGTH_SHORT).show();
}

@Override
public void onResumeFragment() {
    getVocalistList(Gender.All);
    Toast.makeText(AppConstants.applicationContext, "onResumeFragment(): Artist", Toast.LENGTH_SHORT).show();
}

I have been trying to solve that problem since a week ago. Any idea? (P.S I'm using Android Asynchronous Http Client instead of AsyncTask)

Community
  • 1
  • 1
Zin Win Htet
  • 2,448
  • 4
  • 32
  • 54

2 Answers2

1

You have to do something like this:

pager.setOffscreenPageLimit([quantity of tabs that you would like to keep in memory]);

Take a look at this answer. ViewPager inside Fragment loses content when returning to it

Community
  • 1
  • 1
Sebastian Corradi
  • 1,353
  • 2
  • 14
  • 25
1

Finally I got the solution. It's very simple.

  1. I removed implementation of the interface FragmentLifecycle.
  2. Added the following method to each fragments.

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
         super.setUserVisibleHint(isVisibleToUser);
         if(isVisibleToUser && !isDataLoaded){
             getVocalistList(Gender.All);
             isDataLoaded = true;
         }
    }
    

It works like magic. I don't need to worry to lose RecyclerView instance since it was not being null when setUserVisibleHint method is called.

Zin Win Htet
  • 2,448
  • 4
  • 32
  • 54
  • 1
    well, I'm not very convinced that this is "very simple", but thank you for your findings... I'm using ButterKnife and I see that my RecyclerView is being dismissed and a new one is created every time I return to the Fragment inside the ViewPager... Where can I find that `FragmentLifecycle` interface? and also, why set the isDataLoaded flag to true before the AsyncTask is finished with loading data? – rexxar Feb 24 '17 at 15:08
  • 1
    Sorry for my late reply, here you go. http://stackoverflow.com/a/24386516/1976608 – Zin Win Htet Feb 28 '17 at 18:37