Search code examples
androidandroid-fragmentsandroid-viewpagerfragment-lifecycle

Complications with Viewpager


I am using a ViewPager and have 3 Fragments in it.

I know that when the ViewPager loads for the first time it loads all the fragments by default ,

minimum is 3 fragments and viewpager executes all the lifecycle methods of fragments.

Problems I noticed :

  • Whenever I swipe the viewpager the selected fragment doesn't call any of its lifecycle methods again.

  • Hence I cannot access the global variables directly.

    e.g: if I have a preference initialized in OnCreateView() I am getting NPE when I try to access it from activity by initializing the instance of that fragment and calling a method in that fragment.

  • Also not even onResume is getting called for any fragment after first loading.

What I want to Know :

  • How can I access the views and preferences after I have already initialized them in onCreateView() ?

  • But for the button which is initialized in onCreateView(), on click of it calls the web-services and works perfectly as I want .How ?

I have stuck with these issues from the past 3 days and googled a lot but not found my answers.

Any Help will be appreciated.

Fragment Code :

 @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,   
                  Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.activity_dth, container, false);

   preferences= 
    PreferenceManager.getDefaultSharedPreferences(getActivity());
    editor = preferences.edit();


    editAmt = (EditText) v.findViewById(R.id.editAmt);
    browsplan = (Button) v.findViewById(R.id.browsplan);


    Log.e("DTH onCreateView ",""+page);

    String fontpath = "fonts/OpenSans-Regular.ttf";
    Typeface tf = Typeface.createFromAsset(getActivity().getAssets(), 
    fontpath);

    editCustomerid.setTypeface(tf);
    editAmt.setTypeface(tf);

    token=preferences.getString("Token","-1");
    mobileNo=preferences.getString("userMobileNo","0");



    browsplan.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

          //new DTHOperator(getActivity()).execute();   HERE IT WORKS PROPERLY
        }
    });

    return v;
}

 @Override
 public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);

    if(isVisibleToUser && isResumed()){
        new DTHOperator(getActivity()).execute();
        Toast.makeText(getActivity(),"setUserVisibleHint DTH  " 
       ,Toast.LENGTH_SHORT).show();
       }
    }

  public class DTHOperator extends AsyncTask<Context, Integer, String> {
    Context ctx;

    DTHOperator(Context ctx) {
        this.ctx = ctx;
    }

    @Override
    protected String doInBackground(Context... params) {
        List<NameValuePair> telecomdata = new ArrayList<NameValuePair>();
        String result;
        Log.d("Register Activity Token", /*preferences.getString("Token", "")*/token);

        telecomdata.add(new BasicNameValuePair("mobNo", /*preferences.getString("userMobileNo", "")*/mobileNo));
        telecomdata.add(new BasicNameValuePair("requestDate", Utilities.getApiCallTimeFormat()));
        telecomdata.add(new BasicNameValuePair("reqFrom", "APP"));
        Log.v("PostParameters", "telecomdata" + telecomdata);
        if (Connectivity.checkNetwork(ctx)) {
            result = rechargeUrl.queryRESTurlONline(ctx, "/GetDTHOperatorsService", "post", telecomdata, GenericConstants.PiPay_root);
        } else {
            result = GenericConstants.NETWORKNOTFOUND;
            displayToast("The Internet Connection appears to be offline");
        }


        return result;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        MyProgress.show(ctx, "", "");
    }

    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);

        try {
            if (result != null) {
                if (result.equalsIgnoreCase(GenericConstants.NETWORKNOTFOUND)) {
                    MyProgress.CancelDialog();
                    Toast.makeText(ctx,"The Internet Connection appears to be offline",Toast.LENGTH_SHORT).show();
                    return;
                }
                String[] resArr = result.split("delimiter_");
                if (!resArr[0].equals("500")) {
                    MyProgress.CancelDialog();
                    Log.d("DTHOperator Response ::", result);
                    JSONArray jsonArray = null;
                    JSONObject object = new JSONObject(result);
                    jsonArray = object.getJSONArray("data");

                    for (int i = 0; i < jsonArray.length(); i++) {
                        if (jsonArray.getJSONObject(i).has("opName")) {
                            operator.add(jsonArray.getJSONObject(i).getString("opName"));

                            Log.d("", " Question size " + operator.size());
                        }
                    }

                    if(MyProgress.isShowingProgress())
                        MyProgress.CancelDialog();

                        editAmt.setText("2000");  // NPE HERE Also setting the value  

                } else {
                    MyProgress.CancelDialog();
                    displayToast("Failure");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            MyProgress.CancelDialog();
        }finally {
            if(MyProgress.isShowingProgress())
                MyProgress.CancelDialog();
        }

    }

Solution

  • You can keep instances of Fragments which you use in your ViewPager. Here's an example:

    In your ViewPagerAdapter define a SparseArray and override the methods like below:

    SparseArray<Fragment> registeredFragments = new SparseArray<>();
    
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        registeredFragments.put(position, fragment);
        return fragment;
    }
    
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        registeredFragments.remove(position);
        super.destroyItem(container, position, object);
    }
    
    public Fragment getRegisteredFragment(int position) {
        return registeredFragments.get(position);
    }
    

    And add a PageChangeListener to your ViewPager:

    viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    
        }
    
        @Override
        public void onPageSelected(int position) {
            // Here's your fragment instance
            // You can access it's methods, variables, views etc.
            YourFragment fragment =(YourFragment)yourPagerAdapter.getRegisteredFragment(position);
    
        }
    
        @Override
        public void onPageScrollStateChanged(int state) {
    
        }
    });
    

    I hope this'll help you. Good luck!

    Edit:

    You can define your Fragment's Views public or define a getter method for your view and access from your fragment instance such as:

    public TextView mTextView;
    

    or

    private TextView mTextVieW;
    public TextView getTextView(){
       return this.mTextView;
    }
    

    You can access it like below:

    TextView textView = yourFragmentInstance.mTetxView;
    

    or

    TextView textview = yourFragmentInstance.getTextView();