getParentFragment returning null - android

I have a Fragment that has a FrameLayout. This first fragment (A) loads inside its Framelayout another fragment (B). When I call getParentFragment from inner fragment (B), I get null. How should this method be used properly?

getParentFragment() was introduced in API level 17 (Android 4.2). Android 4.2 introduced the idea of nested fragments (fragments containing other fragments). Calling this results in null if the fragment has a parent which is an Activity.
Have a look at this.
If you are using support library then you can use getParent(), may be you need to use getChildFragmentManager() while doing fragment transaction.
See this

In my case, although my fragmentA was nested in fragmentB,but I still get null after call getParentFragment in FragmentA. Finally I found that I should use getChildFragmentManager rather than getFragmentManager in FragmentB.
check this What is difference between getSupportFragmentManager() and getChildFragmentManager()?

The one thing that helped is, when creating adapter use getChildFragmentManager().
If you are not using adapter, just use getChildFragmentManager() when doing transactions.
setTargetFragment() is not recommended, since it gives errors on moveState() of fragment(because fragments should be tied to FragmentManager)

I faced the same issue , and fixed the issues by hosting second fragment in your parent fragment with getChildFragmentManager() then you wont be getting the null value ...
Parent fragment
SignUpFragment signUpFragment = new SignUpFragment();
getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.contentPanel, signUpFragment)
.addToBackStack(null).commit();
Child fragment : what i have used is a dialog
HospitalCardDialog hospitalCardDialog = new HospitalCardDialog();
hospitalCardDialog.show(getChildFragmentManager(), "");

Just in case someone using viewpager inside fragment and facing same issue.
Like if you want parent viewmodel using getParentFragment() inside child fragment. You need to use constructor with fragment not with fragmentActivity
Do not use this
public PagerAdapter(#NonNull FragmentActivity fragmentActivity, Bundle arguments) {
super(fragmentActivity);
this.arguments = arguments;
}
But use this
private static class PagerAdapter extends FragmentStateAdapter {
private final Bundle arguments;
public PagerAdapter(#NonNull Fragment fragment, Bundle arguments) {
super(fragment);
this.arguments = arguments;
}
#NonNull
#Override
public Fragment createFragment(int position) {
switch (position) {
case 1:
return MeterPhotoUploadFragment.newInstance(arguments);
default:
return ServiceConnectionDetailsFragment.newInstance(arguments);
}
}
#Override
public int getItemCount() {
return 2;
}
}
Usage:
PagerAdapter pagerAdapter = new PagerAdapter(this, getArguments());

Related

Fragment has not been attached yet?

