ViewPager Fragments are not recreated after FragmentTransaction.replace() followed by back button - android

I am trying to implement saving and restoring state, but I am running into problems when replacing the main Fragment with a PreferenceFragment and then hitting the back button. My main Fragment consists of a ViewPager with a FragmentPagerAdapter with 3 Fragments to swipe through. None of the Fragment.onCreateView() callbacks for my 3 Fragments are invoked after hitting the back button. I have tried all the solutions I have come over here on SO, but I have not been able to resolve the issue.
Another possibly important thing to note is that the data for my 3 ViewPager Fragments are stored in separate classes which are instanced and accessible through the Activity. The 3 Fragments all contain RecyclerViews for listing a fair amount of data. This is done for cleanliness, but also so that these data persist in the Activity. This is probably not an issue, since it works fine when starting the app, but also since the main issue is that my Fragments are not recreated.
Unexpected behavior:
On application creation, everything works fine, but when I replace my main Fragment (containing a ViewPager with a FragmentPagerAdapter) with another one and then pressing the back button, the Fragments in the ViewPager are not recreated. The onCreateView() of my main Fragment is called.
Questions:
What am I missing? Is there some other callbacks that should be created? Where and how should I recreate the Fragments?
What have I tried:
Changed the FragmentAdapter in use, but I should really use getSupportFragmentManager() as was the solution here.
EDIT: Use of erroneous FragmentManager actually was the source of my troubles. See my answer below.
Add
#Override public int getItemPosition(Object object) {return POSITION_NONE;} to the FragmentPagerAdapter, as suggested here.
Change from FragmentPagerAdapter to FragmentStatePagerAdapter which should not matter here, as far as I understand. The internals of FragmentStatePagerAdapter actually keeps the Fragments, but they are not rendered anew.
Add the main Fragment back using a FragmentTransaction when its onCreateView() is called and in several of the other callbacks of that Fragment, see MMMainFragment below.
Various other things.
My MainFragment class with corresponding XML layout
public class MMMainFragment extends Fragment
{
private MMViewPager mMMViewPager = null;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Note that this method IS called when the back button is pressed.
// I have tried setting the content back to this instance in several places.
View view = inflater.inflate(R.layout.mm_main_fragment, container, false);
// Setup ViewPager.
mMMViewPager = (MMViewPager) view.findViewById(R.id.mm_pager);
TabLayout tabLayout = (TabLayout) view.findViewById(R.id.mm_tablayout);
tabLayout.setupWithViewPager(mMMViewPager);
return view;
}
public MMViewPager getMMViewPager()
{
return mMMViewPager;
}
}
mm_main_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.design.widget.TabLayout
android:id="#+id/mm_tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="scrollable" />
<com.mycompany.myapp.gui.mmpager.MMViewPager
android:id="#+id/mm_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
Then, in Activity.onCreate() i simply do:
// Insert Main Fragment.
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.mm_content_frame, new MMMainFragment())
.commit();
where mm_content_frame is a FrameLayout where I replace and remove Fragments. Then, when a button to view the preferences are pushed, I run the following snippet below in the Activity. I add this Fragment to the backstack to be able to use the back button.
public void showSettingsFragment()
{
getSupportActionBar().setTitle(getString(R.string.mm_drawer_settings));
getSupportFragmentManager().beginTransaction()
.replace(R.id.mm_content_frame, new MMPreferencesFragment(), FragmentConstants.SETTINGS_FRAGMENT_TAG)
.addToBackStack(null)
.commit();
}
The MMViewPager clas:
public class MMViewPager extends ViewPager
{
private MMActivity mMMActivity;
private MMPagerAdapter mMMPagerAdapter;
public MMViewPager(Context context, AttributeSet attrs)
{
super(context, attrs);
mMMActivity = (MMActivity) context;
mMMPagerAdapter = new MMPagerAdapter(mMMActivity.getSupportFragmentManager(), mMMActivity);
this.setOffscreenPageLimit(PagerConstants.OFFSCREEN_PAGE_LIMIT);
this.setAdapter(mMMMPagerAdapter);
this.setCurrentItem(PagerConstants.PAGE_FILTER_RECIPES);
}
}
The MMPagerAdapter class, currently a FragmentStatePagerAdapter:
public class MMPagerAdapter extends FragmentStatePagerAdapter
{
private MMActivity mMMActivity;
public MMPagerAdapter(FragmentManager fragmentManager, MMActivity mmActivity)
{
super(fragmentManager);
mMMActivity = mmActivity;
}
#Override
public Fragment getItem(int position)
{
switch (position)
{
case PagerConstants.PAGE_FILTER_RECIPES:
return new FilteredRecipesFragment();
case PagerConstants.PAGE_SELECTED_RECIPES:
return new SelectedRecipesFragment();
case PagerConstants.PAGE_SHOPPING_LIST:
return new ShoppingListFragment();
default:
return null;
}
}
#Override
public int getCount()
{
return PagerConstants.NUMBER_OF_PAGES; // 3
}
#Override
public CharSequence getPageTitle(int position)
{
switch (position)
{
case PagerConstants.PAGE_FILTER_RECIPES:
return mMMActivity.getResources().getString(R.string.mm_title_recipe_filter_fragment);
case PagerConstants.PAGE_SELECTED_RECIPES:
return mMMActivity.getResources().getString(R.string.mm_title_selected_recipes_fragment);
case PagerConstants.PAGE_SHOPPING_LIST:
return mMMActivity.getResources().getString(R.string.mm_title_shopping_list_fragment);
default:
return "Tab";
}
}
}
For reference, here is also the main layout of the Activity:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/mm_drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Source: http://developer.android.com/training/implementing-navigation/nav-drawer.html -->
<!-- The main content view -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Source: http://android-developers.blogspot.in/2014/10/appcompat-v21-material-design-for-pre.html -->
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mm_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:layout_alignParentTop="true"
style="#style/MMActionBar"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light" />
<!-- The FrameLayout where I replace Fragments -->
<FrameLayout
android:id="#+id/mm_content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/mm_toolbar"/>
</RelativeLayout>
<ListView
android:id="#+id/mm_drawer_listview"
android:layout_width="260dp"
android:layout_height="match_parent"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingStart="16dp"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="0dp"
android:background="#color/mm_white" />
</android.support.v4.widget.DrawerLayout>

