ViewPager not getting updated using FragmentStatePagerAdapter
Asked Answered
I

6

6

I've a two fragments, Fragment A and Fragment B. Fragment A lists all products and Fragment B shows detail about the product and its images.

Fragment A calls Fragment B and Fragment B fetches data from web service and sets to ViewPager using Adapter. Initially it displays correct image but thereafter it always have the same image.

If we fire adapter.notifyDataSetChanged() from any click event in Fragment B then it works perfectly but we have to display as soon as Fragment B is visible

Please look at the code.

 public class ImageAdapter extends FragmentStatePagerAdapter {

private List<String> pImageURL;

public ImageAdapter(FragmentManager fm, List<String> imageURL) {
    super(fm);
    // TODO Auto-generated constructor stub

    pImageURL = imageURL;
    Utility.showLog("PagerAdapter URL", pImageURL.toString());
}

@Override
public Fragment getItem(int position) {
    // TODO Auto-generated method stub

    String imageURL = pImageURL.get(position);

    PImageFragment imageFragment = (PImageFragment) PImageFragment.getInstance();
    imageFragment.setProductImage(imageURL);
    return imageFragment;
}

@Override
public int getItemPosition(Object object) {

    PImageFragment pFragment = (PImageFragment) object;
    String URL = pFragment.getProductImage();

    int position = pImageURL.indexOf(URL);

    if (position>=0)
        URL = pImageURL.get(position);

    if (position>=0)
        return position;
    else
        return POSITION_NONE;

}

@Override
public int getCount() {
    // TODO Auto-generated method stub
    return pImageURL.size();
   }
}

Fragment B

   // Image Loading Process

    String p_images = model.getProdutImageURL();

    imageUrl = new ArrayList<String>();


    if (p_images.contains(","))
        imageUrl.addAll(Arrays.asList(p_images.split(",")));
    else
        imageUrl.add(p_images);

    mAdapter = new ImageAdapter(getChildFragmentManager(), imageUrl);
    vPager.setPageTransformer(true, new DepthPageTransformer());
    vPager.setAdapter(mAdapter);

    mAdapter.notifyDataSetChanged();

PImage Fragment

public class PImageFragment extends Fragment {

private static final String TAG = "Product_Images_Fragment";
private View rootView;

private ImageView ivProductImage;
private String imageURL;

@Override
@Nullable
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // TODO Auto-generated method stub

    rootView = inflater.inflate(R.layout.p_detail_images, container, false);

    bindComponents(rootView);
    loadImages(imageURL);
    return rootView;

}

public static Fragment getInstance() {

    Fragment fragment = new PImageFragment();
    return fragment;
}

public void setProductImage(String image_url) {

    imageURL = image_url;
    Utility.showLog(TAG + "Received URL : ", imageURL.toString());

}

public String getProductImage() {

    return imageURL;
}

private void bindComponents(View v) {
    // TODO Auto-generated method stub

    ivProductImage = (ImageView) v.findViewById(R.id.ivProductImage);
}

private void loadImages(String image_url) {


    Utility.showLog(TAG,
            "http://baryapp.com/kickzexchange/assets/products/" + image_url);

    final ProgressBar pBar = (ProgressBar) rootView
            .findViewById(R.id.pBarLoader);
    pBar.setVisibility(View.VISIBLE);

    ImageLoader imgLoader = VolleySingleton.getInstance().getImageLoader();

    imgLoader.get("http://baryapp.com/kickzexchange/assets/products/"
            + image_url, new ImageListener() {

        @Override
        public void onErrorResponse(VolleyError vError) {
            // TODO Auto-generated method stub

            Utility.showLog(TAG, vError.getLocalizedMessage() + "");
        }

        @Override
        public void onResponse(ImageContainer response, boolean result) {
            // TODO Auto-generated method stub

            if (response.getBitmap() != null) {

                ivProductImage.setImageBitmap(response.getBitmap());
                pBar.setVisibility(View.GONE);

            }
        }
    });
}


}

UPDATE

    // This is how fragment A calls fragment B
   ProductDetail pDetail = new ProductDetail();

    Bundle bundle = new Bundle();
    bundle.putString("product_id", productList.get(position).getProductID());
    bundle.putString("product_title", productList.get(position).getProductTitle());

    pDetail.setArguments(bundle);

    ((MyTabActivity) mActivity).navigateFragment(Utility.BUY_TAB, pDetail,
            true);

