2

I have a page viewer and something really weird is happening on API 21 (It works on API 17).

On the first page of the viewer there’s a link that loads the second page and replaces a fragment in It. The first time I click the link and load the second page It works fine, but if I click on the link after having loaded a third page, the fragment replace doesn’t work and I end up with the second screen without the fragment in it.

Here is a simplified version of code that sets the second page:

ScreenSlidePagerAdapter adapter = (ScreenSlidePagerAdapter) sPager.getAdapter();
// Set second page in the viewer
if (adapter.getCount() != 2) {
    adapter.setCount(2);
    adapter.notifyDataSetChanged();
}
SecondPageFragment frag2 = new SecondPageFragment();
sFragmentManager.beginTransaction().replace(R.id.container_page2, frag2, “Frag2TAG”).commit();
sPager.setCurrentItem(1);

Why does replace work differently on API 21 if compared with API 17? Anyone had a similar problem before? This seems to be related but doesn't have a solution: fragments-transaction-replace-on-api-21-is-staying-behind

EDIT: the code for the SecondPageFragment is just a Fragment with this:

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saved_instance_state) {
    ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_page2, container, false);

    return rootView;
}

And this is the layout fragment_page2:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="@+id/container_page2">

Community
  • 1
  • 1
Marilia
  • 1,691
  • 1
  • 16
  • 26
  • Show code for sFragmentManager declaration. Is it using getSupportFragmentManager()? – The Original Android Jul 25 '15 at 01:07
  • Hi, thank you for the reply. Yes, It's using `getSupportFragmentManager();` – Marilia Jul 25 '15 at 01:09
  • A funny thing about the link you listed on your post, a posted answer has my user name too :-) – The Original Android Jul 25 '15 at 01:18
  • I'm trying to understand... So you're executing a transaction to load a fragment that is being provided by the FragmentPagerAdapter? I'm assuming that your adapter is a FragmentPagerAdapter subclass. FragmentPagerAdapter does all its own transaction management, so executing your own transactions breaks its assumptions and you can get sketchy behavior. Wish I could see more of your code... I have done a *lot* of work with ViewPager and even written a custom PagerAdapter for fragments when FragmentPagerAdapter couldn't give me what I wanted, so I think I can help you out here... – kris larson Jul 25 '15 at 01:30
  • @TheOriginalAndroid : that's funny. I see you've been helping people here It's been a while. Awesome! @kris larson: Thanks for the reply. You're right, I'm using a `class ScreenSlidePagerAdapter extends FragmentPagerAdapter` . The Adapter `getItem(int position)` simply returns a new Page2TransitionFragment() for the second page. The Page2TransitionFragment has a Layout (R.id.container_page2) I replace with another fragment, depending on what was clicked on page 1. I'll try instead of replacing the Layout to do the logic in the Adapter `getItem` method. – Marilia Jul 25 '15 at 03:09

3 Answers3

1

New relevation! It seems to me there is a bug in FragmentPagerAdapter class. Look at SO link @ Replace Fragment inside a ViewPager. Due to your comments of "The first time I click the link and load the second page It works fine...", I think it is related to getItemId(), may have to override it.

Personally, I have used the PagerAdapter inside a Fragment, may use FragmentPagerAdapter in the future because it combines both classes and should be easier.

EDIT: Your ID container_page2 may be using the correct form and layout file. However if you want to use it with replace(), you should be using the same consistent UI element ID. Otherwise the FragmentManager will manage the fragments according to the container view ID, and that's confusing to me.

I have a layout example for the fragment. It is sample_content_fragment, a FrameLayout, nested inside a LinearLayout. Sample layout for the Fragment:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/sample_main_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

    <FrameLayout
         android:id="@+id/sample_content_fragment"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
    />
</LinearLayout>

Keep us posted. I would like to know.

Community
  • 1
  • 1
