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.

moDev
  • 5,248
  • 4
  • 33
  • 63
  • Looking at this line: `PImageFragment imageFragment = (PImageFragment) PImageFragment.getInstance();` Is PImageFragment a singleton? – Bill Phillips Aug 23 '15 at 15:45
  • @Bill Phillips I've added PImageFragment code – moDev Aug 23 '15 at 17:00
  • Can you post the Fragment A code as well? Especially the snippet where in which you are inflating Frgament B. – Abhishek V Aug 24 '15 at 17:22
  • @AbhishekV inflating Fragment B or Fragment A? – moDev Aug 24 '15 at 17:32
  • @ droid_dev Fragment B. – Abhishek V Aug 24 '15 at 17:33
  • @AbhishekV check updated code – moDev Aug 24 '15 at 17:39
  • @ 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? – Abhishek V Aug 24 '15 at 17:53
  • @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. – moDev Aug 24 '15 at 18:01
  • @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()`? – Abhishek V Aug 24 '15 at 18:14
  • @AbhishekV It's .replace – moDev Aug 24 '15 at 18:24
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/87820/discussion-between-droid-dev-and-abhishek-v). – moDev Aug 24 '15 at 18:30
  • @ 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. – Abhishek V Aug 24 '15 at 18:36
  • Can you post code for `navigateFragment`? – random Aug 31 '15 at 10:59
  • @random Posted navigateFragment code – moDev Sep 03 '15 at 20:22

6 Answers6

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.

Denis Loh
  • 2,194
  • 2
  • 24
  • 38
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.

Derek Fung
  • 8,171
  • 1
  • 25
  • 28
  • Whenever fragment B is loaded, fragment A state is saved and B is destroyed fragment A state is restored. – moDev Sep 01 '15 at 08:27
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) {

        }
    });
dejavu89
  • 754
  • 1
  • 7
  • 17
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();
        }
    }
});
Akshay Sharma
  • 418
  • 3
  • 12
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();
}
Ivan V
  • 3,024
  • 4
  • 26
  • 36
  • i am calling web service from fragment b and its working. I can get image URL in adapter constructor. – moDev Sep 01 '15 at 13:46
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);
    }
moDev
  • 5,248
  • 4
  • 33
  • 63