Call FragmentPagerAdapter from activity - android

I want to create FragmentPagerAdapter adapter from activity. How I can do this?
I have used this way when I call from a fragment but now I want from an activity.
MyActivity.java
// here error
adapter = new FragmentPagerAdapter(getFragmentManager(),
images.getImageItem());
viewPage.setAdapter(adapter);
viewPage.setOnPageChangeListener(this);
FragmentPagerAdapter.java
public class FragmentPagerAdapter extends FragmentStatePagerAdapter {
private ArrayList<String> itemData;
public FragmentPagerAdapter(FragmentManager fm,
ArrayList<String> itemData) {
super(fm);
this.itemData = itemData;
}
#Override
public int getCount() {
return itemData.size();
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
}
#Override
public Fragment getItem(int position) {
FragmentImageView f = FragmentImageView.newInstance();
f.setImageList(itemData.get(position));
return f;
}
}

Replace
adapter = new FragmentPagerAdapter(getFragmentManager(),images.getImageItem());
with
adapter = new FragmentPagerAdapter(getSupportFragmentManager(),
images.getImageItem());
getSupportFragmentManager is method of FragmentActivity,not belongs to Activity,so your activity class must implement FragmentActivity.Notice that you should import package com.support.v4.app.FragmentManager,not android.app.FragmentManager.

Instead of getFragmentManager call getSupportFragmentManager
/**
* Return the FragmentManager for interacting with fragments associated
* with this activity.
*/
public FragmentManager getSupportFragmentManager() {
return mFragments.getSupportFragmentManager();
}
So your code will be
adapter = new FragmentPagerAdapter(getSupportFragmentManager(),
images.getImageItem());
viewPage.setAdapter(adapter);
viewPage.setOnPageChangeListener(this);

Related

Android Viewpager.setCurrentitem() and FragmentPagerAdapter instantiateItem() fragments initailize mismatch

I have problem with FragmentPagerAdapter ,I have set fragments in viewpager through FragmentPagerAdapter,
I have to show viewpager dynamically,I have listview data , which I need to show in viewpager,
when I click on the listview , I have to load exact position in the viewpager
so i just set the total size of the item to show in FragmentPagerAdapter, But the problem is that when I select the viewpager position through mPager.setCurrentItem(listposition) it show correct position of the viewpager,
but the data load at wrong place, it load in next fragement in viewpager position,after some tracking in FragmentPagerAdapter, I show that instantiateItem method initailize one fragment in advance, like i set the currentitem (from listview)of viewpager 2
then instantiateItem method initalize 0 to 3 fragment, I think that the reason the data load in 3rd fragment,I tried a lot with FragmentStatePagerAdapter also but no any success , please help me , thanks in advance.
Mainactivity class(Fragmnetactivity) :
public class NewsDetail extends FragmentActivity implements OnClickListener {
public static ViewPager mPager;
private static int NUM_PAGES;
private PagerAdapter mPagerAdapter;
public static ArrayList<Integer> exist_detail = new ArrayList<Integer>();
public static ArrayList<PageFragment> detailfragment = new ArrayList<PageFragment>();
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
NUM_PAGES = topList.size();
PageFragment fragment = new PageFragment();
Bundle args = new Bundle();
args.putInt("page", i);
fragment.setArguments(args);
detailfragment.add(fragment);
mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int arg0) {
if (PageFragment.getInstance() != null) {
Log.d("LOG","PageFragment || "+ PageFragment.getInstance());
PageFragment.getInstance().startLoadingNews(
MusicList.topMusicList.get(arg0)._id);
}
return;
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {}
#Override
public void onPageScrollStateChanged(int arg0) {}
});
mPagerAdapter.notifyDataSetChanged();
mPager.setCurrentItem(listposition);// setcurrent item from here , it is selected from list. "listposition" indicate list position click
private class ScreenSlidePagerAdapter extends FragmentPagerAdapter {
private FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;
private Fragment mCurrentPrimaryItem = null;
private static final boolean DEBUG = false;
public ScreenSlidePagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
this.mFragmentManager = fragmentManager;
}
#Override
public Fragment getItem(int position) {
return detailfragment.get(position);
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return super.getItemId(position);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Log.v("LOG", "On InstantiateItem " + position);// it indicate that this method initialize one fragment in advance.
return super.instantiateItem(container, position);
}
public String makeFragmentName(int viewId, long index) {
return "android:switcher:" + viewId + ":" + index;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG)
Log.e("LOG", "Detaching item #" + position + ": f=" + object
+ " v=" + ((Fragment) object).getView());
mCurTransaction.detach((Fragment) object);
}
#Override
public void restoreState(Parcelable state, ClassLoader loader) {
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
}
PageFragment
public class PageFragment extends Fragment implements OnClickListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPageNumber = getArguments().getInt(ARG_PAGE);
mDetailPageFragment = this;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout containing a title and body text.
rootView = (ViewGroup) inflater.inflate(R.layout.newsdetail, container,
false);
//init();
return rootView;
}
public void startLoadingNews(String _id) {
if (NetworkUtil.cheackNetwork(mActivity)) {
executeTask(_id);
} else {
showRetryCancelDialog();
}
}
}
I can see two issues in the code you've posted:
Only instantiate your fragments in FragmentPagerAdapter.getItem. Don't instantiate them and save them to some list or array, let the FragmentPagerAdapter do that for you. In FragmentPagerAdapter.instantiateItem it checks if the Fragment was already created and if so it returns it, if not, it'll call your getItem to create the fragment.
Don't use the Singleton scheme for your fragment, create a new fragment using a constructor or a .newInstance() static helper.