After struggling with this for a few days I realized that the problem actually was that I passed the wrong FragmentManager to the ViewPager. It is indeed the FragmentManager returned by Fragment.getChildFragmentManager() that should be used. This makes sense, since it is the Fragment itself that stores the state of the child Fragments in the ViewPager. I am not sure why I couldn't make that work before, but now the LifeCycle of the app works fine with the following setup in my main Fragment's onCreate() method:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.mm_main_fragment, container, false);
mMMActivity = (MMActivity) container.getContext();
mMMActivity.getSupportActionBar().setTitle(getString(R.string.mm_toolbar_title_main_fragment));
// Setup ViewPager.
mViewPager = (ViewPager) view.findViewById(R.id.mm_pager);
mAdapter = new MMPagerAdapter(getChildFragmentManager(), mMMActivity); // <-- This is the key
mViewPager.setAdapter(mAdapter);
mViewPager.setOffscreenPageLimit(PagerConstants.OFFSCREEN_PAGE_LIMIT);
mViewPager.setCurrentItem(PagerConstants.PAGE_FILTER_RECIPES);
// Setup TabLayout.
TabLayout tabLayout = (TabLayout) view.findViewById(R.id.mm_tablayout);
tabLayout.setupWithViewPager(mViewPager);
return view;
}
This was also the solution of another question. As a side note, I use this solution to get the Fragments in my ViewPager and it works just fine with the LifeCycle.
Test Application:
In the research of this issue I created a test application for this. The ones interested can clone it from here. It has ugly colors.

As far as, I understand you have solved your problem. I have faced a quite similar issue in the past. Maybe this help others trying something similar.
The thing that helped me in order to retain my fragment state was to replace the
#Override
public Fragment getItem(int position)
{
switch (position)
{
case PagerConstants.PAGE_FILTER_RECIPES:
return new FilteredRecipesFragment();
case PagerConstants.PAGE_SELECTED_RECIPES:
return new SelectedRecipesFragment();
case PagerConstants.PAGE_SHOPPING_LIST:
return new ShoppingListFragment();
default:
return null;
}
}
method of my adapter.
I change the Adapter so as to keep a HashMap of Fragment. This way the fragments are created once and then retrieved from the HashMap. Depending on your structure you can use an ArrayList instead of the HashMap.
This is done in an efficient and "smart" way of implementing getItemPosition(Object object) to return POSITION_NONE; or the proper position of the item.
I don't really remember if I had the getChildFragmentManager in the adapter or not.
I guess that I could dig into my filesystem if you need further information :)

