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
Related
actually i have 4 fragments in my app and i switch the fragments by swiping left or right. I used view pager for swiping the fragments are swiping perfectly but there is a problem if i swipe and fragment B shows but the backend functionality of fragment C runs. If i go to C then backend functionality of D runs. At fragment A first backend functionality of fragment A runs then it automatically shifted to fragment B but front end view is of fragment A
This is adapter
public class TabsPagerAdapter extends FragmentPagerAdapter {
private int mNumTabs;
public TabsPagerAdapter(FragmentManager fm, int numTabs) {
super(fm);
this.mNumTabs= numTabs;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
PersonalSettings tab0 = new PersonalSettings();
return tab0;
case 1:
Health tab1 = new Health();
return tab1;
case 2:
Statistics tab2 = new Statistics();
return tab2;
case 3:
Motivation tab3 = new Motivation();
return tab3;
default:
return null;
}
}
#Override
public int getCount() {
return mNumTabs;
}
}
This is main activity
viewPager = (ViewPager) findViewById(R.id.pager);
mAdapter = new TabsPagerAdapter(getSupportFragmentManager(),4);
viewPager.setAdapter(mAdapter);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
If u want further code i will give u
It because the viewpager will preload the item next to the current item to improve UX (in your case, the item is the fragment)
You can set offscreenPageLimit to ViewPager to limit it:
viewPager.setOffscreenPageLimit(1);
But if you do this, you will found the fragment will reload everytime you enter, IMO this is not a good practice.
Override destroyItem method in FragmentpagerAdapter
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
// remove your fragment obj from here
}
I have an activity with 3 fragments (A, B, C). Fragment A consists of a ViewPager with 2 ListFragments. The user can tap on an item in any of the listfragments and by doing so, goes to fragment B.
In fragment A I do:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
pagerAdapter = new PagerAdapter(getActivity().getSupportFragmentManager());
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragmentA, container, false);
vpPager = (ViewPager)view.findViewById(R.id.vpPager);
vpPager.setOffscreenPageLimit(2);
vpPager.setAdapter(pagerAdapter);
vpPager.addOnPageChangeListener(this);
return view;
}
And the PagerAdapter is as follows:
private class PagerAdapter extends FragmentPagerAdapter {
private final ListFragment1 lf1 = ListFragment1 .newInstance();
private final ListFragment2 lf2 = ListFragment2 .newInstance();
public PagerAdapter(android.support.v4.app.FragmentManager fm) {
super(fm);
}
#Override
public android.support.v4.app.Fragment getItem(int position) {
switch (position) {
case 0: return lf1;
case 1: return lf2;
default: return null;
}
}
}
The first time the activity is shown, the viewpager list fragments are displayed correctly.
The 2 viewpager fragments load data from a db, and I do this only once (when the fragments are created).
The user can tap on an item and fragment B is displayed. If the user presses Back, fragment A is shown. However the list fragments are not shown (already an instance of them still exists).
Could it be that the view has been destroyed, even though instances exist?
What is wrong here? Is there a better approach?
EDIT
If I use newInstance in the pager adapter, I get an IllegalStateException: not attached to activity. This is because I start an async task as follows:
#Override
public void onPageSelected(int position) {
Fragment fragment = pagerAdapter.getItem(position);
if (fragment instanceof IPagedFragment) {
((IPagedFragment) fragment).onShown();
}
}
And onShown is:
#Override
public void onShown() {
myTask= new MyTask();
myTask.execute((Void)null);
}
When can I start the task so that I can be 100% sure that the fragment is attached to the activity and that the view has been created (I need to get listview, etc. from the layout).
You have to use ChildFragmentManager like below.
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
pagerAdapter = new PagerAdapter(getChildFragmentManager()); //here used child fragment manager
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragmentA, container, false);
vpPager = (ViewPager)view.findViewById(R.id.vpPager);
vpPager.setOffscreenPageLimit(2);
vpPager.setAdapter(pagerAdapter);
vpPager.addOnPageChangeListener(this);
return view;
}
It works like charm in my code with viewpager and fragment.
Just now I solved it after struggling for whole day, by using getChildFragmentManager()
pass this as a parameter to the pagerAdapter. and it will work.
while using pagerAdapter in fragment use :
PagerAdapter adapter = new PagerAdapter(getChildFragmentManager());
and in case of activity use getFragmentManager()
PagerAdapter adapter = new PagerAdapter(getFragmentManager());
You're creating ListFragment1 and ListFragment2 using the Activity FragmentManager, while you should use the Fragment FragmentManager. So, modify the pagerAdapter = new PagerAdapter(getActivity().getSupportFragmentManager()); with pagerAdapter = new PagerAdapter(getChildFragmentManager());. In this way, the fragments of the view pager will be 'bound' to the fragment hosting the viewpager. Moreover, you should not keep any reference to fragments inside the viewpager: it's something that Android already manage. Try with:
private class PagerAdapter extends FragmentPagerAdapter {
public PagerAdapter(android.support.v4.app.FragmentManager fm) {
super(fm);
}
#Override
public android.support.v4.app.Fragment getItem(int position) {
switch (position) {
case 0: return ListFragment1.newInstance();
case 1: return ListFragment2.newInstance();
default: return null;
}
}
}
By the way, the vpPager.setOffscreenPageLimit(2); is unuseful since you have just 2 pages and this is a method that I've never used even when I have many fragments to manage, since it requires memory.
About your update: remove any logic related to ViewPager handling the fragment. If you need to start an AsyncTask within your Fragment, you can do it using one of the methods of Fragment lifecycle: onResume(), onCreateView() and so on.
class IPagedFragment extends Fragment {
public void onResume() {
super.onResume();
myTask= new MyTask();
myTask.execute((Void)null);
}
}
and please, remove the private final ListFragment1 lf1 = ListFragment1 .newInstance();. Trust me, it's not a good idea since you have a strong reference to your Fragments.
I've built a simple project that you can use as reference implementation. You can download the source code from my dropbox.
use getChildFragmentManager() instead of supportFragmentManager()
If any of the solutions above doesn't work, you can try a workaround by posting (delayed) to the pager view instance an additional notifyDataSetChanged call of the adapter:
vpPager.post(new Runnable() {
#Override
public void run() {
pagerAdapter.notifyDataSetChanged();
}
});
or
vpPager.postDelayed(new Runnable() {
#Override
public void run() {
pagerAdapter.notifyDataSetChanged();
}
}, 100 /* you have to find out the best delay time by trying/adjusting */);
Try overriding the getItemPosition method in your FragmentPagerAdapter:
#Override
public int getItemPosition(Object object) {
return PagerAdapter.POSITION_NONE;
}
If you experience this with Kotlin, it will be like this.
val fragmentAdapter = FragmentPageAdapter(childFragmentManager)
You shouldn't keep references to fragments in your FragmentPagerAdapter. You should always call newInstance in getItem() call, for example:
#Override
public android.support.v4.app.Fragment getItem(int position) {
switch (position) {
case 0: return ListFragment1.newInstance();
case 1: return ListFragment2.newInstance();
default: return null;
}
}
The data you load from the database should be stored in the fragment itself. The adapter will restore the state of fragments (setOffscreenPageLimit(2)).
You are losing your fragments because the items (fragments) are instantiated by the FragmentManager you provide, and it creates fragments based on tags. So it can happen that it creates a new instance of the fragment you already keep, just with different tag.
See FragmentPagerAdapter source code (check instantiateItem() method):
https://android.googlesource.com/platform/frameworks/support/+/refs/heads/master/v13/java/android/support/v13/app/FragmentPagerAdapter.java
Also see this answer:
keep instances of fragments inside FragmentPagerAdapter
On PagerAdapter class override the method setPrimaryItem,
which is called when there's a change in the pager, i would give it a shot.
I would create something like :
private class PagerAdapter extends FragmentPagerAdapter {
private final ListFragment1 lf1 = ListFragment1 .newInstance();
private final ListFragment2 lf2 = ListFragment2 .newInstance();
public PagerAdapter(android.support.v4.app.FragmentManager fm) {
super(fm);
}
#Override
public android.support.v4.app.Fragment getItem(int position) {
switch (position) {
case 0: return lf1;
case 1: return lf2;
default: return null;
}
}
#Override
public int getCount() {
return 2;
}
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
if (position == 0)
lf1.updateUI(); //Refresh what you need on this fragment
else if (position == 1)
lf2.updateUI();
}
}
You're missing getCount() as well.
I'm not sure offscreen has any use, but its probably not an issue. vpPager.setOffscreenPageLimit(2)
One more thing, i would also remove vpPager.addOnPageChangeListener(this), there's no use for this, an it might cause you some issues.
Whatever you need to do, you can pull it off without it, by overriding the pagination, you might "ruin" some of the standard pagination(since the super isn't called)
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....
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.
In My application, I have used the ViewPager.
Like,
(say main.xml)
<android.support.v4.view.ViewPager
android:id="#+id/viewPager"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#+id/register_header" />
And Create ViewPager object and ViewPagerAdapter(which extends FragmentPagerAdapter) object in FragmentActivity.
_mViewPager = (ViewPager) findViewById(R.id.viewPager);
_adapter = new ViewPagerAdapter(getApplicationContext(),
getSupportFragmentManager());
_mViewPager.setAdapter(_adapter);
And the Adapter class is like,
public class ViewPagerAdapter extends FragmentPagerAdapter {
private Context _context;
private FragmentManager manager;
private Fragment f;
private String classname="ViewPagerAdapter";
public ViewPagerAdapter(Context context, FragmentManager fm) {
super(fm);
_context=context;
manager=fm;
}
#Override
public Fragment getItem(int position) {
Log.i(classname,"getItem called"+position);
f = new Fragment();
f=MyFragment.newInstance();
Bundle args = new Bundle();
args.putInt("position", position);
f.setArguments(args);
return f;
}
#Override
public int getCount() {
return User.getPageCount();
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
And in MyFragment Class,
In onResume , I have to store the wigets object to the common static variable.
public void onResume() {
super.onResume();
pageIndex = getArguments().getInt("position");
User.useTextview1=textview1;
User.useTextview2= textview2;
User.current_pageIndex=pageIndex;
}
I have to update the textviews got from the User class in the FragmentActivity.
My Problem is getItem() method is called twice on First time
In Fact , the view displayed in the emulator is 0th index, but got 1st Index value in the FragmentActivity.
If I stopped the calling of getItem() method on second time, I can able to get the 0th index TextView reference in the FragmentActivity.
Please provide me the best way to do this.
Thanks
The FragmentPagerAdapter instantiates 2 Fragments on start, for index 0 and for index 1. If you want to get data from the Fragment which is on the screen, you can use setOnPageChangeListener for the Pager to get current position. Then have SparseArray with WeakReference to your fragments. Update that array in the getItem call. When onPageSelected gets called use that position to get reference to right Fragment and update User data.
Initialization of array:
private SparseArray<WeakReference<MyFragment>> mFragments = new SparseArray<WeakReference<MyFragment>>(3);
in getItem method i wrote this..
#Override
public Fragment getItem(int index) {
EditFragment frag = new EditFragment();
Bundle args = new Bundle();
args.putSerializable("currentItem", itemsList.get(index));
frag.setArguments(args);
return (frag);
}
here itemsList.get(index) is the model class object which i will use in EditFragment class.
here is that.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
View result = inflater.inflate(R.layout.pager_fragment_layout, container, false);
image = (ImageView)result.findViewById(R.id.pager_image);
text = (TextView)result.findViewById(R.id.pager_item_desc);
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getActivity()).build();
ImageLoader.getInstance().init(config);
imLoader = ImageLoader.getInstance();
****NOTE
final SwapItems itemList = (SwapItems) getArguments().getSerializable("currentItem");
imagePath = itemList.getPaths().get(0);
imLoader.displayImage(imagePath,image);
text.setText(itemList.getItemDescription());
image.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Fragment fragment = new ItemImagesFragment();
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Bundle bundle = new Bundle();
bundle.putSerializable("val", itemList);
fragment.setArguments(bundle);
fragmentTransaction.replace(R.id.content_frame, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
});
return result;
}
NOTE: here i am getting swapitems model object from previous getItem method is 'final'. this solves my issue. Earlier i was initialized the same object with static as modifier.
hope yours will also clear
My Problem is getItem() method is called twice on First time
This is not the problem, this is default feature of FragmentPagerAdapter. Its good for you to swipe from this page to the next one and previous one.
the view displayed in the emulator is 0th index, but got 1st Index value in the FragmentActivity
I got the same issue, and I know why. This because you used same Fragment in View Pager.
How to fix this is separate Fragments by using different Fragment.
I faced the same issue - getItem() method was called twice. I don't know about your purposes of using that method by mine was to trigger changes in a hosting activity when new slide appears.
Well, first of all getItem() is not a right method to get event of appearing new slide.
I used ViewPager.OnPageChangeListener listener on ViewPager itself for this purpose.
Here is what I did:
In Activity that holds slide fragments:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.signature_wizard_activity);
// Instantiate a ViewPager and a PagerAdapter.
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
onSlideChanged(position); // change color of the dots
}
#Override
public void onPageSelected(int position) {}
#Override
public void onPageScrollStateChanged(int state) {}
});
}
onPageScrolled() is called every time your slide appears, as a parameter it gets current position of the slide that is shown. Nice thing is that it is called including the first time it appears, not just when slide was changed.
When it can be used?
For example if you have some wizard activity with ViewPager where you slide fragments with some hints, you probable would like to have a bar with grey dots below the slider that would represent the number of slides in total, and one of the dots will be different color representing the current slide.
In this case ViewPager.OnPageChangeListener and method onPageScrolled() will be the best option.
Or if you have buttons PREV. and NEXT which change slides and you want to disable PREV. button on the first slide and disable NEXT button on the last slide. Here is how you do it inside onPageScrolled() method:
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if(position == 0){
prevBtn.setTextColor(ContextCompat.getColor(SignatureWizardActivity.this, R.color.main_grey_500));
prevBtn.setEnabled(false);
}else{
prevBtn.setTextColor(ContextCompat.getColor(SignatureWizardActivity.this, R.color.main_blue));
prevBtn.setEnabled(true);
}
if(position == NUM_PAGES -1){
nextBtn.setTextColor(ContextCompat.getColor(SignatureWizardActivity.this, R.color.main_grey_500));
nextBtn.setEnabled(false);
}else{
nextBtn.setTextColor(ContextCompat.getColor(SignatureWizardActivity.this, R.color.main_blue));
nextBtn.setEnabled(true);
}
}
where NUM_PAGES is a constant with total number of slides
you can use this method to update your views
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// Fetch data or something...
}
}