Set data in Fragments from ViewPager [duplicate]

I'm using the v4 compatibility ViewPager in Android. My FragmentActivity has a bunch of data which is to be displayed in different ways on different pages in my ViewPager. So far I just have 3 instances of the same ListFragment, but in the future I will have 3 instances of different ListFragments. The ViewPager is on a vertical phone screen, the lists are not side-by-side.
Now a button on the ListFragment starts an separate full-page activity (via the FragmentActivity), which returns and FragmentActivity modifies the data, saves it, then attempts to update all views in its ViewPager. It is here, where I am stuck.
public class ProgressMainActivity extends FragmentActivity
{
MyAdapter mAdapter;
ViewPager mPager;
#Override
public void onCreate(Bundle savedInstanceState)
{
...
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager) findViewById(R.id.viewpager);
mPager.setAdapter(mAdapter);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
...
updateFragments();
...
}
public void updateFragments()
{
//Attempt 1:
//mAdapter.notifyDataSetChanged();
//mPager.setAdapter(mAdapter);
//Attempt 2:
//HomeListFragment fragment = (HomeListFragment) getSupportFragmentManager().findFragmentById(mAdapter.fragId[0]);
//fragment.updateDisplay();
}
public static class MyAdapter extends FragmentPagerAdapter implements
TitleProvider
{
int[] fragId = {0,0,0,0,0};
public MyAdapter(FragmentManager fm)
{
super(fm);
}
#Override
public String getTitle(int position){
return titles[position];
}
#Override
public int getCount(){
return titles.length;
}
#Override
public Fragment getItem(int position)
{
Fragment frag = HomeListFragment.newInstance(position);
//Attempt 2:
//fragId[position] = frag.getId();
return frag;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE; //To make notifyDataSetChanged() do something
}
}
public class HomeListFragment extends ListFragment
{
...
public static HomeListFragment newInstance(int num)
{
HomeListFragment f = new HomeListFragment();
...
return f;
}
...
Now as you can see, my first attempt was to notifyDataSetChanged on the entire FragmentPagerAdapter, and this showed to update the data sometimes, but others I got an IllegalStateException: Can not perform this action after onSaveInstanceState.
My second attempt involed trying to call an update function in my ListFragment, but getId in getItem returned 0. As per the docs I tried by
acquiring a reference to the Fragment from FragmentManager, using
findFragmentById() or findFragmentByTag()
but I don't know the tag or id of my Fragments! I have an android:id="#+id/viewpager" for ViewPager, and a android:id="#android:id/list" for my ListView in the ListFragment layout, but I don't think these are useful.
So, how can I either:
a) update the entire ViewPager safely in one go (ideally returning the user to the page he was on before) - it is ok that the user see the view change.
Or preferably,
b) call a function in each affected ListFragment to update the ListView manually.
Any help would be gratefully accepted!
Barkside's answer works with FragmentPagerAdapter but doesn't work with FragmentStatePagerAdapter, because it doesn't set tags on fragments it passes to FragmentManager.
With FragmentStatePagerAdapter it seems we can get by, using its instantiateItem(ViewGroup container, int position) call. It returns reference to fragment at position position. If FragmentStatePagerAdapter already holds reference to fragment in question, instantiateItem just returns reference to that fragment, and doesn't call getItem() to instantiate it again.
So, suppose, I'm currently looking at fragment #50, and want to access fragment #49. Since they are close, there's a good chance the #49 will be already instantiated. So,
ViewPager pager = findViewById(R.id.viewpager);
FragmentStatePagerAdapter a = (FragmentStatePagerAdapter) pager.getAdapter();
MyFragment f49 = (MyFragment) a.instantiateItem(pager, 49)
OK, I think I've found a way to perform request b) in my own question so I'll share for others' benefit. The tag of fragments inside a ViewPager is in the form "android:switcher:VIEWPAGER_ID:INDEX", where VIEWPAGER_ID is the R.id.viewpager in XML layout, and INDEX is the position in the viewpager. So if the position is known (eg 0), I can perform in updateFragments():
HomeListFragment fragment =
(HomeListFragment) getSupportFragmentManager().findFragmentByTag(
"android:switcher:"+R.id.viewpager+":0");
if(fragment != null) // could be null if not instantiated yet
{
if(fragment.getView() != null)
{
// no need to call if fragment's onDestroyView()
//has since been called.
fragment.updateDisplay(); // do what updates are required
}
}
I've no idea if this is a valid way of doing it, but it'll do until something better is suggested.
Try to record the tag each time a Fragement is instantiated.
public class MPagerAdapter extends FragmentPagerAdapter {
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;
public MPagerAdapter(FragmentManager fm) {
super(fm);
mFragmentManager = fm;
mFragmentTags = new HashMap<Integer, String>();
}
#Override
public int getCount() {
return 10;
}
#Override
public Fragment getItem(int position) {
return Fragment.instantiate(mContext, AFragment.class.getName(), null);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Object obj = super.instantiateItem(container, position);
if (obj instanceof Fragment) {
// record the fragment tag here.
Fragment f = (Fragment) obj;
String tag = f.getTag();
mFragmentTags.put(position, tag);
}
return obj;
}
public Fragment getFragment(int position) {
String tag = mFragmentTags.get(position);
if (tag == null)
return null;
return mFragmentManager.findFragmentByTag(tag);
}
}
If you ask me, the second solution on the below page, keeping track of all the "active" fragment pages, is better: http://tamsler.blogspot.nl/2011/11/android-viewpager-and-fragments-part-ii.html
The answer from barkside is too hacky for me.
you keep track of all the "active" fragment pages. In this case, you keep track of the fragment pages in the FragmentStatePagerAdapter, which is used by the ViewPager.
private final SparseArray<Fragment> mPageReferences = new SparseArray<Fragment>();
public Fragment getItem(int index) {
Fragment myFragment = MyFragment.newInstance();
mPageReferences.put(index, myFragment);
return myFragment;
}
To avoid keeping a reference to "inactive" fragment pages, we need to implement the FragmentStatePagerAdapter's destroyItem(...) method:
public void destroyItem(View container, int position, Object object) {
super.destroyItem(container, position, object);
mPageReferences.remove(position);
}
... and when you need to access the currently visible page, you then call:
int index = mViewPager.getCurrentItem();
MyAdapter adapter = ((MyAdapter)mViewPager.getAdapter());
MyFragment fragment = adapter.getFragment(index);
... where the MyAdapter's getFragment(int) method looks like this:
public MyFragment getFragment(int key) {
return mPageReferences.get(key);
}
"
Okay, after testing the method by #barkside above, I could not get it to work with my application. Then I remembered that the IOSched2012 app uses a viewpager as well, and that is where I found my solution. It does not use any fragment ID's or Tags as these are not stored by viewpager in an easily accessible way.
Here's the important parts from the IOSched apps HomeActivity. Pay particular attention to the comment, as therein lies the key.:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Since the pager fragments don't have known tags or IDs, the only way to persist the
// reference is to use putFragment/getFragment. Remember, we're not persisting the exact
// Fragment instance. This mechanism simply gives us a way to persist access to the
// 'current' fragment instance for the given fragment (which changes across orientation
// changes).
//
// The outcome of all this is that the "Refresh" menu button refreshes the stream across
// orientation changes.
if (mSocialStreamFragment != null) {
getSupportFragmentManager().putFragment(outState, "stream_fragment",
mSocialStreamFragment);
}
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (mSocialStreamFragment == null) {
mSocialStreamFragment = (SocialStreamFragment) getSupportFragmentManager()
.getFragment(savedInstanceState, "stream_fragment");
}
}
And store instances of you Fragments in the FragmentPagerAdapter like so:
private class HomePagerAdapter extends FragmentPagerAdapter {
public HomePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return (mMyScheduleFragment = new MyScheduleFragment());
case 1:
return (mExploreFragment = new ExploreFragment());
case 2:
return (mSocialStreamFragment = new SocialStreamFragment());
}
return null;
}
Also, remember to guard your Fragment calls like so:
if (mSocialStreamFragment != null) {
mSocialStreamFragment.refresh();
}
You can copy FragmentPagerAdapter and modify some source code, add getTag() method
for example
public abstract class AppFragmentPagerAdapter extends PagerAdapter {
private static final String TAG = "FragmentPagerAdapter";
private static final boolean DEBUG = false;
private final FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;
private Fragment mCurrentPrimaryItem = null;
public AppFragmentPagerAdapter(FragmentManager fm) {
mFragmentManager = fm;
}
public abstract Fragment getItem(int position);
#Override
public void startUpdate(ViewGroup container) {
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);
String name = getTag(position);
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,
getTag(position));
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
+ " v=" + ((Fragment) object).getView());
mCurTransaction.detach((Fragment) object);
}
#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() {
return null;
}
#Override
public void restoreState(Parcelable state, ClassLoader loader) {
}
public long getItemId(int position) {
return position;
}
private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
protected abstract String getTag(int position);
}
then extend it, override these abstract method,don't need to be afraid of Android Group change
FragmentPageAdapter source code in the future
class TimeLinePagerAdapter extends AppFragmentPagerAdapter {
List<Fragment> list = new ArrayList<Fragment>();
public TimeLinePagerAdapter(FragmentManager fm) {
super(fm);
list.add(new FriendsTimeLineFragment());
list.add(new MentionsTimeLineFragment());
list.add(new CommentsTimeLineFragment());
}
public Fragment getItem(int position) {
return list.get(position);
}
#Override
protected String getTag(int position) {
List<String> tagList = new ArrayList<String>();
tagList.add(FriendsTimeLineFragment.class.getName());
tagList.add(MentionsTimeLineFragment.class.getName());
tagList.add(CommentsTimeLineFragment.class.getName());
return tagList.get(position);
}
#Override
public int getCount() {
return list.size();
}
}
Also works without problems:
somewhere in page fragment's layout:
<FrameLayout android:layout_width="0dp" android:layout_height="0dp" android:visibility="gone" android:id="#+id/fragment_reference">
<View android:layout_width="0dp" android:layout_height="0dp" android:visibility="gone"/>
</FrameLayout>
in fragment's onCreateView():
...
View root = inflater.inflate(R.layout.fragment_page, container, false);
ViewGroup ref = (ViewGroup)root.findViewById(R.id.fragment_reference);
ref.setTag(this);
ref.getChildAt(0).setTag("fragment:" + pageIndex);
return root;
and method to return Fragment from ViewPager, if exists:
public Fragment getFragment(int pageIndex) {
View w = mViewPager.findViewWithTag("fragment:" + pageIndex);
if (w == null) return null;
View r = (View) w.getParent();
return (Fragment) r.getTag();
}
Alternatively you can override setPrimaryItem method from FragmentPagerAdapter like so:
public void setPrimaryItem(ViewGroup container, int position, Object object) {
if (mCurrentFragment != object) {
mCurrentFragment = (Fragment) object; //Keep reference to object
((MyInterface)mCurrentFragment).viewDidAppear();//Or call a method on the fragment
}
super.setPrimaryItem(container, position, object);
}
public Fragment getCurrentFragment(){
return mCurrentFragment;
}
I want to give my approach in case it can help anyone else:
This is my pager adapter:
public class CustomPagerAdapter extends FragmentStatePagerAdapter{
private Fragment[] fragments;
public CustomPagerAdapter(FragmentManager fm) {
super(fm);
fragments = new Fragment[]{
new FragmentA(),
new FragmentB()
};
}
#Override
public Fragment getItem(int arg0) {
return fragments[arg0];
}
#Override
public int getCount() {
return fragments.length;
}
}
In my activity I have:
public class MainActivity {
private ViewPager view_pager;
private CustomPagerAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
adapter = new CustomPagerAdapter(getSupportFragmentManager());
view_pager = (ViewPager) findViewById(R.id.pager);
view_pager.setAdapter(adapter);
view_pager.setOnPageChangeListener(this);
...
}
}
Then to get the current fragment what I do is:
int index = view_pager.getCurrentItem();
Fragment currentFragment = adapter.getItem(index);
This is my solution since I don't need to keep track of my tabs and need to refresh them all anyway.
Bundle b = new Bundle();
b.putInt(Constants.SharedPreferenceKeys.NUM_QUERY_DAYS,numQueryDays);
for(android.support.v4.app.Fragment f:getSupportFragmentManager().getFragments()){
if(f instanceof HomeTermFragment){
((HomeTermFragment) f).restartLoader(b);
}
}