Related

Fragment with SlidingTabLayout

I've got an app with left navigation drawer, switching between different fragments. Inside one of those fragment I want to implement SlidingTabLayout.
So, what I did is I copied the SlidingTabLayout.java and SlidingTabStrip.java from Google's iosched app. I used them inside my fragment:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<mynamespace.widget.SlidingTabLayout
android:id="#+id/home_fragment_tabs"
android:background="?attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.v4.view.ViewPager
android:id="#+id/home_fragment_pager"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_width="match_parent" />
</LinearLayout>
And then in my Fragment class, inside onCreateView method, I instantiated my pager and SlidingTabLayout like this:
mPager = (ViewPager) view.findViewById(R.id.home_fragment_pager);
mPager.setAdapter(new HomePagerAdapter(getActivity().getSupportFragmentManager()));
mTabs = (SlidingTabLayout) view.findViewById(R.id.home_fragment_tabs);
mTabs.setViewPager(mPager);
And this is my FragmentPagerAdapter:
class HomePagerAdapter extends FragmentPagerAdapter
{
private String[] tabs = { "Today", "This week", "With star" };
private Fragment[] tabFragments = {
new TodayTabFragment(),
new WeekTabFragment(),
new StarTabFragment()
};
public HomePagerAdapter(FragmentManager fm)
{
super(fm);
}
#Override
public Fragment getItem(int i)
{
return tabFragments[i];
}
#Override
public int getCount()
{
return 3;
}
#Override
public CharSequence getPageTitle(int position)
{
return tabs[position];
}
}
Also I got separate Fragment class and layout for each tab, which is nothing special. And it works, but as soon as I switch to other fragment with my navigation drawer, when I get back, content of one of tabs just dissapears. Mostly it happens to second tab. Also, if I select the HomeFragment from navigation drawer, when is already displayed, I get a nullpointerexception inside SlidingTabLayout.java at this line: mViewPager.setCurrentItem(i);
Any way of fixing it? I have no idea what to do. I guess my code would work, but inside activity. Possible to make it work inside fragment?
I also faced the same issue. Please use FragmentStatePagerAdapter instead of FragmentPagerAdapter which will solve this issue.
Solution:
What I did is instead of implementing FragmentPagerAdapter, I implemented PagerAdapter then I overriden the instantiateItem(ViewGroup container, int position) method and destroyItem() method.
Link, that helped me: http://developer.android.com/samples/SlidingTabsBasic/src/com.example.android.slidingtabsbasic/SlidingTabsBasicFragment.html
Can you try to change constructor of HomePagerAdapter to:
public HomePagerAdapter(SupportFragmentManager fm)
{
super(fm);
}
When you begin transaction instead of suuportfragmanager change it to childfragmentmanager

Android: how to add another fragment to the main activity

