I would like to add or delete pages from my view pager dynamically. Is that possible?
Yes. You can add or delete views dynamically to the PagerAdapter that is supplying them to the ViewPager and call notifyDataSetChanged() from the PagerAdapter to alert the affected ViewPager about the changes. However, when you do so, you must override the getItemPosition(Object) of the PagerAdapter, that tells them whether the items they are currently showing have changed positions. By default, this function is set to POSITION_UNCHANGED, so the ViewPager will not refresh immediately if you do not override this method. For example,
public class mAdapter extends PagerAdapter {
List<View> mList;
public void addView(View view, int index) {
mList.add(index, view);
notifyDataSetChanged();
}
public void removeView(int index) {
mList.remove(index);
notifyDataSetChanged();
}
#Override
public int getItemPosition(Object object)) {
if (mList.contains(object) {
return mList.indexOf(object);
} else {
return POSITION_NONE;
}
}
}
Although, if you simply want to add or remove the view temporarily from display, but not from the dataset of the PagerAdapter, try using setPrimaryItem(ViewGroup, int, Object) for going to a particular view in the PagerAdapter's data and destroyItem(ViewGroup, int, Object) for removing a view from display.
Yes, since ViewPager gets the child Views from a PagerAdapter, you can add new pages / delete pages on that, and call .notifyDataSetChanged() to reload it.
Yes, the code should be like this:
public int addPage(View view, int position) {
if ((position >= 0) && (position < getSize())) {
myPagerAdapter.mListViews.add(position, view);
myPagerAdapter.notifyDataSetChanged();
return position;
} else {
return -1;
}
}
public View removePage(int position) {
if ((position < 0) || (position >= getSize()) || (getSize()<=1)) {
return null;
} else {
if (position == mPager.getCurrentItem()) {
if(position == (getSize()-1)) {
mPager.setCurrentItem(position-1);
} else if (position == 0){
mPager.setCurrentItem(1);
}
}
View tempView = myPagerAdapter.mListViews.remove(position);
myPagerAdapter.notifyDataSetChanged();
return tempView;
}
}
But there is a bug. If the current Item is 0, and to remove page 0, it will not refresh the screen instantly, I haven't found a solution for this.
Here's an alternative solution to this question. My adapter:
private class PagerAdapter extends FragmentPagerAdapter implements
ViewPager.OnPageChangeListener, TabListener {
private List<Fragment> mFragments = new ArrayList<Fragment>();
private ViewPager mPager;
private ActionBar mActionBar;
private Fragment mPrimaryItem;
public PagerAdapter(FragmentManager fm, ViewPager vp, ActionBar ab) {
super(fm);
mPager = vp;
mPager.setAdapter(this);
mPager.setOnPageChangeListener(this);
mActionBar = ab;
}
public void addTab(PartListFragment frag) {
mFragments.add(frag);
mActionBar.addTab(mActionBar.newTab().setTabListener(this).
setText(frag.getPartCategory()));
}
#Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
#Override
public int getCount() {
return mFragments.size();
}
/** (non-Javadoc)
* #see android.support.v4.app.FragmentStatePagerAdapter#setPrimaryItem(android.view.ViewGroup, int, java.lang.Object)
*/
#Override
public void setPrimaryItem(ViewGroup container, int position,
Object object) {
super.setPrimaryItem(container, position, object);
mPrimaryItem = (Fragment) object;
}
/** (non-Javadoc)
* #see android.support.v4.view.PagerAdapter#getItemPosition(java.lang.Object)
*/
#Override
public int getItemPosition(Object object) {
if (object == mPrimaryItem) {
return POSITION_UNCHANGED;
}
return POSITION_NONE;
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
mPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) { }
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) { }
#Override
public void onPageScrollStateChanged(int arg0) { }
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) { }
#Override
public void onPageSelected(int position) {
mActionBar.setSelectedNavigationItem(position);
}
/**
* This method removes the pages from ViewPager
*/
public void removePages() {
mActionBar.removeAllTabs();
//call to ViewPage to remove the pages
vp.removeAllViews();
mFragments.clear();
//make this to update the pager
vp.setAdapter(null);
vp.setAdapter(pagerAdapter);
}
}
Code to remove and add dynamically
//remove the pages. basically call to method removeAllViews from ViewPager
pagerAdapter.removePages();
pagerAdapter.addPage(pass your fragment);
After the advice of Peri Hartman, it started to work after I set null to ViewPager adapter and put the adapter again after the views removed. Before this the page 0 doesn't showed its list contents.
I am sure you have created an adapter by extending PageAdapter, so there is one method:
#Override
public void destroyItem(View collection, int position, Object view) {
((ViewPager) collection).removeView((View) view);
}
For detailed example for the same, just go through this example.
First:override the pagerAdapter method "getItemPosition"
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
Second:remove the data bind with the adapter.and call adapter.notifydatachanged
#Bind(R.id.pager)
ViewPager pager;
int pos = pager.getCurrentItem();
pager.removeViewAt(pos);
Based on other answers and other resources I've ended with this code.
CustomPagerAdapter:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CustomPagerAdapter extends PagerAdapter {
private List<Fragment> pages = new ArrayList<>();
private Map<Fragment, Integer> fragmentsPosition = new HashMap<>();
private Fragment currentPrimaryItem;
private FragmentManager fragmentManager;
private FragmentTransaction currentTransaction;
public CustomPagerAdapter(FragmentManager fragmentManager) {
this.fragmentManager = fragmentManager;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
if (currentTransaction == null) {
currentTransaction = fragmentManager.beginTransaction();
}
Fragment pageFragment = pages.get(position);
String tag = pageFragment.getArguments().getString(MainActivity.FRAGMENT_TAG_ARG);
Fragment fragment = fragmentManager.findFragmentByTag(tag);
if (fragment != null) {
if (fragment.getId() == container.getId()) {
currentTransaction.attach(fragment);
}
else {
fragmentManager.beginTransaction().remove(fragment).commit();
fragmentManager.executePendingTransactions();
currentTransaction.add(container.getId(), fragment, tag);
}
}
else {
fragment = pageFragment;
currentTransaction.add(container.getId(), fragment, tag);
}
if (fragment != currentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
#Override
public int getCount() {
return pages.size();
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (currentTransaction == null) {
currentTransaction = fragmentManager.beginTransaction();
}
currentTransaction.detach((Fragment) object);
}
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (fragment != currentPrimaryItem) {
if (currentPrimaryItem != null) {
currentPrimaryItem.setMenuVisibility(false);
currentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
currentPrimaryItem = fragment;
}
}
#Override
public void finishUpdate(ViewGroup container) {
if (currentTransaction != null) {
currentTransaction.commitAllowingStateLoss();
currentTransaction = null;
fragmentManager.executePendingTransactions();
}
}
#Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment) object).getView() == view;
}
#Override
public int getItemPosition(Object o) {
Integer result = fragmentsPosition.get(o);
if (result == null) {
return PagerAdapter.POSITION_UNCHANGED;
}
return result;
}
// ---------------------------------- Page actions ----------------------------------
public void addPage(Fragment fragment) {
fragmentsPosition.clear();
pages.add(fragment);
notifyDataSetChanged();
}
public void removePage(int position) {
fragmentsPosition.clear();
Fragment pageFragment = pages.get(position);
String tag = pageFragment.getArguments().getString(MainActivity.FRAGMENT_TAG_ARG);
Fragment fragment = fragmentManager.findFragmentByTag(tag);
if (fragment != null) {
fragmentsPosition.put(fragment, PagerAdapter.POSITION_NONE);
}
for (int i = position + 1; i < pages.size(); i++) {
pageFragment = pages.get(i);
tag = pageFragment.getArguments().getString(MainActivity.FRAGMENT_TAG_ARG);
fragment = fragmentManager.findFragmentByTag(tag);
if (fragment != null) {
fragmentsPosition.put(fragment, i - 1);
}
}
pages.remove(position);
notifyDataSetChanged();
}
}
MainActivity:
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
public static final String FRAGMENT_TAG_ARG = "tag";
private CustomPagerAdapter mCustomPagerAdapter;
private ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCustomPagerAdapter = new CustomPagerAdapter(getSupportFragmentManager());
mCustomPagerAdapter.addPage(MainFragment.newInstance("Main_Title"));
mCustomPagerAdapter.addPage(SecondaryFragment.newInstance("Secondary_Title"));
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mCustomPagerAdapter);
}
}
To remove pages:
mCustomPagerAdapter.removePage(1);
MainFragment:
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class MainFragment extends Fragment {
public static final String FRAGMENT_TAG = "MainFragment";
public static MainFragment newInstance(String text) {
return newInstance(text, FRAGMENT_TAG);
}
public static MainFragment newInstance(String text, String tag) {
MainFragment fragment = new MainFragment();
Bundle args = new Bundle();
args.putString("text", text);
args.putString(MainActivity.FRAGMENT_TAG_ARG, tag + "_" + fragment.hashCode());
fragment.setArguments(args);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
TextView textView = (TextView) rootView.findViewById(R.id.section_label);
textView.setText(getArguments().getString("text"));
return rootView;
}
}
SecondaryFragment has the same code.
The layouts are simple ViewPager/Fragment layouts with android.support.v4.view.ViewPager with id container and Fragment TextView with id section_label.
Resources:
dynamically add and remove view to viewpager
How to Remove Fragment from FragmentPagerAdapter?
https://github.com/takaaki7/ArrayPagerAdapter
https://github.com/commonsguy/cwac-pager
you have to set the adapter to viewpager again, then it will refresh the content.
removeView(int pos) in my PagerAdaper
public void removeView(int index) {
imageFileNames.remove(index);
notifyDataSetChanged();
}
wherever I am removing the file I have to do like this
imagePagerAdapter.removeView(currentPosition);
viewPager.setAdapter(imagePagerAdapter);
EDIT:
This below method is effective, you can apply the below one.
public void updateView(int pos){
viewPager.setAdapter(null);
imagePagerAdapter =new ImagePagerAdapter(YOUR_CONTEXT,YOUR_CONTENT);
viewPager.setAdapter(imagePagerAdapter);
viewPager.setCurrentItem(pos);
}
replace YOUR_CONTEXT with your context and your content with your content name i.e. updated list or something.
Related
I have an activity which consists of three fragments. In one of these fragments I have a viewpager that consists of three other fragments. I call getChildFragmentManager() inside fragment that has viewpager in it. I have searched SO and read almost every answer that are related to this topic for example: this and this and one that seemed most useful was this but could not solve my problem. I think if i can just return POSITION_NONE for getItemPosition(Object object) in FragmentStatePagerAdapter and call notifyDataSetChanged() will solve my problem but every time I do this my app crashes with this error:
java.lang.IllegalStateException: FragmentManager is already executing
transactions!!!
and I mentioned that I am using getChildFragmentManager() inside my fragment.
Anyone thinks of any solution to this problem?
this is the parent fragment:
public class WordDetailFragment extends Fragment {
#BindView(R.id.word_parts_tab_layout)
TabLayout mTabLayout;
#BindView(R.id.word_part_view_pager)
ViewPager mViewPager;
private WordPagerAdapter mPagerAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_word_detail, container, false);
ButterKnife.bind(this, view);
mPagerAdapter = new WordPagerAdapter(getChildFragmentManager(), getActivity());
mViewPager.setAdapter(mPagerAdapter);
mTabLayout.setupWithViewPager(mViewPager);
return view;
}
public void notifyTranslateChanged() {
mViewPager.getAdapter().notifyDataSetChanged();
}
}
this is the code for adapter
public class WordPagerAdapter extends FragmentStatePagerAdapter {
final int PAGE_COUNT = 3;
private String[] tabTitles = new String[]{"Definition", "Example", "Col & fam"};
private Context context;
public WordPagerAdapter(FragmentManager fm, Context context) {
super(fm);
this.context = context;
}
#Override
public int getCount() {
return PAGE_COUNT;
}
#Override
public Fragment getItem(int position) {
Fragment fragment;
switch (position) {
case 0:
fragment = new WordDefinitionFragment();
break;
case 1:
fragment = new WordExampleFragment();
break;
case 2:
fragment = new WordColFamFragment();
break;
default: fragment = new WordDefinitionFragment();
}
return fragment;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public CharSequence getPageTitle(int position) {
// Generate title based on item position
return tabTitles[position];
}
}
One of the fragments in viewpager
public class WordDefinitionFragment extends Fragment {
#BindView(R.id.show_translate_switch)
Switch mShowTranslateSwitch;
private List<WordInfo> mWordInfoList;
private List<Definition> mDefinitionList;
private boolean mShowTranslation;
private View mView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.fragment_word_definition, container, false);
ButterKnife.bind(this, mView);
mShowTranslation = SharedPrefHelper.getBooleanInfo(getActivity(), getString(R.string.show_translate_key));
mShowTranslateSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if (getActivity() != null)
((WordActivity) getActivity()).changeTranslateMode(b);
showTranslation(b);
}
});
return mView;
}
}
and my activity. U have just included the parts that i felt are needed here.
public class WordActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceSate) {
super.onCreate(savedInstanceSate);
setContentView(R.layout.activity_word);
}
public void changeTranslateMode(boolean show) {
SharedPrefHelper.setBooleanInfo(this, getString(R.string.show_translate_ke)y, show);
((WordDetailFragment)mWordDetailFragment).notifyTranslateChanged();
}
}
and my logcat error is
After many searches I could figure out a way which is not very smart but for the time being has solved my problem.
First of all I used the class FixedFragmentStatePagerAdapter from this post. I also had to remove the part in which the fragment bundle is restored because it caches fragment variables and views.
public abstract class FixedFragmentStatePagerAdapter extends PagerAdapter {
private static final String TAG = "PagerAdapter";
private static final boolean DEBUG = false;
private final FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;
private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<>();
private ArrayList<String> mSavedFragmentTags = new ArrayList<>();
private ArrayList<Fragment> mFragments = new ArrayList<>();
private Fragment mCurrentPrimaryItem = null;
public FixedFragmentStatePagerAdapter(FragmentManager fm) {
mFragmentManager = fm;
}
/**
* Return the Fragment associated with a specified position.
*/
public abstract Fragment getItem(int position);
public String getTag(int position) {
return null;
}
#Override
public void startUpdate(ViewGroup container) {
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
// 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.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
Fragment fragment = getItem(position);
String fragmentTag = getTag(position);
if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " t=" + fragmentTag);
/* if (mSavedState.size() > position) {
String savedTag = mSavedFragmentTags.get(position);
if (TextUtils.equals(fragmentTag, savedTag)) {
Fragment.SavedState fss = mSavedState.get(position);
if (fss != null) {
fragment.setInitialSavedState(fss);
}
}
}*/
while (mFragments.size() <= position) {
mFragments.add(null);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
mFragments.set(position, fragment);
mCurTransaction.add(container.getId(), fragment, fragmentTag);
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() + " t=" + fragment.getTag());
while (mSavedState.size() <= position) {
mSavedState.add(null);
mSavedFragmentTags.add(null);
}
mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment));
mSavedFragmentTags.set(position, fragment.getTag());
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();
}
}
#Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment)object).getView() == view;
}
#Override
public Parcelable saveState() {
Bundle state = null;
if (mSavedState.size() > 0) {
state = new Bundle();
Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
mSavedState.toArray(fss);
state.putParcelableArray("states", fss);
state.putStringArrayList("tags", mSavedFragmentTags);
}
for (int i=0; i<mFragments.size(); i++) {
Fragment f = mFragments.get(i);
if (f != null) {
if (state == null) {
state = new Bundle();
}
String key = "f" + i;
mFragmentManager.putFragment(state, key, f);
}
}
return state;
}
#Override
public void restoreState(Parcelable state, ClassLoader loader) {
if (state != null) {
Bundle bundle = (Bundle)state;
bundle.setClassLoader(loader);
Parcelable[] fss = bundle.getParcelableArray("states");
mSavedState.clear();
mFragments.clear();
ArrayList<String> tags = bundle.getStringArrayList("tags");
if (tags != null) {
mSavedFragmentTags = tags;
} else {
mSavedFragmentTags.clear();
}
if (fss != null) {
for (int i=0; i<fss.length; i++) {
mSavedState.add((Fragment.SavedState)fss[i]);
}
}
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) {
while (mFragments.size() <= index) {
mFragments.add(null);
}
f.setMenuVisibility(false);
mFragments.set(index, f);
} else {
Log.w(TAG, "Bad fragment at key " + key);
}
}
}
}
}
}
Then I implemented ViewPager.OnPageChangeListener in the fragment that contains the viewpager. And in the void onPageScrollStateChanged(int state) method I updated the view of my fragments in viewpager.
public class WordDetailFragment extends Fragment implements ViewPager.OnPageChangeListener {
#BindView(R.id.word_parts_tab_layout)
TabLayout mTabLayout;
#BindView(R.id.word_part_view_pager)
ViewPager mViewPager;
private WordPagerAdapter mPagerAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_word_detail, container, false);
ButterKnife.bind(this, view);
mPagerAdapter = new WordPagerAdapter(getChildFragmentManager(), getActivity());
mViewPager.setAdapter(mPagerAdapter);
mTabLayout.setupWithViewPager(mViewPager);
mViewPager.addOnPageChangeListener(this);
return view;
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
Fragment mFragment;
#Override
public void onPageSelected(int position) {
mFragment = mPagerAdapter.getItem(position);
}
#Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
if (mFragment instanceof WordDefinitionFragment) {
((WordDefinitionFragment) mFragment).showTranslation();
//Log.e("StateChanged", mFragment.getClass().getSimpleName());
}
if (mFragment instanceof WordExampleFragment) {
((WordExampleFragment) mFragment).showTranslation();
//Log.e("StateChanged", mFragment.getClass().getSimpleName());
}
if (mFragment instanceof WordColFamFragment) {
((WordColFamFragment) mFragment).showTranslation();
//Log.e("StateChanged", mFragment.getClass().getSimpleName());
}
}
}
}
And in my fragments, I had a callback interface which was implemented in the activity. And in void setUserVisibleHint(boolean isVisibleToUser) method I updated the UI again because neighbor fragments did not get updated.
public class WordExampleFragment extends Fragment {
#BindView(R.id.example_field_name_id)
TextView mExampleFieldNameTV;
#BindView(R.id.word_tv)
TextView mWordTv;
#BindView(R.id.play_sound_iv)
ImageView mPlaySoundIv;
#BindView(R.id.show_translate_switch)
Switch mShowTranslateSwitch;
private TextView mExample1ContentFaTV;
private List<WordInfo> mWordInfoList;
private List<Example> mExampleList;
private int mWordNumber;
//callback interface
private TranslateModeChangeListener mWordPartFragments;
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mWordPartFragments = (TranslateModeChangeListener) context;
} catch (ClassCastException ex) {
throw new ClassCastException("Must implement WordPartFragments interface");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_word_example, container, false);
ButterKnife.bind(this, view);
mWordInfoList = WordActivity.sWordInfoList;
mWordNumber = WordActivity.sWordNumber;
mExampleList = mWordInfoList.get(mWordNumber).getExamples();
showTranslation();
mShowTranslateSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
//this method is called from activity=> callback interface
mWordPartFragments.changeTranslateMode(b);
showTranslation();
}
});
return view;
}
//this method is called when viewpager shows this fragment and it is visible to user
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
showTranslation();
}
}
//this methoud updates my UI: shows or hides the translation
public void showTranslation() {
if (isAdded()) {
boolean showTranslation = WordActivity.sTranslationShown;
//Log.e("Ex", "added & shown = " + showTranslation);
mShowTranslateSwitch.setChecked(showTranslation);
mExample1ContentFaTV.setVisibility((showTranslation) ? View.VISIBLE : View.INVISIBLE);
}
}
}
I am currently struggling with giving tinder card animation effect in my view pager.
I am new to animation and dont know much about it.
I am currently using :AndTinder library.
here is my view pager code.
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
public class ItemPagerFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
/**
* The pager widget, which handles animation and allows swiping horizontally to access previous
* and next wizard steps.
*/
private ViewPager mPager;
/**
* The pager adapter, which provides the pages to the view pager widget.
*/
private CursorPagerAdapter adapter;
private ImageButton favButton;
private ImageButton shareButton;
public ItemPagerFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
System.gc();
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_item_pager, container, false);
// Instantiate a ViewPager and a PagerAdapter in app.
mPager = (ViewPager) rootView.findViewById(R.id.pager);
getLoaderManager().initLoader(-5, null, this);
mPager.setOffscreenPageLimit(0);
adapter = new CursorPagerAdapter(getFragmentManager(), null);
mPager.setAdapter(adapter);
mPager.setOnPageChangeListener(new OnPageChangeListener() {
// This method will be invoked when a new page becomes selected.
#Override
public void onPageSelected(int position) {
//Toast.makeText(getActivity(),
//"Selected page position: " + ++position, Toast.LENGTH_SHORT).show();
}
// This method will be invoked when the current page is scrolled
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// Code goes here
}
// Called when the scroll state changes:
// SCROLL_STATE_IDLE, SCROLL_STATE_DRAGGING, SCROLL_STATE_SETTLING
#Override
public void onPageScrollStateChanged(int state) {
// Code goes here
}
});
return rootView;
}
#Override
public void onDestroyView() {
System.gc();
super.onDestroyView();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
CursorLoader loader = new CursorLoader(getActivity(),
DataProvider.CONTENT_URI_DATA,
null,
null, // "favorite = 0"
null,
"RANDOM()");
return loader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
adapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
/**
* A simple pager adapter.
*/
private class CursorPagerAdapter extends FragmentStatePagerAdapter {
//HashMap<Integer,ItemDetailFragment> hash = new HashMap<Integer,ItemDetailFragment>();
ItemDetailFragment[] record = new ItemDetailFragment[3];
//HashMap<Integer,ItemDetailFragment> hash = new HashMap<Integer,ItemDetailFragment>(3);
private Cursor mCursor;
public CursorPagerAdapter(FragmentManager fm, Cursor c) {
super(fm);
mCursor = c;
}
#Override
public Fragment getItem(int position) {
if (mCursor.moveToPosition(position)) {
Bundle arguments = new Bundle();
arguments.putLong(ItemDetailFragment.ARG_ITEM_ID, mCursor.getLong(mCursor.getColumnIndex(DataProvider.COL_ID)));
ItemDetailFragment fragment = new ItemDetailFragment();
fragment.setArguments(arguments);
// System.out.println("putting page " + position + " in " + position % 3);
record[position%3] = fragment;
//hash.put(position , fragment);
return fragment;
}
return null;
}
#Override
public int getCount() {
if (mCursor != null) {
return mCursor.getCount();
}
return 0;
}
public void swapCursor(Cursor cursor) {
mCursor = cursor;
try {
notifyDataSetChanged();
}
catch(Exception e)
{
System.out.println("exception caught");
}
}
public void destroyItem(ViewGroup container, int position, Object object){
record[position%3] = null;
super.destroyItem(container,position,object);
}
private ItemDetailFragment getfragment(int position)
{
return record[position%3];
//return hash.get(position);
}
public int getItemPosition(Object object) {
ItemDetailFragment f = (ItemDetailFragment ) object;
int position = mPager.getCurrentItem();
ItemDetailFragment current = adapter.getfragment(position);
if(current.equals(f))
{
int fav = Integer.parseInt(f.data.getFavorite());
if(fav == 1)
f.update(1);
else
f.update(0);
}
return super.getItemPosition(object);
}
//changes to be done here
/*public int getItemPosition(Object object) {
ItemDetailFragment f = (ItemDetailFragment ) object;
int position = mPager.getCurrentItem();
System.out.println("prerna update "+position);
ItemDetailFragment current = adapter.getfragment(position);
if(current.equals(f))
{
System.out.println("prerna got it");
int fav = Integer.parseInt(f.data.getFavorite());
if(fav == 1)
f.update(1);
else
f.update(0);
}
return super.getItemPosition(object);
// return POSITION_NONE;//comment afterwards
}*/
}
}
can anybody please help me to do this.
I know there are already some questions to this problem. But I am really new in Android and ecspecially to Fragments and Viewpager. Pls have passion with me. I didn't found a answer which fits to my code.
I dont know how to refresh a fragment or reload it when it's "active" again.
TabsPagerAdapter.java:
public class TabsPagerAdapter extends FragmentPagerAdapter{
public TabsPagerAdapter(FragmentManager fm){
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
return new KFZFragment();
case 1:
return new LogFragment();
case 2:
return new TrackFragment();
}
return null;
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 3;
}
}
I have this 3 Fragments (KFZFragment,LogFragment,TrackFragment) and on the TrackFragment I calculate some data and this data should be display in a ListView in LogFragment.
But when I change to LogFragment it's not the latest data. So it doesnt refresh.
Now how should I modify my code to refresh the fragments when it's "active"?
MainActivityFragment.java:
public class MainActivityFragment extends FragmentActivity implements ActionBar.TabListener{
private ViewPager viewPager;
private TabsPagerAdapter mAdapter;
private ActionBar actionBar;
List<Fragment> fragments;
private String[] tabs = { "KFZ", "Fahrten Log", "Kosten Track" };
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_fragment);
// Initilization
viewPager = (ViewPager) findViewById(R.id.pager);
actionBar = getActionBar();
mAdapter = new TabsPagerAdapter(getSupportFragmentManager());
fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this, KFZFragment.class.getName(),savedInstanceState));
fragments.add(Fragment.instantiate(this, LogFragment.class.getName(),savedInstanceState));
viewPager.setAdapter(mAdapter);
actionBar.setHomeButtonEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Adding Tabs
for (String tab_name : tabs) {
actionBar.addTab(actionBar.newTab().setText(tab_name)
.setTabListener(this));
}
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// on changing the page
// make respected tab selected
actionBar.setSelectedNavigationItem(position);
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
}
return super.onKeyDown(keyCode, event);
}
}
Pls help me out.
In each of your KFZFragment, LogFragment, and TrackFragment, you should be able to override onResume in your fragment class and put your necessary refresh/reload code there.
Why not? use static methods to refresh the data
I agree with Jonathan Lin
But if the fragment you want to update is the fragment that is currently shown you can do something similar to this:
public class DisplayPagerAdapter extends FragmentPagerAdapter {
private static final String TAG = "DisplayPagerAdapter";
SparseArray<DisplayFragment> registeredFragments = new SparseArray<DisplayFragment>();
public DisplayPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
DisplayCoreModule display = DisplayCoreModule.getInstance();
return (display != null && display.getPagesCount() > 0) ? display.getPagesCount() : 1;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
Log.d(TAG, "getItem " + position);
return DisplayFragment.newInstance(position);
}
#Override
public CharSequence getPageTitle(int position) {
DisplayCoreModule display = DisplayCoreModule.getInstance();
if (display != null && display.getPagesCount() > 0) {
return "Side " + (position+1);
} else {
return super.getPageTitle(position);
}
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Log.d(TAG, "instantiateItem " + position);
DisplayFragment fragment = (DisplayFragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.d(TAG, "destroyItem " + position);
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
public SparseArray<DisplayFragment> getRegisteredFragments() {
return registeredFragments;
}
}
It is the registeredFragments part that I wanted to show you.
Since you use FragmentPagerAdapter then getRegisteredFragment(int position) will always return one of your fragments given that you provide a valid position. Should you change to FragmentStatePagerAdapter at some point, it can return null on some of the positions.
Now, in TrackFragment create a callback to your MainActivity, which then informs LogFragment, shown here in pseudo code:
Fragment TrackFragment {
interface TrackFragmentCallback() {
public void newInfo()
}
TrackFragmentCallback trackFragmentCallback;
onAttatch() {
// make sure to implement TrackFragmentCallback on MainActivity
trackFragmentCallback = (TrackFragmentCallback)getActivity();
}
// When this fragment has new info, simply do:
trackFragmentCallback.newInfo();
}
MainActivity implements TrackFragmentCallback {
newInfo() {
// get LogFragment
LogFragment logFragment = pagerAdapter.getRegisteredFragment(1);
if (logFragment != null) {
logFragment.update();
}
}
}
Hope this helps.
I'm having some problems when I try to replace one Fragment with another one in a ViewPager.
Current situation
I have a ViewPager with 3 pages, each one is a Fragment. In first page, I have a ListView inside a ListFragment ("FacturasFragment"). When I click on an item of that list, I use onListItemClick method for handle that event.
What I want
When list item is clicked, I want to replace ListFragment (contains a list of invoices) with another Fragment ("DetallesFacturaFragment", contains details of invoice).
When I'm in "DetallesFacturaFragment" and press Back Button, should return to ListFragment.
Scrolling between pages should not change Fragment displayed in first one. It is, if I'm in first page with "DetallesFacturaFragment" and scroll to second page, when return to first one should continue displaying "DetallesFacturaFragment".
Code
FragmentActivity
public class TabsFacturasActivity extends SherlockFragmentActivity {
private MyAdapter mAdapter;
private ViewPager mPager;
private PageIndicator mIndicator;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_pager);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mIndicator = (TitlePageIndicator)findViewById(R.id.indicator);
mIndicator.setViewPager(mPager);
}
private static class MyAdapter extends FragmentPagerAdapter {
private String[] titles = { "VER FACTURAS", "VER CONSUMO", "INTRODUCIR LECTURA" };
private final FragmentManager mFragmentManager;
private Fragment mFragmentAtPos0;
private Context context;
public MyAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
mFragmentManager = fragmentManager;
}
#Override
public CharSequence getPageTitle(int position) {
return titles[position];
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: // Fragment # 0
return new FacturasFragment();
case 1: // Fragment # 1
return new ConsumoFragment();
case 2:// Fragment # 2
return new LecturaFragment();
}
return null;
}
#Override
public int getCount() {
return titles.length;
}
#Override
public int getItemPosition(Object object)
{
if (object instanceof FacturasFragment && mFragmentAtPos0 instanceof DetallesFacturaFragment)
return POSITION_NONE;
return POSITION_UNCHANGED;
}
}
}
ListFragment
public class FacturasFragment extends ListFragment {
private ListView lista;
private ArrayList<TuplaFacturaWS> facturas;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.activity_facturas, container, false);
facturas = myApplication.getFacturas();
lista = (ListView) view.findViewById(id.list);
MyAdapter myAdaptador = new MyAdapter(this, facturas);
setListAdapter(myAdaptador);
return view;
}
public void onListItemClick (ListView l, View v, int position, long id) {
myApplication.setFacturaActual(position);
mostrarDatosFactura();
}
private void mostrarDatosFactura() {
final DetallesFacturaFragment fragment = new DetallesFacturaFragment();
FragmentTransaction transaction = null;
transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.pager, fragment); //id of ViewPager
transaction.addToBackStack(null);
transaction.commit();
}
private class MyAdapter extends BaseAdapter {
private final FacturasFragment actividad;
private final ArrayList<TuplaFacturaWS> facturas;
public MyAdapter(FacturasFragment facturasActivity, ArrayList<TuplaFacturaWS> facturas) {
super();
this.actividad = facturasActivity;
this.facturas = facturas;
}
#Override
public View getView(int position, View convertView,
ViewGroup parent) {
LayoutInflater inflater = actividad.getLayoutInflater(null);
View view = inflater.inflate(R.layout.list_row, null, true);
//Set data to view
return view;
}
#Override
public int getCount() {
return facturas.size();
}
#Override
public Object getItem(int pos) {
return facturas.get(pos);
}
#Override
public long getItemId(int position) {
return position;
}
private OnClickListener checkListener = new OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
};
}
}
Fragment
public class DetallesFacturaFragment extends SherlockFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.activity_factura, container, false);
//Set data to view
return view;
}
}
At the moment, when I click on list item, white view appears in first page. I've verified and onCreateView method of "DetallesFacturaFragment" is executed, but nothing appears on that page.
And first time I click on list item, it shows that white screen. But after coming back to list, I have to click twice to a list item for showing white screen.
I've been googling and looking at some many questions but couldn't find anyone solved with completed code.
After so many hours spent, I've found correct solution modifying code in that answer.
It replaces Fragment in first page of ViewPager with another one, and if you return back from second Fragment, first Fragment is correctly displayed. Doesn't matter Fragment displayed in first page, if you swipe from one page to another, it doesn't change its Fragment.
Here is my code:
FragmentActivity
public class TabsFacturasActivity extends SherlockFragmentActivity {
public void onBackPressed() {
if(mPager.getCurrentItem() == 0) {
if (mAdapter.getItem(0) instanceof DetallesFacturaFragment) {
((DetallesFacturaFragment) mAdapter.getItem(0)).backPressed();
}
else if (mAdapter.getItem(0) instanceof FacturasFragment) {
finish();
}
}
}
private static class MyAdapter extends FragmentPagerAdapter {
private final class FirstPageListener implements
FirstPageFragmentListener {
public void onSwitchToNextFragment() {
mFragmentManager.beginTransaction().remove(mFragmentAtPos0)
.commit();
if (mFragmentAtPos0 instanceof FacturasFragment){
mFragmentAtPos0 = new DetallesFacturaFragment(listener);
}else{ // Instance of NextFragment
mFragmentAtPos0 = new FacturasFragment(listener);
}
notifyDataSetChanged();
}
}
private String[] titles = { "VER FACTURAS", "VER CONSUMO", "INTRODUCIR LECTURA" };
private final FragmentManager mFragmentManager;
public Fragment mFragmentAtPos0;
private Context context;
FirstPageListener listener = new FirstPageListener();
public MyAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
mFragmentManager = fragmentManager;
}
#Override
public CharSequence getPageTitle(int position) {
return titles[position];
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: // Fragment # 0
if (mFragmentAtPos0 == null)
{
mFragmentAtPos0 = new FacturasFragment(listener);
}
return mFragmentAtPos0;
case 1: // Fragment # 1
return new ConsumoFragment();
case 2:// Fragment # 2
return new LecturaFragment();
}
return null;
}
#Override
public int getCount() {
return titles.length;
}
#Override
public int getItemPosition(Object object)
{
if (object instanceof FacturasFragment &&
mFragmentAtPos0 instanceof DetallesFacturaFragment) {
return POSITION_NONE;
}
if (object instanceof DetallesFacturaFragment &&
mFragmentAtPos0 instanceof FacturasFragment) {
return POSITION_NONE;
}
return POSITION_UNCHANGED;
}
}
}
FirstPageFragmentListener
public interface FirstPageFragmentListener {
void onSwitchToNextFragment();
}
FacturasFragment (FirstFragment)
public class FacturasFragment extends ListFragment implements FirstPageFragmentListener {
static FirstPageFragmentListener firstPageListener;
public FacturasFragment() {
}
public FacturasFragment(FirstPageFragmentListener listener) {
firstPageListener = listener;
}
public void onListItemClick (ListView l, View v, int position, long id) {
firstPageListener.onSwitchToNextFragment();
}
}
DetallesFacturaFragment (SecondFragment)
public class DetallesFacturaFragment extends SherlockFragment {
static FirstPageFragmentListener firstPageListener;
public DetallesFacturaFragment() {
}
public DetallesFacturaFragment(FirstPageFragmentListener listener) {
firstPageListener = listener;
}
public void backPressed() {
firstPageListener.onSwitchToNextFragment();
}
}
To solve minor issue that Android suggest not using non-default constructor for fragment. You should change the constructor to something like this:
in FacturasFragment :
public static FacturasFragment createInstance(FirstPageFragmentListener listener) {
FacturasFragment facturasFragment = new FacturasFragment();
facturasFragment.firstPageListener = listener;
return facturasFragment;
}
Then you can call it from getItem function like this :
mFragmentAtPos0 = FacturasFragment.createInstance(listener);
in DetallesFacturaFragment :
public static DetallesFacturaFragment createInstance(FirstPageFragmentListener listener) {
DetallesFacturaFragment detallesFacturaFragment= new DetallesFacturaFragment();
detallesFacturaFragment.firstPageListener = listener;
return detallesFacturaFragment;
}
Then you can call it from FirstPageListener.onSwitchToNextFragment() function like this :
mFragmentAtPos0 = DetallesFacturaFragment.createInstance(listener);
I found two ways to replace fragment in viewpager.
Way 1: By updating ViewPagerAdapter
Way 2: By using a root fragment
Way1:
In this way, we need to update the fragment from viewpager adapter's fragment list and notify the fragment about the change.
For example:
private void changefragment(int position) {
Fragment newFragment = CategoryPostFragment.newInstance();
adapter.fragments.set(position, newFragment);
adapter.notifyDataSetChanged();
viewPager.setCurrentItem(position);
}
Here is the viewpager adapter code for this
import java.util.List;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.viewpager.widget.PagerAdapter;
import com.trickbd.trickbdapp.ui.activity.homeactivity.fragments.FavFragment;
import com.trickbd.trickbdapp.ui.activity.homeactivity.fragments.HomePostFragment;
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
public List<Fragment> fragments;
public ViewPagerAdapter(FragmentManager manager, List<Fragment> fragments) {
super(manager);
this.fragments = fragments;
}
#NonNull
#Override
public Fragment getItem(int position) {
if (fragments.get(position)!=null){
return fragments.get(position);
}else {
if (position==0){
return new HomePostFragment();
}else {
return new FavFragment();
}
}
}
#Override
public int getCount() {
return fragments.size();
}
//method to add fragment
public void addFragment(Fragment fragment) {
fragments.add(fragment);
}
#Override
public int getItemPosition(#NonNull Object object) {
if (object instanceof FavFragment) {
return POSITION_UNCHANGED; // don't force a reload
} else {
// POSITION_NONE means something like: this fragment is no longer valid
// triggering the ViewPager to re-build the instance of this fragment.
return POSITION_NONE;
}
}
}
I was doing fine with this code. I become concerned when the super(manager) method of FragmentStatePagerAdapter deprecated in api 28.
When I use "super(manager,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);" along with first way of replacing fragment the application crash on runtime.
Also, there is a back draw that when we called adapter.notifydatasetchanged() from the viewpager adapter it's not efficient. It's recreating the whole viewpager while we are trying to change only a specific fragment.
So, way no 2 come with the solution of all problems.
Way 2:
Without adding the actual fragment to the viewpager we should add a root fragment to the viewpager. In root fragment there will be actual fragment added(previously we added in the viewpager. Then we can replace any fragment easily without the help of FragmentStatePagerAdapter.
Code of the root fragment:
Rootfragment.java
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentTransaction;
import com.trickbd.trickbdapp.R;
import dagger.android.support.DaggerFragment;
public class RootFragment extends DaggerFragment {
public RootFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.root_layout, container, false);
if (getFragmentManager() != null) {
FragmentTransaction transaction = getFragmentManager()
.beginTransaction();
transaction.replace(R.id.root_frame, new HomePostFragment());
transaction.commit();
}
return view;
}
}
root_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/root_frame">
</FrameLayout>
So previous replacing code will be updated like below
private void changefragment(int position) {
Fragment newFragment = CategoryPostFragment.newInstance();
replacefragment(newFragment);
viewPager.setCurrentItem(position);
}
public void replacefragment(Fragment fragment){
getSupportFragmentManager();
FragmentTransaction trans = getSupportFragmentManager()
.beginTransaction();
trans.replace(R.id.root_frame, fragment);
trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
trans.addToBackStack(null);
trans.commit();
}
By this way, we can replace the fragment more efficiently.
To get the currently added fragment in rootfragment we may use this code.
if (getSupportFragmentManager().findFragmentById(R.id.root_frame) instanceof HomePostFragment){
HomePostFragment postFragment = (HomePostFragment) getSupportFragmentManager().findFragmentById(R.id.root_frame);
}
You may learn more about this here
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;
}
}