Fragments in viewpager

I have viewpager with some fragments and i set to each fragment my model class according which UI of fragment should be edited. That model class i set to fragment at constructor before i add that fragment to FragmentStatePagerAdapter. My problem is that after rotating screen or when system kills my application and i restore it, my fragment are recreated by system and my model class instance is lost. Even if I set new FragmentStatePagerAdapter, fragments created by system are visible. Is there any right way of achive that i create new instances of my fragments and place them into viewpager? I tried so many thinks like removing fragments from FragmentManager, adding Adapter to viewpager again, change FragmentStatePagerAdapter only to FragmentPagerAdapter and nothing helped.
My activity class with loading model classes in loader from some resource:
List<Fragment> visibleFragment = new ArrayList<Fragment>();
for (Screen screen : wizard.screens) {
if (screen.visible) {
visibleFragment.add(new ScreenUI(screen));
}
}
viewPager.setAdapter(null);
viewPager.setAdapter(new WizardPagerAdapter(getSupportFragmentManager(), visibleFragment));
My FragmentStatePagerAdapter
public class WizardPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragments;
private final int count;
public WizardPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
mFragments = fragments;
count = mFragments.size();
}
#Override
public Fragment getItem(int index) {
return mFragments.get(index);
}
#Override
public int getCount() {
return count;
}
}
try add in Manifest android:configChanges="orientation|keyboardHidden|screenSize" to activity.
OR something like that
public class ViewPagerTaskAdapter extends FragmentStatePagerAdapter {
Map<Integer, FragmentViewTask> fragmentsTask = new HashMap<Integer, FragmentViewTask>();
#Override
public Fragment getItem(int arg0) {
FragmentViewTask myFragment = FragmentViewTask.newInstance(arg0,
tasks.get(arg0));
fragmentsTask.put(arg0, myFragment);
return myFragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
fragmentsTask.remove(position);
}
public FragmentViewTask getFragment(int key) {
return fragmentsTask.get(key);
}
#Override
public int getCount() {
if (tasks != null)
return tasks.size();
return 0;
}
}
Have you tried using onRetainInstance(true) on your Fragments?
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}