I asked a question about how to add a Fragment that contained something drawn using OpenGL ES
here. Someone was kind enough to answer that for me, but unfortunately today I encountered another problem. As I mentioned in my other question, my purpose is to add other Fragments next to the one that contains OpenGL and because I am a beginner in Android development I don't seem to understand how this is done.
Here's what I want: right now, my code is exactly the one from my other question. I also have this Fragment:
public class TextFragment extends Fragment
{
private TextView textview;
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.text_fragment,
container, false);
textview = (TextView) view.findViewById(R.id.textView1);
return view;
}
}
together with its layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/frag2">
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="Fragment Two"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
and I want to add this to my main activity, where right now I only have the OpenGL Fragment. Here's my main activity:
public class FragmentExampleActivity extends FragmentActivity implements ToolbarFragment.ToolbarListener
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener()
{
public void onBackStackChanged()
{
int backCount = getSupportFragmentManager().getBackStackEntryCount();
if (backCount == 0)
{
finish();
}
}
});
if (savedInstanceState == null)
{
getSupportFragmentManager()
.beginTransaction()
.add(R.id.main_container, new OpenGLES20ActivityFrag())
.addToBackStack(null)
.commit();
}
}
}
and the Fragment that has OpenGL in it and that I have already added to the main activity:
public class OpenGLES20ActivityFrag extends Fragment
{
private GLSurfaceView mGLView;
public OpenGLES20ActivityFrag()
{
super();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
mGLView = new MyGLSurfaceView(this.getActivity());
return mGLView;
}
}
What I tried and failed: using another call to the .add method inside getSupportFragmentManager() or adapting this bit of code for my second Fragment
getSupportFragmentManager()
.beginTransaction()
.add(R.id.frag2, TextFragment)
.addToBackStack(null)
.commit();
that gave me an 'expression expected' error in the add method. I tried adding this constructor to my second Fragment
public TextFragment()
{
super();
}
and then inside the add method I put .add(R.id.frag2, new TextFragment())
which still didn't work.
In order to dynamically add a Fragment to a layout, what you need is a container (like in your case, it was R.id.main_container). Thus, if you want to add multiple fragments, what you need is multiple containers, like so:
<LinearLayout android:orientation="vertical" android:layout_height="fill_parent" android:layout_width="fill_parent">
<FrameLayout android:id="#+id/main_container_1" android:layout_weight="1" android:layout_height="fill_parent" android:layout_width="fill_parent"/>
<FrameLayout android:id="#+id/main_container_2" android:layout_weight="1" android:layout_height="fill_parent" android:layout_width="fill_parent"/>
</LinearLayout>
(this snippet is from How to split the screen with two equal LinearLayouts? )
And then you would need to add the two Fragments:
if (savedInstanceState == null)
{
getSupportFragmentManager()
.beginTransaction()
.add(R.id.main_container_1, new OpenGLES20ActivityFrag())
.commit();
getSupportFragmentManager()
.beginTransaction()
.add(R.id.main_container_2, new TextFragment())
.commit();
}
Please note that with multiple Fragments on a single Activity, it's better not to add them to the backstack, because then you'd have to press Back as many times as there are Fragments, and in this case it's more reasonable to navigate between the "views" or states of the application with Activities, and not by replacing the Fragments.
(considering the backstack doesn't change, I don't think the backstack listener needs to be removed, but that's done so that if you press Back, you don't end the Activity, but the Fragments within it first if you have them added to the backstack. But the Activity doesn't end when it contains no fragments, and you'd have an "empty view", hence why that was added.)
Please also check if the rotation works and data is maintained even after the activity reconstruction, because there's a chance you need to set the retain instance state to true explicitly on the Fragments for that to work.

Android : Swipe within fragment

Could anybody give me a clue in the following situation :
I have an Android 3+ app, which consists of a sofisticated set of interchangably operating activities derived from ActionBarActivity. One of them includes a set of fragments derived from Fragment, that can be selected by user by means of
getSupportFragmentManager().beginTransaction()
.replace(R.id.containerBase, FragmentX.getInstance(0))
.commit()
where
FragmentX extends Fragment {
public static FragmentX getInstance() {
FragmentX fragment = new FragmentX();
return fragment;
}
public FragmentX() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_x, container, false);
. . . . . . . .
return rootView;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
. . . . . . . .
}
}
All fragments (X, Y, Z etc) appear properly and I was happy until it became necessary to modify some fragments so that they can have several subpages, which should be swipable horizontally by user. Other fragments should stay as they are.
Which Android classes could be the optimal choice and how could they be intergrated in my app?
Any help would be highly appreciated.
The Fragment that needs to have "horizontally swipable subpages" should probably use a ViewPager of some sort. Your use case isn't completely clear but in my experience a ViewPager with another set of fragments provides a nice and flexible solution. Also, if your "child" fragments (those in the ViewPager) are essentially the same but display different data (e.g. a set of event details fragments), this fragment can also be reused. If not, you can use a switch or something similar in the ViewPager's public Fragment getItem(int position) method.
You should take a look at the FragmentStatePagerAdapter which should fit your needs, the linked page also has a nice and detailed example of how to use it. One thing to note here, since you are using fragments within fragments, the fragment which contains the ViewPager will need to use getSupportChildFragmentManager instead of the regular getSupportFragmentManager to display those fragments.
Certainly I must give some more details about my app.
public class BasePage extends ActionBarActivity implements NavigationBase.NavigationDrawerCallbacks {
private int mPage;
private NavigationBase mNavigationFragment;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
. . . . . . . .
setContentView(R.layout.activity_base);
mNavigationFragment = (NavigationBase) fragmentManager.findFragmentById(R.id.navigation_drawer);
mNavigationFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
private void setPage() {
switch(mPage) {
case 1:
fragmentManager.beginTransaction()
.replace(R.id.containerBase, FragmentX.getInstance())
.commit();
break;
case 2:
fragmentManager.beginTransaction()
.replace(R.id.containerBase, FragmentY.getInstance())
.commit();
break;
}
}
}
The activity_base layout looks like this:
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="app.BasePage">
<RelativeLayout
android:id="#+id/containerBase"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<fragment
android:id="#+id/navigation_drawer"
android:layout_width="#dimen/navigation_drawer_width"
android:layout_height="match_parent"
android:layout_gravity="start"
android:name="app.NavigationBase" />
</android.support.v4.widget.DrawerLayout>
User navigates through current fragments by setting mPage and calling setPage().
Now I need to modify the fragment X thus it has several subpages, whereas the fragment Y must stay as it is. Programmatically the classes FragmentX and FragmentY are identical, they display solely different user contents.
I comprehend that ViewPager must be a right solution in this scenario. My problem is I do not see where to build the ViewPager in my current code...

