I would like to instantiate a new fragment on every tab, and change the content of it from an array.
#Override
public Fragment getItem(int position) {
Fragment fragment = null;
fragment = CustomPageViewerFragment.newInstance(position);
return fragment;
}
#Override
public int getCount() {
return rest_id.length;
}
#Override
public float getPageWidth(int position) {
return 0.9f;
}
}
Inside my CustomPageViewerFragment I declare every view and set the data from an array depending on position(i debugged and the position is right) The problem is that it fills the data wrong.
I tried instantiateItem method, but the views has to be added programmatically, and it is not my case.
How do I make the fragment reistantiate every time, so that it does not show the the data from last filled tab on all tabs?
Try creating your Fragments before initializing your Adapter:
Create an Array or ArrayList of Fragments outside your Adapter
Fill that array with your desired Fragments
Pass the array to the Adapter
Get Fragment in getItem() using the position variable
Return that Fragment.
you have to add this,
#Override
public Fragment getItem(int pos) {
// TODO Auto-generated method stub
Fragment f = null;
switch (pos) {
case 0:
f = FirstFragment.newInstance();
break;
case 1:
f = SecondFragment.newInstance();
break;
}
return f;
}
try this one....
Related
How to reuse the fragments inside view pager ???
I have view pager and inside that Having 3 fragments.
I'm creating new instance of fragment to all time. So It's take more space in memory.
In this 3 fragments having perform same functionality. While Im creating new instance every time. It's having more problems like.
1) It take times to keyboard open and close.
2) Scrollview take times to scroll.
3) Edit text take time to focus on and off.
https://prnt.sc/or86ec
Here screen that I have implemented viewpager with 3 fragment. In swipe it's look like same.
I have created Pager adapter:
public class ProposalDetailPagerAdapter : FragmentStatePagerAdapter, CardAdapter
{
private static int NUM_ITEMS = 3;
private float mBaseElevation;
public ProposalDetailPagerAdapter(Android.Support.V4.App.FragmentManager fm, float baseElevation) : base(fm)
{
mBaseElevation = baseElevation;
}
public override int Count => NUM_ITEMS;
public override int GetItemPosition(Java.Lang.Object #object)
{
if (#object is ProposalDetailFragment)
{
ProposalDetailFragment fragment = (ProposalDetailFragment)#object;
if (fragment != null)
{
fragment.UpdateFragmentAfterCreate();
}
}
return base.GetItemPosition(#object);
}
public int getCount()
{
return NUM_ITEMS;
}
public override Android.Support.V4.App.Fragment GetItem(int position)
{
switch (position)
{
case 0:
return OpenFragment(0);
case 1:
return OpenFragment(1);
case 2:
return OpenFragment(2);
default:
return null;
}
}
private Android.Support.V4.App.Fragment OpenFragment(int position)
{
ProposalDetailFragment createdFragment = new ProposalDetailFragment();
Bundle bundle = new Bundle();
bundle.PutInt("position", position);
createdFragment.Arguments = bundle;
return createdFragment;
}
public override Android.Support.V4.App.Fragment GetItem(int position)
{
switch (position)
{
case 0:
return OpenFragment(0);
case 1:
return OpenFragment(1);
case 2:
return OpenFragment(2);
default:
return null;
}
}
}
In this adapter, created 3 fragments. But It's having lot's of memory problem. Because lot's of condition having in fragments. and created 3 instance of memory. So How to resolve this issue ???
Here is a solution :
In ProposalDetailPagerAdapter , can override DestroyItem method.And comment out the method of calling the parent class to avoid repeated creation of the fragment.
As follow:
public class ProposalDetailPagerAdapter : FragmentStatePagerAdapter, CardAdapter
{
...
public override void DestroyItem(ViewGroup container, int position, Java.Lang.Object #object)
{
//base.DestroyItem(container, position, #object);
// Comment out the calling parent class
}
...
}
So in my MainActivity OnCreate I create 3 Fragments, each filled with different data. I then add these Fragments to my ViewPagerAdapter.
ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
MealsFragment exploreFragment = null;
MealsFragment favoriteFragment = null;
MealsFragment localFragment = null;
//if there is no saved instance, create fragments with passed data as args and add them to the viewpageradapter
if(savedInstanceState == null){
exploreFragment = fetchExploreMealsDataAndCreateFragment();
favoriteFragment = fetchFavoriteMealsDataAndCreateFragment();
localFragment = fetchLocalMealsDataAndCreateFragment();
}
//add fragments to the adapter
viewPagerAdapter.addFragment(exploreFragment, "EXPLORE");
viewPagerAdapter.addFragment(favoriteFragment, "FAVORITES");
viewPagerAdapter.addFragment(localFragment, "LOCAL");
//set adapter to the viewpager and link it with the different tabs
mviewPager.setAdapter(viewPagerAdapter);
mtabLayout.setupWithViewPager(mviewPager);
The 3 different fetch methods get the data from an API or DB, so I don't want to call these methods every time I rotate my screen and go through my lifecycle. That's why I firstly check if the savedInstanceState is null. But what happens now is that if my savedInstanceState is not null, the Fragments will be null since i initialised them that way.
Apparently this is not a problem since when I rotate the screen, the fragments remain the same. I was wondering what is going on here behind the scenes as I don't think this is the correct way of handling my situation. Any suggestions of improvement are appreciated aswell.
Thanks in advance!
EDIT:
Forgot to mention that ViewPagerAdapter is my own implementation of FragmentPageAdapter
public class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> fragmentList = new ArrayList<>();
private final List<String> fragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
return fragmentList.get(i);
}
#Override
public int getCount() {
return fragmentList.size();
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return fragmentTitleList.get(position);
}
public void addFragment(Fragment fragment, String title){
fragmentList.add(fragment);
fragmentTitleList.add(title);
}
}
I was wondering what is going on here behind the scenes
Alright, so, this is going to involve looking at the source for FragmentPagerAdapter. What's relevant is the implementation of the instantiateItem() method... though really just a portion of it.
#NonNull
#Override
public Object instantiateItem(#NonNull ViewGroup container, int 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));
}
[...]
}
Essentially, the getItem() method from your ViewPagerAdapter is only called once for each position for the lifetime of the Activity (even across configuration changes). Every time other than the first, the Fragment object is retrieved directly from the FragmentManager as opposed to the Adapter.
So yes, for all re-creations of your Activity, your Adapter is holding a List<Fragment> that is just null, null, null... but it doesn't matter, because this list is not accessed again.
HOWEVER
The above statements assume that every Fragment in your adapter was constructed and added to the FragmentManager before your configuration change, and this is not necessarily guaranteed.
By default, the off-screen page limit for the ViewPager is 1. That means that your third page (your localFragment), is not necessarily added to the ViewPager, and therefore the FragmentManager, on first launch. If you scroll over to the next page even one time, it will be, but this is not necessarily true.
Or perhaps you have manually set the off-screen page limit to be 2, in which case all the pages/Fragments will be added immediately.
Probably the best thing to do is to change how you're using FragmentPagerAdapter altogether. I'd put this inside your Activity as an inner class:
private class ExampleAdapter extends FragmentPagerAdapter {
public ExampleAdapter() {
super(getSupportFragmentManager());
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: return fetchExploreMealsDataAndCreateFragment();
case 1: return fetchFavoriteMealsDataAndCreateFragment();
case 2: return fetchLocalMealsDataAndCreateFragment();
default: throw new IllegalArgumentException("unexpected position: " + position);
}
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0: return "EXPLORE";
case 1: return "FAVORITES";
case 2: return "LOCAL";
default: throw new IllegalArgumentException("unexpected position: " + position);
}
}
#Override
public int getCount() {
return 3;
}
}
And then your onCreate() could be changed to this:
FragmentPagerAdapter viewPagerAdapter = new ExampleAdapter();
mviewPager.setAdapter(viewPagerAdapter);
mtabLayout.setupWithViewPager(mviewPager);
The advantage of doing it this way is that you only call the "fetch and create" methods on demand, but still have the ability to call them after a configuration change in situations where they weren't loaded before the configuration change.
I am newbie to android.I am trying to develop an application which contains a Viewpager with Tabs.My Aim is to pass the data entered in first fragment(i.e tab1) to second fragment.I am confused how to get the data in FragmentA and show it in FragmentB besides my viewpager is in Mainactivity.
some of my code is
mviewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// Toast.makeText(getApplicationContext(),""+servloc,Toast.LENGTH_SHORT).show();
}
#Override
public void onPageSelected(int position) {
mviewpager.setCurrentItem(position);
int index = mviewpager.getCurrentItem();
if(position==0) {
Fragmentpageadapter adapter = (Fragmentpageadapter) mviewpager.getAdapter();
CreateInspectionFragmentA ca= (CreateInspectionFragmentA) adapter.getFragment(index);
}
above code is viewpage setonpagechangelistener..
and my pageadapter is
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
CreateInspectionFragmentA createInspectionFragmenta=new CreateInspectionFragmentA();
mPageReferenceMap.put(position, createInspectionFragmenta);
return createInspectionFragmenta;
case 1:
CreateInspectionFragmentB createInspectionFragmentb = new CreateInspectionFragmentB();
// createInspectionFragmentb.deliverData(data1,data2);
mPageReferenceMap.put(position,createInspectionFragmentb);
return createInspectionFragmentb;
case 2:
CreateInspectionFragmentC createInspectionFragmentc = new CreateInspectionFragmentC();
mPageReferenceMap.put(position,createInspectionFragmentc);
return createInspectionFragmentc;
default:
return null;
}
}
my requirement is to pass the data i entered in FragmentA in edittext to be seen in FragmentB when swiped but not when button click or something.
posibilities:
1.if there is a way to get the UI controls of FragmentA in onpageselected i can bind the EditText view and get the data.
2. using Bundle.
please help me to resolve this problem.
Thanks in Advance.
You can try this. In FragmentA you put this code
private static NameOfTheFragment instance = null;
public static NameOfTheFragment getInstance() {
return instance;
}
Then create a function to return what you want, like a List View
public ListView getList(){
return list;
}
Then in FragmentB you do this in onCreateView
ListView list = NameOfTheFragment.getInstance().getList();
Then you show list in fragment B
This is the PagingAdapter I am using:
public class PagingAdapter extends FragmentStatePagerAdapter {
Context context;
public PagingAdapter(FragmentManager fm, Context con) {
super(fm);
context = con;
}
#Override
public Fragment getItem(int index) {
try {
switch (index) {
case 0:
return Fragment.instantiate(context, ActivityFragment.class.getName());
case 1:
return Fragment.instantiate(context, GroupFragment.class.getName());
case 2:
return Fragment.instantiate(context, MessageFragment.class.getName());
case 3:
return Fragment.instantiate(context, NotificationFragment.class.getName());
}
return null;
}
catch (Exception ex)
{
ex.printStackTrace();
return null;
}
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 4;
}
}
This creates same view for first two tabs. I want separate content for all tabs.
I am placing WebView in all 4 tabs.
In Activity, I am doing this :
mViewPager.setOffscreenPageLimit(3);
Use SparseArray<Fragment> in your adapter, and in your getItem use new instantiate your fragments like
return new ActivityFragment();
If you want to add bundle info than first create your fragment than add bundle in your
fragment.setArgument(bundle);
And use these line in your deateyItem before calling super
if(0<=sparseArray.indexOfKey(position))
sparseArray.remove(position);
First of all your method instantiate is returning a Fragment right?
Does it add the param implicitly in that method using setArguements if not you may want to change to it. On the other hand FragmentManager will call default constructor of each fragment when needed. So you have to keep them (do not overload constructor) and in destroyItem try removing the fragment for real.
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
getFragmentManager().beginTransaction().remove((Fragment)(object)).commit();
}
What do you mean by separate view? are you sure that you are not inflating the wrong layout in.
GroupClassFragment.java class
Do check int the method.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View myCorrectView = inflater.inflate(R.layout.my_correct_view,container,false);
return myCorrectView;
}
I've been trying to update a listview on my second position of a viewpager when checking a "favourite" checkbox on the listview using an interface. It's working as expected but when I go to the 3rd Fragment and then swipe back again the favourites doesn't update even though everything seems like it should work as before doesn't work until i rotate or restart the app.
I've checked the fragmentmanager and it does contain only three Fragments all the time and the methods do get called (register as if the listview should be changed like it does before i swipe to the 3rd tab and back), so I'm really scratching my head here.
FragmentStatePageAdapter:
SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();
/* Some irrelevant code */
#Override
public Fragment getItem(int position) {
switch (position){
case 0:
return FragmentList.newInstance();
case 1:
return FragmentFavourites.newInstance();
case 2:
return FragmentThree.newInstance();
default:
return FragmentList.newInstance();
}
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
#Override
public int getCount() {
return 3;
}
Activity (callback):
#Override
public void favouritesChangedFromMainMenu() {
FragmentFavourites favouritesList = (FragmentFavourites) mSectionsPagerAdapter.getRegisteredFragment(1);
if(null != favouritesList) {
favouritesList.favouritesChangedFromMainMenu();
}
}
When you move to the third fragment, the first one is probably killed (with a call to onSaveInstanceState).
Solution 1, yourViewPager.setOffscreenPageLimit(2);
Solution 2, check if your first fragment is killed... and re-created when you swipe and re-create correct link that you do in onCreate.