Below is my code. What i am trying to achieve is that i am displaying viewholder inside my Recycler view. Inside view pager i am displaying one fragment and on swipe left i am displaying another fragment. But when i run the app. App is crashing.Don't know where i am going wrong. I think it's some where in fragment's concept. Please do help me
#Override
public void onBindViewHolder(ManageCustomerViewHolder holder, int position)
{
holder.viewPager.setAdapter(new MyPageAdapter(fragmentManager, fragments));
}
private List<Fragment> getFragments()
{
List<Fragment> fList = new ArrayList<Fragment>();
fList.add(MyFragment.newInstance("Fragment 1"));
fList.add(Myfragment1.newInstance("Fragment 2"));
return fList;
}
public class ManageCustomerViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
{
ViewPager viewPager;
viewPager = (ViewPager) itemView.findViewById(R.id.viewpager);
itemView.setOnClickListener(this);
}
This is what the error is :
java.lang.IllegalStateException: Fragment has not been attached yet.
I was facing the same issue in my app.
Stack Trace:
java.lang.IllegalStateException: Fragment has not been attached yet. android.support.v4.app.Fragment.instantiateChildFragmentManager(Fragment.java:2154)
android.support.v4.app.Fragment.getChildFragmentManager(Fragment.java:704)
The app was crashing at this line:
function performSomeActionInChildFragments() {
List<Fragment> fragments = getChildFragmentManager().getFragments();
...
}
This happens when the fragment is no longer attached to its parent activity/fragment. So a simple isAdded() check should resolve this issue for you.
Fix:
function performSomeActionInChildFragments() {
if (!isAdded()) return;
List<Fragment> fragments = getChildFragmentManager().getFragments();
...
}
From documentation:
isAdded() - Return true if the fragment is currently added to its activity.
Update the Support Library Revision to 27.0.2 to solve the problem. See changelog.
you can do this
FragmentManager childFragmentManager ;
in onCreate :
childFragmentManager = getChildFragmentManager();
then you can use childFragmentManager in
ViewPagerAdapter adapter = new ViewPagerAdapter(childFragmentManager);
adapter.addFragment(tabLlegadaFragment, "Llegada");
adapter.addFragment(tabIngresoFragment, "Ingreso");
viewPager.setAdapter(adapter);
`
and don't forget before do that you have to check your activity was added or not.
you can try this way:
if(getActivity()!=null && isAdded()) //do now
I faced this exception today. After looking for answer I noticed that all are related with ViewPager and getFragmentManger() or in my case getChildFragmentManager().
Well sometimes this things happen when there is no fragment so nothing can be attached or shown. Therefore when my fragment was created I now save in a variable the fragment manager, then I use that manager in my Viewpager instance so there will allways be a manager saved in the class.
To clarify what I'm saying here's my code:
Before
ViewPagerAdapter adapter = new ViewPagerAdapter(getChildFragmentManager());
adapter.addFragment(tabLlegadaFragment, "Llegada");
adapter.addFragment(tabIngresoFragment, "Ingreso");
viewPager.setAdapter(adapter);
Here I faced the error because I recreated the view with some method and I also pressed the back button. So when that happened Android is looking for a getChildFragmentManager() from a Fragment that no longer exists or is gone (because I pressed the back button)
Now
FragmentManager childFragMang;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_tab_main, container, false);
..
childFragMang= getChildFragmentManager();
..
return view;
}
and when I call Viewpager I use that manager
ViewPagerAdapter adapter = new ViewPagerAdapter(childFragMang);
adapter.addFragment(tabLlegadaFragment, "Llegada");
adapter.addFragment(tabIngresoFragment, "Ingreso");
viewPager.setAdapter(adapter);
I did not face that exception again so it was because my fragment had dissapeared when I pressed back and there were no getChildFragmentManager() to call
In all cases,when you using getChildFragmentManager, add isAdded() will beneficial to our application.
You shouldn't be trying to instantiate Fragments. Fragments are created and have lifecycles that are controlled by the Android system. You respond to lifecycle events by overriding callback methods like onAttached (called when the fragment is attached to its Activity)

ViewPager - FragmentStatePagerAdapter and handling rotation

I have a ViewPager which holds Fragments. ViewPager has an adapter of FragmentStatePagerAdapter
Adapter's getItem method.
#Override
public Fragment getItem(int position) {
String text= dbHelper.getText(position);
CustomFragment frg = new CustomFragment(text);
return frg;
}
I am initializing the fragment in the getItem method of the adapter.
Everything works perfectly.
When the orientation changes however, instead of restoring my initialized fragments, CustomFragments are created using the default constructor of CustomFragment. So this creates fragments with dummy data.
What is the reason of this?
How can i restore the previously created fragments?
The ideal way to initialise your Fragments is to create a factory method like:
public static CustomFragment newInstance(String text) {
Bundle arguments = new Bundle();
arguments.put("someText", text);
CustomFragment fragment = new CustomFragment();
fragment.setArguments(arguments);
return fragment;
}
and get the arguments with getArguments() in the onCreate() method and process it to initialise whatever you want in the fragment.
This way when your fragments are recreated on configuration change, the arguments are persisted and the Fragments take care of themselves when their onCreate() method is called.
You might have noticed the lint warnings about the same if you are using the latest tools.

Android ViewPager and Fragment serialization

I've an activity with two attributes:
private Fragment firstFragment;
private Fragment secondFragment;
In onCreate method:
adapter = new MyPagerAdapter(getSupportFragmentManager());
pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(adapter);
pager.setOffscreenPageLimit(6);
pager.setSaveEnabled(true);
where MyPagerAdapter extends FragmentStatePagerAdapter class.
Into getItem() method:
#Override
public Fragment getItem(int position) {
Bundle args = new Bundle();
switch (position) {
case FIRST:
secondFragment = new FirstFragment();
secondFragment.setArguments(args);
return secondFragment;
case SECOND:
secondFragment = new SecondFragment();
secondFragment.setArguments(args);
return secondFragment;
}
}
and all works correctly.
But, when I change the screen orientation, the private attributes is set to null and I lost the reference of two fragments.
So i've tried to serialized this fragment with:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getSupportFragmentManager().putFragment(outState, FirstFragment.class.getName(), firstFragment);
getSupportFragmentManager().putFragment(outState, SecondFragment.class.getName(), secondFragment);
}
and load them into onCreate method with:
if (savedInstanceState != null) {
firstFragment = (FirstFragment) getSupportFragmentManager().getFragment(savedInstanceState, FirstFragment.class.getName());
secondFragment = (SecondFragment) getSupportFragmentManager().getFragment(savedInstanceState, SecondFragment.class.getName());
}
My questions:
1. Is it the correct way to serialized fragment into activity screen orientation changes?
2. Sometimes I've the error: "Unmarshalling unknown type code 55 at offset 448", is it possible that it has caused by fragment serialization?"
EDIT:
I need to have the fragments as activity attributes because I've a listener interface into activity that:
#Override
public void executeTask(String what) {
secondFragment.executeTask(what);
}
this method was invoked into firstFragment. So the FirstFragment can execute a method of SecondFragment.
I'm not sure what may be the cause of the problem but I'll give you a hint that may help.
I don't think that you should reference the Fragments in the Adapter from the Activity.
Mainly due to the fact that it's pretty hard to synchronize the Activity life-cycle, Fragment lifecycle and ViewPager children life-cycles. And if any bugs emerge the debugging can be really painful.
Believe me, been there, done that...
By the way - is there a reason why you need references to Fragments in your Activity ?
EDIT
I don't think you should pass the information between the Fragments this way. In general the FragmentManager handles (creates, deletes) Fragments on it's own and you cannot be sure that these Fragments will be available at any time.
I think that the best way would be to move your data to separate model (database entry, SharedPreference or a singelton class) and then letting know the Adapter that data has changed (by a DataObserver in Fragments or simply notify the Adapter to update children data by calling notifyDataChanged).
EXAMPLE
FragmentA --->listener (reloadData())--->Activity--->adapter.notifyDataChanged()-----> fragmentB gets updated
This way if you ever want to add a ThirdFragment or in fact any number of Fragments that will use the Data you will not have to worry about updating data in any of these - just let the Adapter worry about it.
If your using same layout for portrait and landscape then When orientation change you can avoid activity recreate. change your manifest as...
<activity android:name=".activity.MainActivity"
android:configChanges="keyboardHidden|orientation|screenSize">
</activity>
and then override onConfigurationChanged() in activity ...
#Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
}

Navigating back to FragmentPagerAdapter -> fragments are empty

I have a Fragment (I'll call it pagerFragment) that is added to the backstack and is visible. It holds a viewPager with a FragmentPagerAdapter. The FragmentPagerAdapter holds (let's say) two fragments: A and B.
First adding of the fragments works great.
Fragment A has a button that once clicked, adds a fragment (C) to the backstack.
The problem is this: if I add that fragment (C), and then click back, the pagerAdapter is empty, and I cannot see any fragments inside.
If I use a hack, and destroy the children fragments (A and B) in the pagerFragments onDestroyView(), this solves the problem, although I don't wan't to use this hack.
Any ideas what the issue could be?
I had the same problem. The solution for me was simple:
in onCreateView I had:
// Create the adapter that will return a fragment for each of the three
// primary sections of the app.
mSectionsPagerAdapter = new SectionsPagerAdapter(getActivity()
.getSupportFragmentManager());
where SectionPageAdapter is something like this:
class SectionsPagerAdapter extends FragmentPagerAdapter {
...
}
after changing getSupportFragmentManager to
mSectionsPagerAdapter = new SectionsPagerAdapter(getChildFragmentManager());
it started working!
It sounds like you are using nested fragments since your ViewPager is inside a PagerFragment. Have you passed getChildFragmentManager() to the constructor of your FragmentPagerAdapter? If not you should.
I don't think you need a FragmentStatePagerAdapter, but I would give that a shot since it handles saving and restoring Fragment state. The fact that your onDestroyView() hack works makes me think that you may want a FragmentStatePagerAdapter.
It could also have something to do with the way the FragmentPagerAdapter adds Fragments. The FragmentPagerAdapter doesn't add Fragments to the backstack. Imagine if you had a 10+ pages added in your ViewPager and the user swiped through them. The user would need to hit back 11 times just to back out of the app.
It may also be related to this post: Nested Fragments and The Back Stack.
Also I'm not sure what you are adding the Fragment C to. Are you adding it to the same container as the ViewPager?
Well at least you have a few options to investigate. In these situations I like to debug down into the Android SDK source code and see what's causing the behaviour. I recommend grabbing the AOSP source and adding frameworks/support and frameworks/base as your SDK sources. That's the only true way to understand what is happening and avoid making random changes until things work.
Use getChildFragmentManager() instead of getSupportFragmentManager().
It will work fine.
I just faced the problem in our project as well. The root cause is the way the the FragmentPagerAdapter works:
The FragmentPagerAdapter just detaches a Fragment he does not currently need from its View but does not remove it from its FragmentManager. When he wants to display the Fragment again he looks if the FragmentManager still contains the Fragment using a tag that is created from the view id of the ViewPager and the id returned by the adapters getItemId(position) call. If he finds a Fragment he just schedules an attach of the Fragment to its View within the updating transaction of the FragmentManager. Only if he does not find a Fragment this way he creates a new one using the adapters getItem(position) call!
The problem with a Fragment containing a ViewPager with a FragmentPagerAdapter is, that the contents of the FragmentManager is never cleaned up when the containing Fragment is put to the back stack. If the containing Fragment comes back from the back stack it creates a new View but the FragmentManager still contains the fragments that were attached to the old view and the attach of an existing fragment does not work anymore.
The easiest way to get rid of this problem is to avoid nested fragments. :)
The second easiest way is as already mentioned in other posts to use the ChildFragmentManager for the FragmentPagerAdapter as this one gets properly updated during the life cycle of the container fragment.
As there are projects (as my current one) where both options are not possible, I have published here a solution that works with an arbitrary FragmentManager by using the hashCode of the sub fragments as the item id of the fragment at that position. It comes at the price of storing all fragments for all positions within the adapter.
public class MyPagerAdapter extends FragmentPagerAdapter {
private static int COUNT = ...;
private final FragmentManager fragmentManager;
private Fragment[] subFragments = new Fragment[COUNT];
private FragmentTransaction cleanupTransaction;
public MyPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
this.fragmentManager = fragmentManager;
}
#Override
public Fragment getItem(int position) {
return getSubFragmentAtPosition(position);
}
#Override
public int getCount() {
return COUNT;
}
#Override
public long getItemId(int position) {
return getSubFragmentAtPosition(position).hashCode();
}
//The next three methods are needed to remove fragments no longer used from the fragment manager
#Override
public void startUpdate(ViewGroup container) {
super.startUpdate(container);
cleanupTransaction = fragmentManager.beginTransaction();
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
cleanupTransaction.remove((Fragment) object);
}
#Override
public void finishUpdate(ViewGroup container) {
super.finishUpdate(container);
cleanupTransaction.commit();
}
private Fragment getSubFragmentAtPosition(int position){
if (subFragments[position] == null){
subFragments[position] = ...;
}
return subFragments[position];
}
}
I had same problem, just set adapter twice at once and that's all.
Example code :
private fun displayImg(photo1:String, photo2:String){
val pager:ViewPager = v?.findViewById(R.id.ProductImgPager)!!
val arr = ArrayList<String>()
arr.add(photo1)
arr.add(photo2)
pager.adapter = AdapterImageView(fm, arr ,arr.size)
pager.adapter = AdapterImageView(fm, arr ,arr.size)
}

FragmentPagerAdapter getItem is not being triggered

Currently, with a FragmentActivity, I toggle among 2 type of Fragments using the following code.
private void toggle() {
Fragment oldFragment = getSupportFragmentManager().findFragmentById(R.id.content);
Fragment fragment = null;
if (oldFragment instanceof ColorFragment) {
fragment = new ViewPagerFragment();
} else {
fragment = new ColorFragment(android.R.color.black);
}
getSupportFragmentManager().beginTransaction().replace(R.id.content, fragment).commitAllowingStateLoss();
}
2 Fragments are being toggle.
ColorFragment - A simple fragment which fill up its background with solid black color.
ViewPagerFragment - A fragment contains ViewPager. User can swipe between a purple color fragment, and a blue color fragment.
The code which responsible for swiping purple and blue color fragments are as below.
private static class MyFragmentPagerAdapter extends FragmentPagerAdapter {
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return 2;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new ColorFragment(android.R.color.holo_purple);
default:
return new ColorFragment(android.R.color.holo_blue_bright);
}
}
}
However, I encounter the weird behavior during toggling.
Black color fragment was shown.
Toggling.
View pager, which can swipe between purple and blue fragments shown.
Toggling.
Black color fragment was shown.
Toggling.
Nothing shown, as MyFragmentPagerAdapter's getItem is not being triggered.
I think my situation is similar to FragmentPagerAdapter getItem is not called
However, I prefer not to use FragmentStatePagerAdapter, because of the cost of potentially more overhead when switching between pages.
Any workaround to overcome this problem?
I include a complete workable source code to demonstrate this problem : https://www.dropbox.com/s/jok9tz5ktvfcteo/viewpagerbug.zip
Any workaround to overcome this problem?
I've downloaded your code and the problem appears because you don't handle those Fragments right. Most precisely you use nested Fragments in the ViewPager based Fragment and for that ViewPager you create the adapter like this:
MyFragmentPagerAdapter myFragmentPagerAdapter = new MyFragmentPagerAdapter(this.getFragmentManager());
Instead, you should be using getChildFragmentManager() to bind the nested fragments:
MyFragmentPagerAdapter myFragmentPagerAdapter = new MyFragmentPagerAdapter(this.getChildFragmentManager());
Also, you shouldn't pass data through a constructor to a Fragment as that data will not survive a configuration change and bad things will start to appear. Use a Bundle instead.
Global working tested solution.
getSupportFragmentManager() keeps the null reference some times and View pager does not create new fragment instance.Since it finds reference to same fragment. So to over come this use getChildFragmentManager() solves problem in simple way.
Don't
new PagerAdapter(getSupportFragmentManager(), fragments);
Do
new PagerAdapter(getChildFragmentManager() , fragments);
Simple use FragmentStatePagerAdapter instead of FragmentPagerAdapter
or
you can use new MyFragmentPagerAdapter(this.getChildFragmentManager())
Hope it will help you :)
In my case I was correctly calling
MyFragmentPagerAdapter myFragmentPagerAdapter = new MyFragmentPagerAdapter(this.getChildFragmentManager());
but then in the nested fragment I was trying to replace the container fragment with another one by using:
getFragmentManager()
You need to go to the activity and call
getActivity().getSupportFragmentManager();
In my cases it worked after add this to my FragmentPagerAdapter:
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
and I also used getChildFragmentManager() like Luksprog said

Categories

Resources