The Original Android
  • 5,651
  • 3
  • 21
  • 31
  • Overriding `public int getItemPosition(Object object)` affects the behavior. If I set It to always return `POSITION_NONE`, I get a second page created correctly with the fragment after having loaded a third page. However, I get a second page without the fragment if I hit back from the third page. It's like creating the third page reset the previously created second page. xD – Marilia Jul 28 '15 at 00:29
  • @Marilia, It seems your second page needs the fix. Perhaps post code related to the second fragment, I assume it's SecondPageFragment. Another, your quote "I get a second page without the fragment if I hit back from the third page", why should it go back to the second page? You're using replace() only, without addToBackStack(). – The Original Android Jul 28 '15 at 00:55
  • @Marilia, Due to your comments, I just noticed your UI ID and so I added more comments under the EDIT section. – The Original Android Jul 28 '15 at 01:02
  • Hi! Thanks for the reply. I edited the post to add code for the SecondPageFragment and It's layout. It's a page where I replace the fragment in the container_page2 depending on what they clicked on the first page. – Marilia Jul 28 '15 at 18:15
  • @Marilia, I think there is a misunderstanding about the ID container_page2. I believe for replacing fragments you need to keep using the same consistent UI ID for the fragment, as said in my post. That means also you need to reference the same layout XML file. This is the way FragmentManager manages fragments and replace() method. I add few more comments under the EDIT section on sample layout file. This is the same technique I have used. – The Original Android Jul 28 '15 at 19:23
  • @Marilia, If you do reference different layout files and UI ID, then don't expect the FragmentManager and replace() to behave like you claim. This is because the UI ID is the container view for the fragments, like a root key for them. I think you can make it work but it is more complicated, I think. And I have not done that, someday I might. – The Original Android Jul 28 '15 at 19:25
  • @Marilia, I hope you got a chance to review the SO link I provided. It seems useful. – The Original Android Jul 28 '15 at 19:27
  • Hi! I reviewed the link you provided a while ago, yes, and even tho there's no accepted answers I tried the one with more votes. One main difference in my code is that I don't use in the replace "the ID of the view pager itself" or even one single and consistent UI ID, because I need to replace fragments not only in one page, but in the first, second and third page, so while the first page uses R.id.container_page2, the third page uses R.id.container_page3 for example. Another thing I wonder is if the way I set the pages is right. `adapter.setCount(2); adapter.notifyDataSetChanged();` – Marilia Jul 29 '15 at 18:01
  • @Marilia, I am glad you realize those 2 important aspects. Since you insist on having different UI ID, you could use FragmentTransaction methods show() and hide(), as some developers have done. It may be confusing to manage however as some have learned. For anyone to help at this point, you have to post more code. I expect show() and hide() used besides replace(). – The Original Android Jul 29 '15 at 19:03
  • The issue was related to the discrepancy between older API levels and API 21 when replacing fragments after changing the pager amount of pages (adding a 3rd page in this case). What I ended up doing was having only 2 pages and replacing fragments in the second page. What used to be the 3rd page ended up as a fragment inserted in the second page with replace as well (I plan to animate its appearance). That way I kept the amount of pages as 2 and the UI ID consistent for all replaces as you suggested. The second page fragment looks pretty much as the code you posted. Thank you for the answer. :) – Marilia Aug 04 '15 at 23:36
  • I am glad you solved it! It is rather surprising for me that there is a difference between the latest API and older ones. I think the older APIs may have a bug due to my understanding of fragment management. I'll put a Star on your question so that I can remember and share this post among other developers. Others have solved it in a complicated way using show/hide, don't recommend it for anyone. I'll upvote your question as well. Congrats! – The Original Android Aug 05 '15 at 02:11
  • 1
    Thank you for the upvote. :) The difference between APIs might have been introduced along with the changes to support [Nested Fragments](http://developer.android.com/about/versions/android-4.2.html#NestedFragments) – Marilia Aug 05 '15 at 23:25
0

Method getSupportFragmentManager() references the directory android/support/v4/... Since sFragmentManager is created using getSupportFragmentManager(), you should check all references to Fragment and see if they reference the same support/v4 directory. One quick way is to check the imports for android.support.v4. You can also check by clicking all the references to Fragment.

Another option is not use the "support" packages.

The Original Android
  • 5,651
  • 3
  • 21
  • 31
  • Thanks for the reply. I double checked and I'm using support.v4 in all Fragments. I have to continue using support.v4, since that's what would work with a ViewPager. – Marilia Jul 25 '15 at 03:12
0

For debugging, I suggest using addToBackStack() of FragmentTransaction. This saves data and state of fragments, active in the stack. This will be good for code inspection and possibly have positive impact due to saving states.

Also you can call getBackStackEntryCount() of FragmentManager after the code commit(). In order to do this, you will have to break up the code sFragmentManager.beginTransaction().replace(R.id.container_page2, frag2, “Frag2TAG”).commit(). But it should not be too hard.

The Original Android
  • 5,651
  • 3
  • 21
  • 31