LATEST UPDATE

    public void navigateFragment(String tag, Fragment fragment,
                             boolean shouldAdd) {

    FragmentManager manager = getSupportFragmentManager();
    FragmentTransaction ft = manager.beginTransaction();


    if (shouldAdd)
        mStacks.get(tag).push(fragment); // push fragment on stack


    if (mCurrentFragment != null) {
        saveFragmentState(mCurrentFragment.getClass().getName(), mCurrentFragment);
    }

    mCurrentFragment = fragment;
    restoreFragmentState(fragment.getClass().getName(), fragment);


    ft.replace(android.R.id.tabcontent, fragment);

    ft.commit();


}

 @Override
protected void onSaveInstanceState(Bundle outState) {
    // TODO Auto-generated method stub
    super.onSaveInstanceState(outState);

    Bundle fragmentStates = new Bundle(mFragmentStates.size());

    for (Map.Entry<String, Fragment.SavedState> entry : mFragmentStates.entrySet()) {
        fragmentStates.putParcelable(entry.getKey(), entry.getValue());
    }
    outState.putParcelable(KEY_FRAGMENT_STATES, fragmentStates);
}


private void saveFragmentState(String id, Fragment fragment) {

    Fragment.SavedState fragmentState =
            getSupportFragmentManager().saveFragmentInstanceState(fragment);
    mFragmentStates.put(id, fragmentState);


}

private void restoreFragmentState(String id, Fragment fragment) {

    Fragment.SavedState fragmentState = mFragmentStates.remove(id);

    if (fragmentState != null) {

        if (!fragment.isAdded())
            fragment.setInitialSavedState(fragmentState);

    }

}

Any help/idea is highly appreciated.

Ion answered 20/8, 2015 at 22:9 Comment(14)
Looking at this line: PImageFragment imageFragment = (PImageFragment) PImageFragment.getInstance(); Is PImageFragment a singleton?Mohican
@Bill Phillips I've added PImageFragment codeIon
Can you post the Fragment A code as well? Especially the snippet where in which you are inflating Frgament B.Mccarver
@AbhishekV inflating Fragment B or Fragment A?Ion
@ droid_dev Fragment B.Mccarver
@AbhishekV check updated codeIon
@ droid_dev You mean to say that first page of the ImageAdapter shows proper image but rest of the pages shows the same image from first page?Mccarver
@AbhishekV Nopes. I've list of products in fragment A, when we go from fragment A to Fragment B, we load product related info from web service. so for the first time it works but when i go back to fragment A and choose any product and navigates to fragment B. It doesn't display image.Ion
@droid_dev Okay. Could you post the code for ((MyTabActivity) mActivity).navigateFragment How exactly you are inflating the fragment there? is it .replace() or .add()?Mccarver
@AbhishekV It's .replaceIon
Let us continue this discussion in chat.Ion
@ droid_dev Hmm. Code looks fine to me except this part - imageFragment.setProductImage(imageURL); I don't think you should be passing the data that way. Instead pass it through bundle/arguments. That way if the fragment is destroyed and then recreated it can restore the data. I am not sure if it is the cause of this issue though.Mccarver
Can you post code for navigateFragment?Schlessinger
@Schlessinger Posted navigateFragment codeIon
I
0

Finally I've solved the answer. Fragment B was getting saved in navigateFragment, it has been prevented by the following line and it worked.

    if (mCurrentFragment != null) {
        if (mCurrentFragment instanceof GetProducts)
            saveFragmentState(mCurrentFragment.getClass().getName(), mCurrentFragment);
    }
Ion answered 3/9, 2015 at 21:14 Comment(0)
T
1

You could use the following PagerFragment base class for your fragments and implement the onVisible method as needed:

public abstract class PagerFragment extends Fragment {

private boolean mCalled = false;
private boolean mWasVisible = false;
private boolean mIsResumed = false;
private boolean mIsPaused = false;

protected void onVisible() {
    mCalled = true;
}

protected void onHide() {
    mCalled = true;
}

/**
 * {@inheritDoc}
 * 
 * @see android.app.Fragment#onAttach(android.app.Activity)
 */
@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
}

/**
 * {@inheritDoc}
 * 
 * @see android.app.Fragment#onDetach()
 */
@Override
public void onDetach() {
    super.onDetach();
}

/**
 * {@inheritDoc}
 * 
 * @see android.app.Fragment#onResume()
 */
@Override
public void onResume() {
    super.onResume();
    mIsPaused = false;
    mIsResumed = true;
    setUserVisibleHint(getUserVisibleHint());
}