No view found for ID xxx for fragment (nested fragments)

I have an activity that has a ViewPager in the layout.
I have two fragments which display, one for each tab.
One of the fragments is designed to host other fragments - this is the CustomerMainFragment which inflates fragment_customer_main:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/lyt_customer_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<FrameLayout
android:id="#+id/fragment_customer_content"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
This then adds CustomerSearchFragment to the FrameLayout which inflates fragment_customer_search.
CustomerSearchFragment also has the following override to switch out the search fragment for a detail fragment on a button press:
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button btnSearch1 = (Button) view.findViewById(R.id.button1);
if (btnSearch1 != null) {
btnSearch1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// This is important bit
Fragment customerDetailFragment = new CustomerDetailFragment();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.addToBackStack(null);
transaction.replace(R.id.fragment_customer_content, customerDetailFragment).commit();
}
});
}
}
After clicking the button I get the following error:
java.lang.IllegalArgumentException: No view found for id 0x7f080006
(com.chrisbeckyapps.sample:id/fragment_customer_content) for fragment
CustomerDetailFragment{4280b0b8 #0 id=0x7f080006}
I'm new to fragments and understand the concepts, but I'm stumped by this. I originally had the search fragment going straight into the pager, but then replacing it with the detail fragment mean it just showed over the top, and my research led to this being a better solution.
I have wondered about trying to move the search logic to the CustomerMainFragment but this means hooking up a lot of logic and I thought you could embed logic within fragments.
Suggestions?
Sorry, just found such a simple fix.
In my onclick handler, I just had to change from getChildFragmentManager to getFragmentManager

Android Fragment no view found for ID?

I have a fragment I am trying to add into a view.
FragmentManager fragMgr=getSupportFragmentManager();
feed_parser_activity content = (feed_parser_activity)fragMgr
.findFragmentById(R.id.feedContentContainer);
FragmentTransaction xaction=fragMgr.beginTransaction();
if (content == null || content.isRemoving()) {
content=new feed_parser_activity(item.getLink().toString());
xaction
.add(R.id.feedContentContainer, content)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.addToBackStack(null)
.commit();
Log.e("Abstract", "DONE");
}
When this code is executed I get the following error in debug..
java.lang.IllegalArgumentException: No view found for id 0x7f080011
for fragment feed_parser_activity{41882f50 #2 id=0x7f080011}
feed_parser_activity is a Fragment that is set to Fragment layout in xml.
I am using a FragmentActivity to host the Fragment Layout holding the feed_parser_layout.
Am I coding this correctly above?
I was having this problem too, until I realized that I had specified the wrong layout in setContentView() of the onCreate() method of the FragmentActivity.
The id passed into FragmentTransaction.add(), in your case R.id.feedContentContainer, must be a child of the layout specified in setContentView().
You didn't show us your onCreate() method, so perhaps this is the same problem.
This error also occurs when having nested Fragments and adding them with getSupportFragmentManager() instead of getChildFragmentManager().
The solution was to use getChildFragmentManager()
instead of getFragmentManager()
when calling from a fragment. If you are calling the method from an activity, then use getFragmentManager().
That will solve the problem.
Another scenario I have met.
If you use nested fragments, say a ViewPager in a Fragment with it's pages also Fragments.
When you do Fragment transaction in the inner fragment(page of ViewPager), you will need
FragmentManager fragmentManager = getActivity().getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
getActivity() is the key here.
...
I had this problem (when building my UI in code) and it was caused by my ViewPager (that showed Fragments) not having an ID set, so I simply used pager.setID(id) and then it worked.
This page helped me figure that out.
In my case I was trying to show a DialogFragment containing a pager and this exception was thrown when the FragmentPagerAdapter attempted to add the Fragments to the pager. Based on howettl answer I guess that it was due to the Pager parent was not the view set in setContentView() in my FragmentActivity.
The only change I did to solve the problem was to create the FragmentPagerAdapter passing in a FragmentMager obtained by calling getChildFragmentManager(), not the one obtained by calling getFragmentManager() as I normally do.
public class PagerDialog extends DialogFragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.pager_dialog, container, false);
MyPagerAdapter pagerAdapter = new MyPagerAdapter(getChildFragmentManager());
ViewPager pager = (ViewPager) rootView.findViewById(R.id.pager);
pager.setAdapter(pagerAdapter);
return rootView;
}
}
This exception can also happen if the layout ID which you are passing to FragmentTransaction.replace(int ID, fragment) exists in other layouts that are being inflated. Make sure the layout ID is unique and it should work.
With Nested fragments
For me by using getChildFragmentManager() instead of getActivity().getSupportFragmentManager() resolved crash
java.lang.IllegalArgumentException: No view found for id
An answer I read on another thread similar to this one that worked for me when I had this problem involved the layout xml.
Your logcat says "No view found for id 0x7f080011".
Open up the gen->package->R.java->id and then look for id 0x7f080011.
When I had this problem, this id belonged to a FrameLayout in my activity_main.xml file.
The FrameLayout did not have an ID (there was no statement android:id = "blablabla").
Make sure that all of your components in all of your layouts have IDs, particularly the component cited in the logcat.
I got this error when I upgraded from com.android.support:support-v4:21.0.0 to com.android.support:support-v4:22.1.1.
I had to change my layout from this:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container_frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
To this:
<?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">
<FrameLayout
android:id="#+id/container_frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</FrameLayout>
So the layout MUST have a child view. I'm assuming they enforced this in the new library.
I know this has already been answered for one scenario, but my problem was slightly different and I thought I'd share in case anybody else is in my shoes.
I was making a transaction within onCreate(), but at this point the view tree has not been inflated so you get this same error. Putting the transaction code in onResume() made everything run fine.
So just make sure your transaction code runs after the view tree has been inflated!
I was facing a Nasty error when using Viewpager within Recycler View.
Below error I faced in a special situation.
I started a fragment which had a RecyclerView with Viewpager (using FragmentStatePagerAdapter). It worked well until I switched to different fragment on click of a Cell in RecyclerView, and then navigated back using Phone's hardware Back button and App crashed.
And what's funny about this was that I had two Viewpagers in same RecyclerView and both were about 5 cells away(other wasn't visible on screen, it was down). So initially I just applied the Solution to the first Viewpager and left other one as it is (Viewpager using Fragments).
Navigating back worked fine, when first view pager was viewable . Now when i scrolled down to the second one and then changed fragment and came back , it crashed (Same thing happened with the first one). So I had to change both the Viewpagers.
Anyway, read below to find working solution.
Crash Error below:
java.lang.IllegalArgumentException: No view found for id 0x7f0c0098 (com.kk:id/pagerDetailAndTips) for fragment ProductDetailsAndTipsFragment{189bcbce #0 id=0x7f0c0098}
Spent hours debugging it. Read this complete Thread post till the bottom applying all the solutions including making sure that I am passing childFragmentManager.
Nothing worked.
Finally instead of using FragmentStatePagerAdapter , I extended PagerAdapter and used it in Viewpager without Using fragments. I believe some where there is a BUG with nested fragments. Anyway, we have options. Read ...
Below link was very helpful :
Viewpager Without Fragments
Link may die so I am posting my implemented Solution here below:
public class ScreenSlidePagerAdapter extends PagerAdapter {
private static final String TAG = "ScreenSlidePager";
ProductDetails productDetails;
ImageView imgProductImage;
ArrayList<Imagelist> imagelists;
Context mContext;
// Constructor
public ScreenSlidePagerAdapter(Context mContext,ProductDetails productDetails) {
//super(fm);
this.mContext = mContext;
this.productDetails = productDetails;
}
// Here is where you inflate your View and instantiate each View and set their values
#Override
public Object instantiateItem(ViewGroup container, int position) {
LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup layout = (ViewGroup) inflater.inflate(R.layout.product_image_slide_cell,container,false);
imgProductImage = (ImageView) layout.findViewById(R.id.imgSlidingProductImage);
String url = null;
if (imagelists != null) {
url = imagelists.get(position).getImage();
}
// This is UniversalImageLoader Image downloader method to download and set Image onto Imageview
ImageLoader.getInstance().displayImage(url, imgProductImage, Kk.options);
// Finally add view to Viewgroup. Same as where we return our fragment in FragmentStatePagerAdapter
container.addView(layout);
return layout;
}
// Write as it is. I don't know much about it
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
/*super.destroyItem(container, position, object);*/
}
// Get the count
#Override
public int getCount() {
int size = 0;
if (productDetails != null) {
imagelists = productDetails.getImagelist();
if (imagelists != null) {
size = imagelists.size();
}
}
Log.d(TAG,"Adapter Size = "+size);
return size;
}
// Write as it is. I don't know much about it
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
Hope this was helpful !!
Just in case someone's made the same stupid mistake I did; check that you're not overwriting the activity content somewhere (i.e. look for additional calls to setContentView)
In my case, due to careless copy and pasting, I used DataBindingUtil.setContentView in my fragment, instead of DataBindingUtil.inflate, which messed up the state of the activity.
I had the same issue but my issue was happenning on orientation change. None of the other solutions worked. So it turns out that I forgot to remove setRetainInstance(true); from my fragments, when doing a two or one pane layout based on screen size.
My mistake was on the FragamentTransaction.
I was doing this t.replace(R.layout.mylayout); instead of t.replace(R.id.mylayout);
The difference is that one is the layout and the other is a reference to the layout(id)
This happens when you are calling from a fragment inside another one.
use :
getActivity().getSupportFragmentManager().beginTransaction();
I had this same issue, let me post my code so that you can all see it, and not do the same thing that I did.
#Override
protected void onResume()
{
super.onResume();
fragManager = getSupportFragmentManager();
Fragment answerPad=getDefaultAnswerPad();
setAnswerPad(answerPad);
setContentView(R.layout.abstract_test_view);
}
protected void setAnswerPad(AbstractAnswerFragment pad)
{
fragManager.beginTransaction()
.add(R.id.AnswerArea, pad, "AnswerArea")
.commit();
fragManager.executePendingTransactions();
}
Note that I was setting up fragments before I setContentView. Ooops.
This page seems to be a good central location for posting suggestions about the Fragment IllegalArgumentException. Here is one more thing you can try. This is what finally worked for me:
I had forgotten that I had a separate layout file for landscape orientation. After I added my FrameLayout container there, too, the fragment worked.
On a separate note, if you have already tried everything else suggested on this page (and the entire Internet, too) and have been pulling out your hair for hours, consider just dumping these annoying fragments and going back to a good old standard layout. (That's actually what I was in the process of doing when I finally discovered my problem.) You can still use the container concept. However, instead of filling it with a fragment, you can use the xml include tag to fill it with the same layout that you would have used in your fragment. You could do something like this in your main layout:
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<include layout="#layout/former_fragment_layout" />
</FrameLayout>
where former_fragment_layout is the name of the xml layout file that you were trying to use in your fragment. See Re-using Layouts with include for more info.
I fixed this bug, I use the commitNow() replace commit().
mFragment.getChildFragmentManager()
.beginTransaction()
.replace(R.id.main_fragment_container,fragment)
.commitNowAllowingStateLoss();
The commitNow is a sync method, the commit() method is an async method.
I use View Binding in my project and was inattentive to add setContentView() after inflating ActivityHelloWorldBinding class:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHelloWorldBinding.inflate(layoutInflater)
// Add this line.
setContentView(binding.root)
}
In my case I had a SupportMapFragment in a recycler view item (I was using the lower overhead "liteMode" which makes the map appear as non-interactive, almost like a static image). I was using the correct FragmentManager, and everything appeared to work fine... with a small list. Once the list of items exceeded the screen height by a bit then I started getting this issue when scrolling.
Turned out, it was because I was injecting a dynamic SupportMapFragment inside a view, which was inside another fragment, to get around some issues I was having when trying to declare it statically in my XML. Because of this, the fragment placeholder layout could only be replaced with the actual fragment once the view was attached to the window, i.e. visible on screen. So I had put my code for initialising the SupportMapFragment, doing the Fragment replace, and calling getMapAsync() in the onAttachedToWindow event.
What I forgot to do was ensure that my code didn't run twice. I.e. in onAttachedToWindow event, check if my dynamic SupportMapFragment was still null before trying to create a new instance of it and do a Fragment replace. When the item goes off the top of the RecyclerView, it is detached from the window, then reattached when you scroll back to it, so this event is fired multiple times.
Once I added the null check, it happened only once per RecyclerView item and issue went away! TL;DR!
This issue also happens when you don't put <include layout="#layout/your_fragment_layout"/> in your app_bar_main.xml
use childFragmentManager instead of activity!!.supportFragmentManager
I encountered this problem when I tried to replace view with my fragment in onCreateView(). Like this:
public class MyProjectListFrag extends Fragment {
private MyProjectListFragment myProjectListFragment;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
FragmentManager mFragmentManager = getFragmentManager();
myProjectListFragment = new MyProjectListFragment();
mFragmentManager
.beginTransaction()
.replace(R.id.container_for_my_pro_list,
myProjectListFragment, "myProjectListFragment")
.commit();
}
It told me
11-25 14:06:04.848: E/AndroidRuntime(26040): java.lang.IllegalArgumentException: No view found for id 0x7f05003f (com.example.myays:id/container_for_my_pro_list) for fragment MyProjectListFragment{41692f40 #2 id=0x7f05003f myProjectListFragment}
Then I fixed this issue with putting replace into onActivityCreated(). Like this:
public class MyProjectListFrag extends Fragment {
private final static String TAG = "lch";
private MyProjectListFragment myProjectListFragment;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater
.inflate(R.layout.frag_my_project_list, container, false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
FragmentManager mFragmentManager = getFragmentManager();
myProjectListFragment = new MyProjectListFragment();
mFragmentManager
.beginTransaction()
.replace(R.id.container_for_my_pro_list,
myProjectListFragment, "myProjectListFragment")
.commit();
}
You have to return a view in onCreateView() so that you can replace it later
You can put any operation towards this view in the following function in fragment liftcycle, like onActivityCreated()
Hope this helps!
In my case this exception was thrown when I used different ids for the same layout element (fragment placeholder) while having several of them for different Build Variants. For some reason it works perfectly well when you are replacing fragment for the first time, but if you try to do it again you get this exception.
So be sure you are using the same id if you have multiple layouts for different Build Variants.
I was having this problem. In my case I have forgotten to add FrameLayout in my Xml File, after adding frame layout, my problem has been solved.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/wraper"
android:layout_above="#id/wraper"/>
If you are trying to replace a fragment within a fragment with the fragmentManager but you are not inflating the parent fragment that can cause an issue.
In BaseFragment.java OnCreateView:
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.replace(R.id.container, new DifferentFragment())
.commit();
}
return super.onCreateView(inflater, container, savedInstanceState);
Replace super.onCreateView(inflater, container, savedInstanceState);
with inflating the correct layout for the fragment:
return inflater.inflate(R.layout.base_fragment, container, false);
I've had the same problem when was doing fragment transaction while activity creation.
The core problem is what Nick has already pointed out - view tree has not been inflated yet. But his solution didn't work - the same exception in onResume, onPostCreate etc.
The solution is to add callback to container fragment to signal when it's ready:
public class MyContainerFragment extends Fragment {
public static interface Callbacks {
void onMyContainerAttached();
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
Log.d(TAG, "--- onAttach");
((Callbacks) activity).onMyContainerAttached();
}
//... rest of code
}
And then in activity:
public class MainActivity extends Activity
implements MyContainerFragment.Callbacks
{
#Override
public void onMyContainerAttached() {
getFragmentManager()
.beginTransaction()
.replace(R.id.containerFrame, new MyFragment())
.commit();
}
//...
}
In my case, i was using a fragment class file to declare a listview adapter class.
I just used a different file for the public adapter class and the error was gone.
It happens also when you have two views in two fragments with the same ids

Categories

Resources