问题
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.
回答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.
回答2:
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
- create new instance of Fragment B every time and replace the existing one, or
- 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.
回答3:
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) {
}
});
回答4:
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();
}
}
});
回答5:
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();
}
回答6:
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);
}
来源:https://stackoverflow.com/questions/32129247/viewpager-not-getting-updated-using-fragmentstatepageradapter