Fragment inside ViewPager not getting refreshed - android

This is the Fragment in which i am capturing Image using camera2basic(google sample).
And this method i am using to update the gallery with the latest captured image:
private void refreshGallery(File file)
{
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(file));
getActivity().sendBroadcast(intent);
}
And this is the Gallery Fragment where i am showing all the images stored in SD Card:
Here MainActivity is having a ViewPager, which is having 5 Fragments:
FragmentHome
FragmentGallery
FragmentCamera
FragmentStar
FragmentProfile
and inside FragmentGallery, there is an another ViewPager(GalleryViewPager) which is having 2 Fragments:
FragmentPhotos
FragmentVideos
Now as you can see, the CameraFragment is in a ViewPager of MainActivity, and the FragmentPhotos(which is showing all the photos of the gallery) is in another ViewPager inside FragmentGallery.
Now when i click the photo and navigate to FragmentPhotos(inside FragmentGallery), the gallery gets updated and recently clicked pic is shown(only for first time) but as i click more pics and return to FragmentPhotos(inside FragmentGallery) the gallery doesn't gets updated !!! Also when I move to a different tab(other than Tab1, Tab2 and Tab3 ie Tab4 or Tab5) ie when the ViewPager gets detroyed, then the gallery gets updated every time without any problem !!!
Please help me, how to update the fragment inside ViewPager.
Although I tried to update it with notifysetchanged but it had no effect.
To initiate the Gallery i am calling the initGallery() from MainActivity on Tab Selection(when the selected tab is the Gallery Fragment), as:
ViewPager viewPager = (ViewPager) findViewById(R.id.pagerGallery);
GalleryViewPagerAdapter adapter = (GalleryViewPagerAdapter) viewPager.getAdapter();
for (int i = 0; i < adapter.getCount(); i++) {
Fragment fragmentObject = (Fragment) viewPager.getAdapter().instantiateItem(viewPager, i);
if (fragmentObject instanceof FragmentPhotos) {
FragmentPhotos fragment = (FragmentPhotos) fragmentObject;
if (fragment != null) {
((FragmentPhotos) fragment).initGallery();
Log.e("", "Gallery called");
}
}
}
and the following is my PagerAdpater:
public class GalleryViewPagerAdapter extends FragmentStatePagerAdapter {
private final List<Fragment> mFragments = new ArrayList<>();
private final List<String> mFragmentTitles = new ArrayList<>();
int numOfTabs;
public GalleryViewPagerAdapter(FragmentManager fm) {
super(fm);
}
public void addFragment(Fragment fragment, String title)
{
mFragments.add(fragment);
mFragmentTitles.add(title);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
#Override
public int getCount() {
return mFragments.size();
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitles.get(position);
}
}
Following is initGallery():
public void initGallery()
{
{
new GetImagesFromCard().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
and GetImagesFromCard is as follows:
private class GetImagesFromCard extends AsyncTask<Void,LoadedImage,Void>
{
Bitmap bitmap;
ArrayList<LoadedImage> bitmapArrays=new ArrayList<LoadedImage>();
#Override
protected Void doInBackground(Void... params) {
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = {MediaStore.Images.Media.DATE_TAKEN,MediaStore.Images.Media._ID,MediaStore.Images.Thumbnails._ID,MediaStore.Images.Thumbnails.DATA,MediaStore.Images.Media.DATE_TAKEN};
String orderBy = MediaStore.Images.Media.DATE_TAKEN + " DESC";
Cursor cursor = getActivity().getContentResolver().query(uri, projection, null, null, orderBy);
int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
int size = cursor.getCount();
if (size==0)
{
}
int imageId =0;
int i=0;
while (i<size && !isCancelled())
{
cursor.moveToPosition(i);
imageId = cursor.getInt(columnIndex);
uri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + imageId);
publishProgress(new LoadedImage(uri.toString()));
if (i==0)
{
new ShowImagesEnlarged(i).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
i++;
}
cursor.close();
return null;
}
#Override
protected void onProgressUpdate(LoadedImage... values) {
super.onProgressUpdate(values);
for (LoadedImage photo: values)
{
imageAdapter.addPhoto(photo);
imageAdapter.notifyDataSetChanged();
bitmapArrays.add(photo);
}
}
#Override
protected void onCancelled() {
Log.e("","Task Cancelled");
super.onCancelled();
}
}

write this line where you extend FragmentPagerAdapter or FragmentStatPagerAdapter
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
it will automatically refresh fragment when you swipe one fragment to another in your viewPager or else you can attach and detach fragment like this
This will refresh current fragment:
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.detach(this).attach(this).commit();
note: You have to put this code in a one-time event like when data is refreshed otherwise your app will be hang too much.. just dont write in onCreat and onCreatView method

Okay what I would suggest is that you create an interface like this:
public interface IContentRefreshableFragment {
void refreshContent()
}
All of your Fragments should implement this interface e.g.:
public class FragmentPhoto implements IContentRefreshableFragment {
#Override
public void refreshContent() {
// You have to refresh your dataset here and push it into your
// adapter, don't forget to call adapter.notifiyDataSetChanged();
initGallery();
}
}
Then you start your Intent for taking a photo with an ResultCode:
public static final String PHOTO_TAKEN = "PHOTO_TAKEN";
startActivityForResult(PhotoActivity.class, PHOTO_TAKEN);
After that in your onActivityResult(...) in your Activity you check for the ResultCode and trigger content refresh:
#Override
public void onActivityResult(..., String requestCode) {
if(requestCode.equals(PHOTO_TAKEN) {
adapter.refreshContent();
}
}
And in your Adapter:
public class GalleryViewPagerAdapter extends FragmentStatePagerAdapter {
private List<IContentRefreshableFragment> mFragments;
public void refreshContent() {
for(IContentRefreshableFragment fragment : mFragments) {
fragment.refreshContent();
}
}
}
So you can handle a bunch of ViewPager Fragments content refreshs without recreating the Fragments every time from any point in your context.
Also you could add Tags to your fragments and refresh just single ones if you modify the methods a little.

Use getChildFragmentManager() in the constructor of your ViewPagerAdapter
ViewPager viewPager = (ViewPager) findViewById(R.id.pagerGallery);
GalleryViewPagerAdapter adapter = new GalleryViewPagerAdapter(getChildFragmentManager());
for (int i = 0; i < adapter.getCount(); i++) {
Fragment fragmentObject = (Fragment) viewPager.getAdapter().instantiateItem(viewPager, i);
if (fragmentObject instanceof FragmentPhotos) {
FragmentPhotos fragment = (FragmentPhotos) fragmentObject;
if (fragment != null) {
((FragmentPhotos) fragment).initGallery();
Log.e("", "Gallery called");
}
}
}
There a issue with some tips in https://code.google.com/p/android/issues/detail?id=55068

Related

Instance of same fragment destroy layout of first one in ViewPager?

I am trying to fetch results from sqllite db in ViewPager using Adapater class
public class AppDetailPagerAdapter extends android.support.v4.app.FragmentStatePagerAdapter {
private List<AppPagingData> mData;
public AppDetailPagerAdapter(FragmentManager fm, List<AppPagingData> data) {
super(fm);
this.mData = data;
}
#Override
public Fragment getItem(int i) {
sCurrentPosition = i;
Fragment fragment = AppDetailFragment.newInstance(mData, i);
return fragment;
}
#Override
public int getCount() {
return mData.size();
}
#Override
public CharSequence getPageTitle(int position) {
return "OBJECT " + (position + 1);
}
}
And my fragment is
public class AppDetailFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private List<AppPagingData> mData;
private int mCurrentPosition;
private int mToken;
private static final String EXTRA_KEY_APP_DATA = "EXTRA_KEY_APP_DATA";
private static final String EXTRA_KEY_APP_CURR_POSITION = "EXTRA_KEY_APP_CURR_POSITION";
public static AppDetailFragment newInstance(ArrayList<AppPagingData> param1, int currentPosition) {
AppDetailFragment fragment = new AppDetailFragment();
Bundle args = new Bundle();
args.putParcelableArrayList(EXTRA_KEY_APP_DATA, param1);
args.putInt(EXTRA_KEY_APP_CURR_POSITION, currentPosition);
fragment.setArguments(args);
return fragment;
}
public AppDetailFragment() {
// Required empty public constructor
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getLoaderManager().restartLoader(mToken, null, this);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if (loader.getId() == mToken) {
ViewGroup oocsGroup = (ViewGroup) getActivity().findViewById(R.id.oocsGroup);
// Remove all existing timings (except 1 ie header)
//I think this line remove childs for all fragment????
for (int i = oocsGroup.getChildCount() - 1; i >= 1; i--) {
oocsGroup.removeViewAt(i);
}
} else {
cursor.close();
}
}...
Now, the problem is my fragment linear layout items get deleted, as android call my fragment second instance. for e.g. if i select 1st item second will called automatically.
How to avoid layout of first instance to destry because of second.
As your adapter creates a new instance of fragment at getItem(), you are probably loosing the previous fragment to the garbage collector.
You could keep your fragments in an array (or ArrayList) and return fragment from the array at getItem().
like:
// keep created instances of AppDetailFragments
private AppDetailFragment[] frags;
public AppDetailPagerAdapter(FragmentManager fm, List<AppPagingData> data) {
super(fm);
this.data = data;
frags = new AppDetailFragment[data.size()];
// init all frags
for (int i=0;i<data.size();i++) {
frags[i] = AppDetailFragment.newInstance(data, i);
}
}
#Override
public Fragment getItem(int i) {
sCurrentPosition = i;
return frags[i]; // may want to check for arrayindexoutofboundsEx..
}
This way you will keep a reference to all created fragments.
note:
You no longer need to keep the data as for getCount you can return length of frags-array.

Fragments not updating in viewpager

I am pretty new to ViewPager and adapter world in android. I have seen this issue asked many times but did not quite understand so hoping for a simplified answer :).
I have a ViewPager with 3 tabs and each tab has a fragment. By default the user first sees tab2 where he gives some imput to generate a string. this string then needs to be taken in tab3 which generates a qr code from it.
the string from tab 2 is updated to a class and tab3 takes the string from there.
My Problem is that when i move from tab2->tab3 the qr code is not generated. But when i go tab2->tab1->tab3 it is generated. I am guessing it retains my old fragment .
Below are my class codes. Could you please let me know why is this viewpager behaviour so.
my FragementActivity class:
public class ConfigExchangeMain extends FragmentActivity {
FragmentPagerAdapter adapterViewPager;
SampleFragementPagerAdapterExchange adapter;
String sent;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.config_exchange_main);
final ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
adapter=new SampleFragementPagerAdapterExchange(getSupportFragmentManager());
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(1);
// Give the TabLayout the ViewPager
TabLayout tabLayout = (TabLayout) findViewById(R.id.sliding_tabs);
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
tabLayout.setupWithViewPager(viewPager);
}
}
My Pager Adapter looks like
public class SampleFragementPagerAdapterExchange extends FragmentStatePagerAdapter {
final int PAGE_COUNT = 3;
private String tabTitles[] = new String[]{"CONTACTS", "HOME", "QR+SCAN"};
private ConfigDetail context;
public SampleFragementPagerAdapterExchange(FragmentManager fm) {
super(fm);
//this.context = context;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 1:
return new ConfigExchangeNFR();
case 0:
return new ConfigExchangeHome();
case 2:
return new ConfigExchangeQR();
default:
return new ConfigExchangeQR();
}
}
#Override
public int getCount() {
return PAGE_COUNT;
}
public CharSequence getPageTitle(int position) {
// Generate title based on item position
return tabTitles[position];
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
and my fragment in tab3 that is not updating:
public class ConfigExchangeQR extends Fragment {
ImageView iQRCode ;
int iQRDimension;
Button bScan;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
FacebookSdk.sdkInitialize(getActivity().getApplicationContext());
View view1 = inflater.inflate(R.layout.config_exchange_qr, container, false);
iQRCode = (ImageView)view1.findViewById(R.id.qrCode);
iQRDimension = 900;
String FinalQRCode = KeyValueDb.getPrefFinalVCard(getActivity());
QRCodeEncoder qrCodeEncoder = new QRCodeEncoder(FinalQRCode, null,
contents.Type.TEXT, BarcodeFormat.QR_CODE.toString(), iQRDimension);
try {
Bitmap bitmap = qrCodeEncoder.encodeAsBitmap();
iQRCode.setImageBitmap(bitmap);
} catch (WriterException e) {
e.printStackTrace();
}
bScan = (Button)view1.findViewById(R.id.scan_qr);
bScan.setOnClickListener(new View.OnClickListener() {
public void onClick(View paramAnonymousView) {
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
startActivityForResult(intent, 0);
}
});
return view1;
}
}
In ConfigExchangeMain class:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
Fragment fragment= adapter.getItem(position);
if(fragment instanceof ConfigExchangeQR ){
((ConfigExchangeQR)fragment).update();
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
#Override
public void onDestroy() {
super.onDestroy();
viewPager.clearOnPageChangeListeners();
}
Add this method to your ConfigExchangeQR fragment
public class ConfigExchangeQR extends Fragment {
...
public void update(){
String FinalQRCode = KeyValueDb.getPrefFinalVCard(getActivity());
QRCodeEncoder qrCodeEncoder = new QRCodeEncoder(FinalQRCode, null,
contents.Type.TEXT, BarcodeFormat.QR_CODE.toString(), iQRDimension);
try {
Bitmap bitmap = qrCodeEncoder.encodeAsBitmap();
iQRCode.setImageBitmap(bitmap);
} catch (WriterException e) {
}
}
}
When you are currently in your second tab. The first and the third tab are preloaded (because the animation is smoother). When you switch to the first tab the third tab is unloaded -> so after switching back to the third tab it is recreated and it works for you.
The 3rd tab onCreateView is called before the 2nd tab is done, thats why it is not working. Try implementing logic inside the onResume() - to make sure it is always called when the focus is back on the fragment.
Hope this helps...
Cheers

ViewPager Cursor position

I am writing a simple news app as a learning exercise. Display a listfragment driven by a SQLite cursor. Click and go to detail screen. Now trying to convert detail screen to ViewPager. I do not want to pass the Cursor from listfragment to ViewPager activity, so I pass the current position in the cursor instead and then create a new Cursor. Can't seem to get the position right though. If I set it in the Activity and not getItem() it goes to the correct position but never changes. If I set it in getItem, it always starts at position 0. The key lines are
// cp.getCursor().moveToPosition(mStartPosition);
// cp.notifyDataSetChanged();
Thanks for any help.
public class ViewPagerActivity extends FragmentActivity {
private static final int NUM_PAGES = 5;
private static String mSectionSelected = "";
private static int mStartPosition;
private ViewPager mPager;
private PagerAdapter mPagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_pager);
// Instantiate a ViewPager and a PagerAdapter.
mSectionSelected = (String)this.getIntent().getSerializableExtra("SECTION");
mPager = (ViewPager) this.findViewById(R.id.pager);
CursorPagerAdapter cp = new CursorPagerAdapter( getSupportFragmentManager(), mSectionSelected);
mPager.setAdapter(cp);
// cp.getCursor().moveToPosition(mStartPosition);
// cp.notifyDataSetChanged();
}
#Override
public void onBackPressed() {
if (mPager.getCurrentItem() == 0) {
// If the user is currently looking at the first step, allow the system to handle the
// Back button. This calls finish() on this activity and pops the back stack.
super.onBackPressed();
} else {
// Otherwise, select the previous step.
mPager.setCurrentItem(mPager.getCurrentItem() - 1);
}
}
}
And here is the adapter - the key line is cursor.moveToPosition(position);
public class CursorPagerAdapter extends FragmentStatePagerAdapter {
private Cursor cursor;
private static String mSectionSelected;
public CursorPagerAdapter(FragmentManager fm, String mSection) {
super(fm);
mSectionSelected = mSection;
this.cursor = NewsManager.get(ApplicationContext.getInstance()).queryNews(mSectionSelected);
}
#Override
public PagerDetailFragment getItem(int position) {
if (cursor == null) // shouldn't happen
return null;
cursor.moveToPosition(position);
PagerDetailFragment frag;
try {
frag = PagerDetailFragment.newInstance();
String mTitle = cursor.getString(NewsDatabaseHelper.COLNUM_TITLE);
String mSection = cursor.getString(NewsDatabaseHelper.COLNUM_CATEGORY);
String mSource = cursor.getString(NewsDatabaseHelper.COLNUM_SOURCE);
String mPublishedDate = cursor.getString(NewsDatabaseHelper.COLNUM_PUBLISHDATE);
String mThumbnail = cursor.getString(NewsDatabaseHelper.COLNUM_THUMBNAIL);
String mBody = cursor.getString(NewsDatabaseHelper.COLNUM_CONTENT);
Bundle args = new Bundle();
args.putString(NewsDatabaseHelper.COLUMN_TITLE, mTitle);
args.putString(NewsDatabaseHelper.COLUMN_CATEGORY, mSection);
args.putString(NewsDatabaseHelper.COLUMN_SOURCE, mSource);
args.putString(NewsDatabaseHelper.COLUMN_PUBLISHDATE, mPublishedDate);
args.putString(NewsDatabaseHelper.COLUMN_THUMBNAIL, mThumbnail);
args.putString(NewsDatabaseHelper.COLUMN_CONTENT, mBody);
frag.setArguments(args);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
return frag;
}
#Override
public int getCount() {
if (cursor == null)
return 0;
else
return cursor.getCount();
}
public void swapCursor(Cursor c) {
if (cursor == c)
return;
this.cursor = c;
notifyDataSetChanged();
}
public Cursor getCursor() {
return cursor;
}
}

Remove Fragment Page from ViewPager in Android

I'm trying to dynamically add and remove Fragments from a ViewPager, adding works without any problems, but removing doesn't work as expected.
Everytime I want to remove the current item, the last one gets removed.
I also tried to use an FragmentStatePagerAdapter or return POSITION_NONE in the adapter's getItemPosition method.
What am I doing wrong?
Here's a basic example:
MainActivity.java
public class MainActivity extends FragmentActivity implements TextProvider {
private Button mAdd;
private Button mRemove;
private ViewPager mPager;
private MyPagerAdapter mAdapter;
private ArrayList<String> mEntries = new ArrayList<String>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mEntries.add("pos 1");
mEntries.add("pos 2");
mEntries.add("pos 3");
mEntries.add("pos 4");
mEntries.add("pos 5");
mAdd = (Button) findViewById(R.id.add);
mRemove = (Button) findViewById(R.id.remove);
mPager = (ViewPager) findViewById(R.id.pager);
mAdd.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
addNewItem();
}
});
mRemove.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
removeCurrentItem();
}
});
mAdapter = new MyPagerAdapter(this.getSupportFragmentManager(), this);
mPager.setAdapter(mAdapter);
}
private void addNewItem() {
mEntries.add("new item");
mAdapter.notifyDataSetChanged();
}
private void removeCurrentItem() {
int position = mPager.getCurrentItem();
mEntries.remove(position);
mAdapter.notifyDataSetChanged();
}
#Override
public String getTextForPosition(int position) {
return mEntries.get(position);
}
#Override
public int getCount() {
return mEntries.size();
}
private class MyPagerAdapter extends FragmentPagerAdapter {
private TextProvider mProvider;
public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
super(fm);
this.mProvider = provider;
}
#Override
public Fragment getItem(int position) {
return MyFragment.newInstance(mProvider.getTextForPosition(position));
}
#Override
public int getCount() {
return mProvider.getCount();
}
}
}
TextProvider.java
public interface TextProvider {
public String getTextForPosition(int position);
public int getCount();
}
MyFragment.java
public class MyFragment extends Fragment {
private String mText;
public static MyFragment newInstance(String text) {
MyFragment f = new MyFragment(text);
return f;
}
public MyFragment() {
}
public MyFragment(String text) {
this.mText = text;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment, container, false);
((TextView) root.findViewById(R.id.position)).setText(mText);
return root;
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="#+id/add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="add new item" />
<Button
android:id="#+id/remove"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="remove current item" />
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1" />
</LinearLayout>
fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/position"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="35sp" />
</LinearLayout>
The ViewPager doesn't remove your fragments with the code above because it loads several views (or fragments in your case) into memory. In addition to the visible view, it also loads the view to either side of the visible one. This provides the smooth scrolling from view to view that makes the ViewPager so cool.
To achieve the effect you want, you need to do a couple of things.
Change the FragmentPagerAdapter to a FragmentStatePagerAdapter. The reason for this is that the FragmentPagerAdapter will keep all the views that it loads into memory forever. Where the FragmentStatePagerAdapter disposes of views that fall outside the current and traversable views.
Override the adapter method getItemPosition (shown below). When we call mAdapter.notifyDataSetChanged(); the ViewPager interrogates the adapter to determine what has changed in terms of positioning. We use this method to say that everything has changed so reprocess all your view positioning.
And here's the code...
private class MyPagerAdapter extends FragmentStatePagerAdapter {
//... your existing code
#Override
public int getItemPosition(Object object){
return PagerAdapter.POSITION_NONE;
}
}
The solution by Louth was not enough to get things working for me, as the existing fragments were not getting destroyed. Motivated by this answer, I found that the solution is to override the getItemId(int position) method of FragmentPagerAdapter to give a new unique ID whenever there has been a change in the expected position of a Fragment.
Source Code:
private class MyPagerAdapter extends FragmentPagerAdapter {
private TextProvider mProvider;
private long baseId = 0;
public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
super(fm);
this.mProvider = provider;
}
#Override
public Fragment getItem(int position) {
return MyFragment.newInstance(mProvider.getTextForPosition(position));
}
#Override
public int getCount() {
return mProvider.getCount();
}
//this is called when notifyDataSetChanged() is called
#Override
public int getItemPosition(Object object) {
// refresh all fragments when data set changed
return PagerAdapter.POSITION_NONE;
}
#Override
public long getItemId(int position) {
// give an ID different from position when position has been changed
return baseId + position;
}
/**
* Notify that the position of a fragment has been changed.
* Create a new ID for each position to force recreation of the fragment
* #param n number of items which have been changed
*/
public void notifyChangeInPosition(int n) {
// shift the ID returned by getItemId outside the range of all previous fragments
baseId += getCount() + n;
}
}
Now, for example if you delete a single tab or make some change to the order, you should call notifyChangeInPosition(1) before calling notifyDataSetChanged(), which will ensure that all the Fragments will be recreated.
Why this solution works
Overriding getItemPosition():
When notifyDataSetChanged() is called, the adapter calls the notifyChanged() method of the ViewPager which it is attached to. The ViewPager then checks the value returned by the adapter's getItemPosition() for each item, removing those items which return POSITION_NONE (see the source code) and then repopulating.
Overriding getItemId():
This is necessary to prevent the adapter from reloading the old fragment when the ViewPager is repopulating. You can easily understand why this works by looking at the source code for instantiateItem() in FragmentPagerAdapter.
final long itemId = getItemId(position);
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
As you can see, the getItem() method is only called if the fragment manager finds no existing fragments with the same Id. To me it seems like a bug that the old fragments are still attached even after notifyDataSetChanged() is called, but the documentation for ViewPager does clearly state that:
Note this class is currently under early design and development. The API will likely change in later updates of the compatibility library, requiring changes to the source code of apps when they are compiled against the newer version.
So hopefully the workaround given here will not be necessary in a future version of the support library.
my working solution to remove fragment page from view pager
public class MyFragmentAdapter extends FragmentStatePagerAdapter {
private ArrayList<ItemFragment> pages;
public MyFragmentAdapter(FragmentManager fragmentManager, ArrayList<ItemFragment> pages) {
super(fragmentManager);
this.pages = pages;
}
#Override
public Fragment getItem(int index) {
return pages.get(index);
}
#Override
public int getCount() {
return pages.size();
}
#Override
public int getItemPosition(Object object) {
int index = pages.indexOf (object);
if (index == -1)
return POSITION_NONE;
else
return index;
}
}
And when i need to remove some page by index i do this
pages.remove(position); // ArrayList<ItemFragment>
adapter.notifyDataSetChanged(); // MyFragmentAdapter
Here it is my adapter initialization
MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);
The fragment must be already removed but the issue was viewpager save state
Try
myViewPager.setSaveFromParentEnabled(false);
Nothing worked but this solved the issue !
Cheers !
I had the idea of simply copy the source code from android.support.v4.app.FragmentPagerAdpater into a custom class named
CustumFragmentPagerAdapter. This gave me the chance to modify the instantiateItem(...) so that every time it is called, it removes / destroys the currently attached fragment before it adds the new fragment received from getItem() method.
Simply modify the instantiateItem(...) in the following way:
#Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
// remove / destroy current fragment
if (fragment != null) {
mCurTransaction.remove(fragment);
}
// get new fragment and add it
fragment = getItem(position);
mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
You can combine both for better :
private class MyPagerAdapter extends FragmentStatePagerAdapter {
//... your existing code
#Override
public int getItemPosition(Object object){
if(Any_Reason_You_WantTo_Update_Positions) //this includes deleting or adding pages
return PagerAdapter.POSITION_NONE;
}
else
return PagerAdapter.POSITION_UNCHANGED; //this ensures high performance in other operations such as editing list items.
}
I had some problems with FragmentStatePagerAdapter.
After removing an item:
there was another item used for a position (an item which did not belong to the position but to a position next to it)
or some fragment was not loaded (there was only blank background visible on that page)
After lots of experiments, I came up with the following solution.
public class SomeAdapter extends FragmentStatePagerAdapter {
private List<Item> items = new ArrayList<Item>();
private boolean removing;
#Override
public Fragment getItem(int position) {
ItemFragment fragment = new ItemFragment();
Bundle args = new Bundle();
// use items.get(position) to configure fragment
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
return items.size();
}
#Override
public int getItemPosition(Object object) {
if (removing) {
return PagerAdapter.POSITION_NONE;
}
Item item = getItemOfFragment(object);
int index = items.indexOf(item);
if (index == -1) {
return POSITION_NONE;
} else {
return index;
}
}
public void addItem(Item item) {
items.add(item);
notifyDataSetChanged();
}
public void removeItem(int position) {
items.remove(position);
removing = true;
notifyDataSetChanged();
removing = false;
}
}
This solution only uses a hack in case of removing an item. Otherwise (e.g. when adding an item) it retains the cleanliness and performance of an original code.
Of course, from the outside of the adapter, you call only addItem/removeItem, no need to call notifyDataSetChanged().
For future readers!
Now you can use ViewPager2 for dynamically adding, removing fragment from the viewpager.
Quoting form API reference
ViewPager2 replaces ViewPager, addressing most of its predecessor’s
pain-points, including right-to-left layout support, vertical
orientation, modifiable Fragment collections, etc.
Take look at MutableCollectionFragmentActivity.kt in googlesample/android-viewpager2 for an example of adding, removing fragments dynamically from the viewpager.
For your information:
Articles:
Exploring the ViewPager2
Look deep into ViewPager2
API reference
Release notes
Samples Repo: https://github.com/googlesamples/android-viewpager2
Louth's answer works fine. But I don't think always return POSITION_NONE is a good idea. Because POSITION_NONE means that fragment should be destroyed and a new fragment will be created.
You can check that in dataSetChanged function in the source code of ViewPager.
if (newPos == PagerAdapter.POSITION_NONE) {
mItems.remove(i);
i--;
... not related code
mAdapter.destroyItem(this, ii.position, ii.object);
So I think you'd better use an arraylist of weakReference to save all the fragments you have created. And when you add or remove some page, you can get the right position from your own arraylist.
public int getItemPosition(Object object) {
for (int i = 0; i < mFragmentsReferences.size(); i ++) {
WeakReference<Fragment> reference = mFragmentsReferences.get(i);
if (reference != null && reference.get() != null) {
Fragment fragment = reference.get();
if (fragment == object) {
return i;
}
}
}
return POSITION_NONE;
}
According to the comments, getItemPosition is Called when the host view is attempting to determine if an item's position has changed. And the return value means its new position.
But this is not enought. We still have an important step to take.
In the source code of FragmentStatePagerAdapter, there is an array named "mFragments" caches the fragments which are not destroyed. And in instantiateItem function.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}
It returned the cached fragment directly when it find that cached fragment is not null. So there is a problem. From example, let's delete one page at position 2, Firstly, We remove that fragment from our own reference arraylist. so in getItemPosition it will return POSITION_NONE for that fragment, and then that fragment will be destroyed and removed from "mFragments".
mFragments.set(position, null);
Now the fragment at position 3 will be at position 2. And instantiatedItem with param position 3 will be called. At this time, the third item in "mFramgents" is not null, so it will return directly. But actually what it returned is the fragment at position 2. So when we turn into page 3, we will find an empty page there.
To work around this problem. My advise is that you can copy the source code of FragmentStatePagerAdapter into your own project, and when you do add or remove operations, you should add and remove elements in the "mFragments" arraylist.
Things will be simpler if you just use PagerAdapter instead of FragmentStatePagerAdapter. Good Luck.
add or remove fragment in viewpager dynamically.
Call setupViewPager(viewPager) on activity start.
To load different fragment call setupViewPagerCustom(viewPager).
e.g. on button click call: setupViewPagerCustom(viewPager);
private void setupViewPager(ViewPager viewPager)
{
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(new fragmnet1(), "HOME");
adapter.addFrag(new fragmnet2(), "SERVICES");
viewPager.setAdapter(adapter);
}
private void setupViewPagerCustom(ViewPager viewPager)
{
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(new fragmnet3(), "Contact us");
adapter.addFrag(new fragmnet4(), "ABOUT US");
viewPager.setAdapter(adapter);
}
//Viewpageradapter, handles the views
static class ViewPagerAdapter extends FragmentStatePagerAdapter
{
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager){
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
#Override
public int getItemPosition(Object object){
return PagerAdapter.POSITION_NONE;
}
public void addFrag(Fragment fragment, String title){
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position){
return mFragmentTitleList.get(position);
}
}
Try this solution. I have used databinding for binding view. You can use common "findViewById()" function.
public class ActCPExpense extends BaseActivity implements View.OnClickListener, {
private static final String TAG = ActCPExpense.class.getSimpleName();
private Context mContext;
private ActCpLossBinding mBinding;
private ViewPagerAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
setContentView(R.layout.act_cp_loss);
mBinding = DataBindingUtil.setContentView(this, R.layout.act_cp_loss);
mContext = ActCPExpense.this;
initViewsAct();
} catch (Exception e) {
LogUtils.LOGE(TAG, e);
}
}
private void initViewsAct() {
adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFragment(FragmentCPPayee.newInstance(), "Title");
mBinding.viewpager.setAdapter(adapter);
mBinding.tab.setViewPager(mBinding.viewpager);
}
#Override
public boolean onOptionsItemSelected(MenuItem itemActUtility) {
int i = itemActUtility.getItemId();
if (i == android.R.id.home) {
onBackPressed();
}
return super.onOptionsItemSelected(itemActUtility);
}
#Override
public void onClick(View view) {
super.onClick(view);
int id = view.getId();
if (id == R.id.btnAdd) {
addFragment();
} else if (id == R.id.btnDelete) {
removeFragment();
}
}
private void addFragment(){
adapter.addFragment(FragmentCPPayee.newInstance("Title");
adapter.notifyDataSetChanged();
mBinding.tab.setViewPager(mBinding.viewpager);
}
private void removeFragment(){
adapter.removeItem(mBinding.viewpager.getCurrentItem());
mBinding.tab.setViewPager(mBinding.viewpager);
}
class ViewPagerAdapter extends FragmentStatePagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public int getItemPosition(#NonNull Object object) {
return PagerAdapter.POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
public void removeItem(int pos) {
destroyItem(null, pos, mFragmentList.get(pos));
mFragmentList.remove(pos);
mFragmentTitleList.remove(pos);
adapter.notifyDataSetChanged();
mBinding.viewpager.setCurrentItem(pos - 1, false);
}
#Override
public CharSequence getPageTitle(int position) {
return "Title " + String.valueOf(position + 1);
}
}
}
I added a function "clearFragments" and I used that function to clear adapter before setting the new fragments. This calls the proper remove actions of Fragments. My pagerAdapter class:
private class ChartPagerAdapter extends FragmentPagerAdapter{
private ArrayList<Fragment> fragmentList;
ChartPagerAdapter(FragmentManager fm){
super(fm);
fragmentList = new ArrayList<>();
}
void setFragments(ArrayList<? extends Fragment> fragments){
fragmentList.addAll(fragments);
}
void clearFragments(){
for(Fragment fragment:fragmentList)
getChildFragmentManager().beginTransaction().remove(fragment).commit();
fragmentList.clear();
}
#Override
public Fragment getItem(int i) {
return fragmentList.get(i);
}
#Override
public int getCount() {
return fragmentList.size();
}
}
i solved this problem by these steps
1- use FragmentPagerAdapter
2- in each fragment create a random id
fragment.id = new Random().nextInt();
3- override getItemPosition in adapter
#Override
public int getItemPosition(#NonNull Object object) {
return PagerAdapter.POSITION_NONE;
}
4-override getItemId in adapter
#Override
public long getItemId(int position) {
return mDatasetFragments.get(position).id;
}
5- now delete code is
adapter.mDatasetFragments.remove(< item to delete position >);
adapter.notifyDataSetChanged();
this worked for me i hope help
My final version code, fixed ALL bugs. It took me 3 days
Updated 2020/07/18:
I had changed a lot of the source code and fix so many bugs, but I don't promise it still work today.
https://github.com/lin1987www/FragmentBuilder/blob/master/commonLibrary/src/main/java/android/support/v4/app/FragmentStatePagerAdapterFix.java
public class FragmentStatePagerAdapterFix extends PagerAdapter {
private static final String TAG = FragmentStatePagerAdapterFix.class.getSimpleName();
private static final boolean DEBUG = false;
private WeakReference<FragmentActivity> wrFragmentActivity;
private WeakReference<Fragment> wrParentFragment;
private final FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;
protected ArrayList<Fragment> mFragments = new ArrayList<>();
protected ArrayList<FragmentState> mFragmentStates = new ArrayList<>();
protected ArrayList<String> mFragmentTags = new ArrayList<>();
protected ArrayList<String> mFragmentClassNames = new ArrayList<>();
protected ArrayList<Bundle> mFragmentArgs = new ArrayList<>();
private Fragment mCurrentPrimaryItem = null;
private boolean[] mTempPositionChange;
#Override
public int getCount() {
return mFragmentClassNames.size();
}
public FragmentActivity getFragmentActivity() {
return wrFragmentActivity.get();
}
public Fragment getParentFragment() {
return wrParentFragment.get();
}
public FragmentStatePagerAdapterFix(FragmentActivity activity) {
mFragmentManager = activity.getSupportFragmentManager();
wrFragmentActivity = new WeakReference<>(activity);
wrParentFragment = new WeakReference<>(null);
}
public FragmentStatePagerAdapterFix(Fragment fragment) {
mFragmentManager = fragment.getChildFragmentManager();
wrFragmentActivity = new WeakReference<>(fragment.getActivity());
wrParentFragment = new WeakReference<>(fragment);
}
public void add(Class<? extends android.support.v4.app.Fragment> fragClass) {
add(fragClass, null, null);
}
public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args) {
add(fragClass, args, null);
}
public void add(Class<? extends android.support.v4.app.Fragment> fragClass, String tag) {
add(fragClass, null, tag);
}
public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag) {
add(fragClass, args, tag, getCount());
}
public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag, int position) {
mFragments.add(position, null);
mFragmentStates.add(position, null);
mFragmentTags.add(position, tag);
mFragmentClassNames.add(position, fragClass.getName());
mFragmentArgs.add(position, args);
mTempPositionChange = new boolean[getCount()];
}
public void remove(int position) {
if (position < getCount()) {
mTempPositionChange = new boolean[getCount()];
for (int i = position; i < mTempPositionChange.length; i++) {
mTempPositionChange[i] = true;
}
mFragments.remove(position);
mFragmentStates.remove(position);
mFragmentTags.remove(position);
mFragmentClassNames.remove(position);
mFragmentArgs.remove(position);
}
}
public void clear(){
mFragments.clear();
mFragmentStates.clear();
mFragmentTags.clear();
mFragmentClassNames.clear();
mFragmentArgs.clear();
}
#Override
public void startUpdate(ViewGroup container) {
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment;
// If we already have this item instantiated, there is nothing
// to do. This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
fragment = mFragments.get(position);
if (fragment != null) {
return fragment;
}
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
FragmentState fs = mFragmentStates.get(position);
if (fs != null) {
fragment = fs.instantiate(getFragmentActivity(), getParentFragment());
// Fix bug
// http://stackoverflow.com/questions/11381470/classnotfoundexception-when-unmarshalling-android-support-v4-view-viewpagersav
if (fragment.mSavedFragmentState != null) {
fragment.mSavedFragmentState.setClassLoader(fragment.getClass().getClassLoader());
}
}
if (fragment == null) {
fragment = Fragment.instantiate(getFragmentActivity(), mFragmentClassNames.get(position), mFragmentArgs.get(position));
}
if (DEBUG) {
Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
mFragments.set(position, fragment);
mFragmentStates.set(position, null);
mCurTransaction.add(container.getId(), fragment, mFragmentTags.get(position));
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) {
Log.v(TAG, "Removing item #" + position + ": f=" + object
+ " v=" + ((Fragment) object).getView());
}
if (position < getCount()) {
FragmentState fragmentState = new FragmentState(fragment);
Fragment.SavedState savedState = mFragmentManager.saveFragmentInstanceState(fragment);
if (savedState != null) {
fragmentState.mSavedFragmentState = savedState.mState;
}
mFragmentStates.set(position, fragmentState);
mFragments.set(position, null);
}
mCurTransaction.remove(fragment);
}
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
mCurrentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}
#Override
public void finishUpdate(ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitAllowingStateLoss();
mCurTransaction = null;
mFragmentManager.executePendingTransactions();
// Fix: Fragment is added by transaction. BUT didn't add to FragmentManager's mActive.
for (Fragment fragment : mFragments) {
if (fragment != null) {
fixActiveFragment(mFragmentManager, fragment);
}
}
}
}
#Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment) object).getView() == view;
}
#Override
public Parcelable saveState() {
Bundle state = null;
// 目前顯示的 Fragments
for (int i = 0; i < mFragments.size(); i++) {
Fragment f = mFragments.get(i);
if (f != null && f.isAdded()) {
if (state == null) {
state = new Bundle();
}
String key = "f" + i;
mFragmentManager.putFragment(state, key, f);
}
}
if (mFragmentStates.size() > 0) {
if (state == null) {
state = new Bundle();
}
FragmentState[] fs = new FragmentState[mFragmentStates.size()];
mFragmentStates.toArray(fs);
state.putParcelableArray("states_fragment", fs);
}
return state;
}
#Override
public void restoreState(Parcelable state, ClassLoader loader) {
if (state != null) {
Bundle bundle = (Bundle) state;
bundle.setClassLoader(loader);
Parcelable[] fs = bundle.getParcelableArray("states_fragment");
mFragments.clear();
mFragmentStates.clear();
mFragmentTags.clear();
mFragmentClassNames.clear();
mFragmentArgs.clear();
if (fs != null) {
for (int i = 0; i < fs.length; i++) {
FragmentState fragmentState = (FragmentState) fs[i];
mFragmentStates.add(fragmentState);
if (fragmentState != null) {
mFragmentArgs.add(fragmentState.mArguments);
mFragmentTags.add(fragmentState.mTag);
mFragmentClassNames.add(fragmentState.mClassName);
} else {
mFragmentArgs.add(null);
mFragmentTags.add(null);
mFragmentClassNames.add(null);
}
mFragments.add(null);
}
}
Iterable<String> keys = bundle.keySet();
for (String key : keys) {
if (key.startsWith("f")) {
int index = Integer.parseInt(key.substring(1));
Fragment f = mFragmentManager.getFragment(bundle, key);
if (f != null) {
f.setMenuVisibility(false);
mFragments.set(index, f);
mFragmentArgs.set(index, f.mArguments);
mFragmentTags.set(index, f.mTag);
mFragmentClassNames.set(index, f.getClass().getName());
} else {
Log.w(TAG, "Bad fragment at key " + key);
}
}
}
// If restore will change
notifyDataSetChanged();
}
}
public static void fixActiveFragment(FragmentManager fragmentManager, Fragment fragment) {
FragmentManagerImpl fm = (FragmentManagerImpl) fragmentManager;
if (fm.mActive != null) {
int index = fragment.mIndex;
Fragment origin = fm.mActive.get(index);
if (origin != null) {
if ((origin.mIndex != fragment.mIndex) || !(origin.equals(fragment))) {
Log.e(TAG,
String.format("fixActiveFragment: Not Equal! Origin: %s %s, Fragment: %s $s",
origin.getClass().getName(), origin.mIndex,
fragment.getClass().getName(), fragment.mIndex
));
}
}
fm.mActive.set(index, fragment);
}
}
// Fix
// http://stackoverflow.com/questions/10396321/remove-fragment-page-from-viewpager-in-android
#Override
public int getItemPosition(Object object) {
int index = mFragments.indexOf(object);
if (index < 0) {
return PagerAdapter.POSITION_NONE;
}
boolean isPositionChange = mTempPositionChange[index];
int result = PagerAdapter.POSITION_UNCHANGED;
if (isPositionChange) {
result = PagerAdapter.POSITION_NONE;
}
return result;
}
}
2020 now.
Simple add this to PageAdapter:
override fun getItemPosition(`object`: Any): Int {
return PagerAdapter.POSITION_NONE
}
You could just override the destroyItem method
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
fragmentManager.beginTransaction().remove((Fragment) object).commitNowAllowingStateLoss();
}
I hope this can help what you want.
private class MyPagerAdapter extends FragmentStatePagerAdapter {
//... your existing code
#Override
public int getItemPosition(Object object){
return PagerAdapter.POSITION_UNCHANGED;
}
}