Replace one Fragment with another in ViewPager

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

Android FragmentStatePagerAdapter

I'm working with a FragmentStatePagerAdapter using this example.
The MyAdapter class is implemented as follows:
public static class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
return ArrayListFragment.newInstance(position);
}
}
The ListFragment class includes the following method to create a new instance:
/**
* Create a new instance of CountingFragment, providing "num"
* as an argument.
*/
static ArrayListFragment newInstance(int num) {
ArrayListFragment f = new ArrayListFragment();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
When I create a new fragment state pager adapter in my activity, getItem is called, which in turn calls the newInstance method in the ListFragment class. This is great when I want to create a new fragment.
But it's not clear to me how to modify getItem (if even needed) to get the fragment object when it already exists and the user pages from, for example, page 2 to page 1. I'd like my Activity to retrieve that existing, previously created fragment so that it can run an inner class AsyncMethod, which resides in the fragment class.
I think the solution is much simpler than the answers provided.
If you take a look at the source of FragmentStatePagerAdapter.instantiateItem(), you'll notice that instantiateItem() handles this logic for you, and thus your implementation of getItem() should always return a new instance.
So in order to return an existing Fragment, simply do (assuming you're calling from ViewPager):
Fragment f = (Fragment) getAdapter().instantiateItem(this, getCurrentItem());
While #imiric's solution is very concise, it still bothers me that it requires knowledge of the implementation of the class. My solution involves adding the following to your adapter, which should behave nicely with a FragmentStatePagerAdapter possibly destroying fragments:
private SparseArray<WeakReference<Fragment>> mFragments = new SparseArray<>();
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment f = (Fragment) super.instantiateItem(container, position);
mFragments.put(position, new WeakReference<>(f)); // Remember what fragment was in position
return f;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
mFragments.remove(position);
}
public Fragment getFragment(int position) {
WeakReference<Fragment> ref = mFragments.get(position);
Fragment f = ref != null ? ref.get() : null;
if (f == null) {
Log.d(TAG, "fragment for " + position + " is null!");
}
return f;
}
I have implemented something similar to what you have. I extended the FragmentPagerAdapter class like so:
public class ContactsFragmentPagerAdapter extends FragmentPagerAdapter {
ActionBar mActionBar;
private List<Fragment> mFragments;
public ContactsFragmentPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
mFragments = fragments;
}
#Override
public int getCount() {
return mFragments.size();
}
#Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
public void setActionBar(ActionBar bar) {
mActionBar = bar;
}
}
Notice I have added an argument to the constructor to pass in the List of Fragment objects. This way the getItem() method of this class can return any class that extends Fragment or any of its subclasses and not just one specific class ArrayListFragment like you have done.
In the Activity where I instantiate my subclass of FragmentPagerAdapter I have passed in the list of Fragment objects:
Class the instantiates the FragmentPagerAdapter
public final class ContactManager extends Activity {
private ContactsFragmentPagerAdapter mAdapter;
private ViewPager mPager;
public ActionBar mActionBar;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.contact_manager);
List<Fragment> fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this, ContactsListFragment.class.getName()));
fragments.add(Fragment.instantiate(this, GroupsListFragment.class.getName()));
mAdapter = new ContactsFragmentPagerAdapter(this.getFragmentManager(), fragments);
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageScrollStateChanged(int arg0) {}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {}
#Override
public void onPageSelected(int arg0) {
mActionBar.getTabAt(arg0).select();
}
});
}
}
By accessing the variable "fragments", you can access a previously created Fragment so that you can run methods of that Fragment.
public class MyPagerAdapter extends FragmentStatePagerAdapter {
final Context m_context;
final WeakReference<Fragment>[] m_fragments;
public DetailPagerAdapter(FragmentManager fm) {
super(fm);
m_context = ...
m_fragments = new WeakReference[enter size here];
}
#Override
public Fragment getItem(int position) {
final Fragment fragment = instantiate your fragment;
m_fragments[position] = new WeakReference<Fragment>(fragment);
return fragment;
}
#Override
public int getCount() {
return ...
}
public Fragment getFragment(final int position) {
return m_fragments[position] == null ? null :
m_fragments[position].get();
}
}
Do not need modify the FragmentPagerAdapter, because fragments are cached by the FragmentManager. So you must need find inside it. Use this function to find the fragment by pager adapter position.
public Fragment findFragmentByPosition(int position) {
FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
return getSupportFragmentManager().findFragmentByTag(
"android:switcher:" + getViewPager().getId() + ":"
+ fragmentPagerAdapter.getItemId(position));
}
Sample code for v4 support api.
I think this could help:
class HomePagerAdapter(fragmentManager: FragmentManager) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
private val fragmentConstructors: List<() -> Fragment> by lazy {
listOf(
::AuctionLotsFragment,
::ChatListFragment,
::MarketHomeFragment,
::WalletSectionsFragment,
::SettingsFragment
)
}
private val fragments: HashMap<Int, WeakReference<Fragment>> by lazy {
HashMap()
}
override fun getCount(): Int = fragmentConstructors.size
override fun getItem(position: Int): Fragment {
return fragments[position]?.get() ?: this.createFragmentForPosition(position)
}
private fun createFragmentForPosition(position: Int): Fragment {
val fragment = fragmentConstructors[position].invoke()
fragments[position] = WeakReference(fragment)
return fragment
}
}

Categories

Resources