/**
 * {@inheritDoc}
 * 
 * @see android.app.Fragment#onPause()
 */
@Override
public void onPause() {
    mIsPaused = true;
    mIsResumed = false;
    setUserVisibleHint(getUserVisibleHint());
    super.onPause();
}

/**
 * {@inheritDoc}
 * 
 * @see android.app.Fragment#setUserVisibleHint(boolean)
 */
@Override
public void setUserVisibleHint(boolean visible) {
    super.setUserVisibleHint(visible);

    mCalled = false;
    if (mIsResumed) {
        if (visible) {
            performVisible();
            mWasVisible = visible;
        } else {
            performHide();
            mWasVisible = visible;
        }
    } else if (mIsPaused && mWasVisible) {
        performHide();
        mWasVisible = visible;
    }

}

private void performVisible() {
    onVisible();
    if (!mCalled) {
        throw new SuperNotCalledException("PagerFragment " + this + " did not call through to super.onVisible()");
    }
}

private void performHide() {
    onHide();
    if (!mCalled) {
        throw new SuperNotCalledException("PagerFragment " + this + " did not call through to super.onHide()");
    }
}

}

Or you simply preload the Data on Fragment A and notify the Adapter when the data was loaded and then change the pages. It is always possible to add and remove Fragments dynamically from a ViewPager. So you may also try to delete the fragment with the old data and create a new one afterwards.

Tasimeter answered 26/8, 2015 at 15:43 Comment(0)
J
1

You haven't shown the critical part, i.e. MyTabActivity.navigateFragment().

I believe you are trying to reuse existing instance of Fragment B, and pass new data by Fragment.setArguments()

setArguments has no use after Fragment is created

either

  1. create new instance of Fragment B every time and replace the existing one, or
  2. pass the new data using custom functions in the fragment

I would suggest method 1, because by doing this, your data is kept even if Activity recreation, e.g. screen rotation. For method 2, you need extra work to handle Activity recreate.

Johny answered 28/8, 2015 at 17:40 Comment(1)
Whenever fragment B is loaded, fragment A state is saved and B is destroyed fragment A state is restored.Ion
A
1

If I understand you correctly, you can try using the onPageSelected and depending on position update your fragments on which page is selected.

        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        }

        @Override
        public void onPageSelected(int position) {

        //use this to run when your fragment is visible

        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });
Ardy answered 29/8, 2015 at 22:12 Comment(0)
O
1

As you might know you are loading the image in a background process. i.e using volley library.

your adapter.notifyDataSetChanged() is being called even before the process is finished. But as you told when you call it on the button click it works, then solution is simple.

Call your adapter.notifyDataSetChanged() after the background process is completed.

i.e inside :-

  @Override
      public void onResponse(ImageContainer response, boolean result) {
        // TODO Auto-generated method stub

        if (response.getBitmap() != null) {

            ivProductImage.setImageBitmap(response.getBitmap());
            pBar.setVisibility(View.GONE);
            adapter.notifyDataSetChanged();
        }
    }
});
Olivenite answered 31/8, 2015 at 6:17 Comment(4)
I get null response then how i can call from onResponseIon
if there is an image at the specified url. you will get response. right? And if the response is null, that might be because there might not be any image at the url.Olivenite
I am getting image URL in fragment pager Adapter constructor but in pImageFragment it shows null.Ion
If i get the correct image then there is no issue of loading imageIon
S
0

When you are using FragmentStatePagerAdapter, getItem() is called just once, so initially you have the correct image, but then it stays the same.

You should reload your data using a callback interface. So when the one of items in your Fragment A is selected, you reload data in your Fragment B. For this you will need to add

public void reloadData() {
//relaod your data here
}

Then create a callback interface in Fragment A like this

public interface itemSelectedListener {
    void onItemSelected(View view, int position)
}

Implement this interface in activity that holds both fragments

in

onItemSelected() {
    MyFragmentB myFragmentB = ...//get your fragment
    myFragmentB.reloadData();
}
Slum answered 31/8, 2015 at 11:13 Comment(1)
i am calling web service from fragment b and its working. I can get image URL in adapter constructor.Ion
I
0

Finally I've solved the answer. Fragment B was getting saved in navigateFragment, it has been prevented by the following line and it worked.

    if (mCurrentFragment != null) {
        if (mCurrentFragment instanceof GetProducts)
            saveFragmentState(mCurrentFragment.getClass().getName(), mCurrentFragment);
    }
Ion answered 3/9, 2015 at 21:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.