How to Remove Fragment from FragmentPagerAdapter?

I know there are some topics about this here already but I could not find a solution which I could get to work for my case.
I have a working sliding gallery using a custom FragmentActivity and FragmentPagerAdapter which holds a list of Fragments.
Within the FragmentActivity is a ImageView "delete". If clicked, the function deleteMedia() is called which then should remove the current Fragment and the following Fragment should be displayed.
How would I have to do that in my example?
FragmentActivity:
public class GalleryPagerActivity extends FragmentActivity implements OnClickListener {
private Intent intent;
private SharedPreferences settings;
private PagerAdapter mPagerAdapter;
private ViewPager mPager;
private List<Fragment> fragments;
private List<WhiteboardMedia> wiList;
private int selectedPosition;
private LinearLayout llTop;
private TextView tvTop;
private ImageView delete;
private ImageView share;
private TextView tvCounter;
private TextView tvFilename;
private TextView tvFilesize;
private TextView tvDate;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
super.setContentView(R.layout.gallery_pager);
intent = getIntent();
Type collectionType = new TypeToken<List<WhiteboardMedia>>(){}.getType();
wiList = gson.fromJson(intent.getStringExtra("wiList"), collectionType);
selectedPosition = intent.getIntExtra("position", 1);
llTop = (LinearLayout) findViewById(R.id.llTop);
llTop.setOnClickListener(this);
tvTop = (TextView) findViewById(R.id.tvTop);
tvTop.setOnClickListener(this);
delete = (ImageView) findViewById(R.id.imgDelete);
delete.setOnClickListener(this);
share = (ImageView) findViewById(R.id.imgShare);
share.setOnClickListener(this);
tvCounter = (TextView) findViewById(R.id.tvCounter);
tvFilename = (TextView) findViewById(R.id.tvFilename);
tvFilesize = (TextView) findViewById(R.id.tvFilesize);
tvDate = (TextView) findViewById(R.id.tvDate);
createContextMenu();
initDropbox();
} catch (Exception e) {
Log.e("GalleryPagerActivity", e.getLocalizedMessage());
}
}
/**
* Initialise the pager
*/
private void initialisePager() {
mPager = (ViewPager) super.findViewById(R.id.viewpager);
mPager.setAdapter(this.mPagerAdapter);
mPager.setOnPageChangeListener(new GalleryPageListener(tvCounter, tvFilename, tvFilesize, tvDate, wiList));
mPager.setCurrentItem(selectedPosition, true);
updatePage(selectedPosition);
}
public void updatePage(int position)
{
int focusedPage = position + 1;
Log.i("onPageSelected", "page selected " + position);
WhiteboardMedia wiImage = wiList.get(position);
String imageDate = "N/A";
try {
Date dateTaken= new Date(); //wiImage.getDate();
SimpleDateFormat sdf = new SimpleDateFormat("yy/MM/dd");
imageDate = sdf.format(dateTaken);
} catch (Exception e) {
}
try {
tvCounter.setText(focusedPage + "/" + wiList.size());
tvFilename.setText(wiImage.getFilename());
tvFilesize.setText(wiImage.getSize() + "a");
tvDate.setText(imageDate);
} catch (Exception e) {
}
}
#Override
protected void onResume() {
super.onResume();
}
#Override
public void onDestroy() {
super.onDestroy();
}
private WhiteboardMedia getActiveWhiteboardImage() {
return wiList.get(mPager.getCurrentItem());
}
private final int DELETE = 1;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(1, DELETE, 2, R.string.delete).setIcon(R.drawable.menu_btn_trash);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case DELETE:
deleteMedia();
return true;
}
return super.onContextItemSelected(item);
}
#Override
public void onClick(View v) {
if (v == delete) {
deleteMedia();
}
}
private void deleteMedia() {
// TODO delete the active Fragment and display the next Fragment in the list
}
/******************************************************************************
* Context Menu
*****************************************************************************/
private void createContextMenu() {
// context menu stuff
}
#Override
protected Dialog onCreateDialog(int id) {
// stuff
}
}
FragmentPagerAdapter:
public class GalleryPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> fragments;
public GalleryPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
#Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
#Override
public int getCount() {
return this.fragments.size();
}
}
Thanks for help!
that is the solution I'm using:
mViewPager : is the view you are using to set you Fragment
mViewPager = (YourViewPager) findViewById(R.id.myPager);
TABLE : is just a Integer list of the position of all my Fragments
public void destroyAllItem() {
int mPosition = mViewPager.getCurrentItem();
int mPositionMax = mViewPager.getCurrentItem()+1;
if (TABLE.size() > 0 && mPosition < TABLE.size()) {
if (mPosition > 0) {
mPosition--;
}
for (int i = mPosition; i < mPositionMax; i++) {
try {
Object objectobject = this.instantiateItem(mViewPager, TABLE.get(i).intValue());
if (objectobject != null)
destroyItem(mViewPager, TABLE.get(i).intValue(), objectobject);
} catch (Exception e) {
Log.i(TAG, "no more Fragment in FragmentPagerAdapter");
}
}
}
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
if (position <= getCount()) {
FragmentManager manager = ((Fragment) object).getFragmentManager();
FragmentTransaction trans = manager.beginTransaction();
trans.remove((Fragment) object);
trans.commit();
}
}
First, I suggest that you consider altering your FragmentPagerAdapter, to look more like the sample. You normally do not hold a list of fragments, any more than an ArrayAdapter normally holds a list of Views for the rows. Normally, you create the fragments on demand, and somebody else holds the list.
Then, to delete something, delete it from your model data (what the FragmentPagerAdapter normally wraps). Make sure that getCount() will then return the right number of items. Then, call notifyDataSetChanged() on the FragmentPagerAdapter, which should trigger a redraw of the ViewPager.
I found a solution ovverriding the method "onPostResume()" of the activity and calling the notifyDataSetChanged inside that.
#Override
protected void onPostResume() {
super.onPostResume();
if(this.mCustomPagerAdapter!=null){
this.mCustomPagerAdapter.notifyDataSetChanged();
}
}
If you are using FragmentPagerAdapter for adding and removing fragments at random position(not always at the end) dynamically, there is a method you need to taken more attention which is getItemId. By default, FragmentPagerAdapter uses position combines viewId as the tag name for fragments, however the position changes if you add or remove fragments. As a result, you may get an empty page because the position you are adding is occupied by an existing fragment. To solved this problem, override getItemId.
#Override
public long getItemId(int position) {
long itemId = ...; //Provide your unique ID here according to you logic
return itemId;
}
In my case, when i try to remove one item from the adapter, i will do as follow:
// get the position of item to remove
int position = getBinding().vp.getCurrentItem();
// remove the item from adapter
adapter.removeItem(position);
adapter.notifyDataSetChanged();
// minus one from the count
totalInvoice--;
updateTitle(getBinding().vp.getCurrentItem());
if (totalInvoice == 0) {
finish();
}
// set the adapter to view pager again
getBinding().vp.setAdapter(adapter);
// smooth scroll to given position
getBinding().vp.setCurrentItem(position);
The reason that i did above is that i find that even though you removed one from the data list, but the view of fragment still exist. So you have to let the view pager instantiate the view of given position. The answer above which trying to remove all fragments doesn't work for me. So, I find out the poor way of setting adapter to view pager again.

Categories

Resources