I can´t execute code in the last fragment of a TabLayout - android

My problem: I am following this link " https://guides.codepath.com/android/google-play-style-tabs-using-tablayout " to develop a sliding tab layout and get something like this:
Image
I followed the guide and the SlideTabLayout seems to work pretty good, BUT. I´ve realize that if i try to make some code it works perfect in all the tabs but in the last one. I have to say i always use the same Fragment class, so the code should work on all the tabs.
For example, I´ve put a Log.e message in the fragment and i see the message in all the tabs but the last one.
After all i´ve tried following another guide using PagerTabStrip instead of TabLayout and I am having the same problem.
The fun part is the last page shows correctly the TextView with the page number.
Fragment pager adapter:
public static class MyPagerAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3;
public MyPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
// Returns total number of pages
#Override
public int getCount() {
return NUM_ITEMS;
}
// Returns the fragment to display for that page
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: // Fragment # 0 - This will show FirstFragment
return PageFragment.newInstance(0, "Page # 1");
case 1: // Fragment # 0 - This will show FirstFragment different title
return PageFragment.newInstance(1, "Page # 2");
case 2: // Fragment # 1 - This will show SecondFragment
return PageFragment.newInstance(2, "Page # 3");
default:
return null;
}
}
// Returns the page title for the top indicator
#Override
public CharSequence getPageTitle(int position) {
return "Page " + position;
}
}

You see two fragments created because that is how FragmentPagerAdapter works to provide you a smooth transition between 2 tabs (2 fragments). I guess that your code may run for the first time, then it will not run if the fragment is already in memory. This is how Android says about FragmentPagerAdapter
"This version of the pager is best for use when there are a handful of typically more static fragments to be paged through, such as a set of tabs. The fragment of each page the user visits will be kept in memory, though its view hierarchy may be destroyed when not visible. This can result in using a significant amount of memory since fragment instances can hold on to an arbitrary amount of state"

Related

Design Issue - Launching new Fragment from TabLayout

I'm not quite sure if there's a "best way" to tackle the following design issue.
I have a Tablayout with 2 Tabs in my MainActivity. Each Tab is a different Fragment. I go to Tab1 and see Fragment1. I need to launch a new Fragment (1A) from Fragment 1 and am not sure the best way to do it? I was thinking about one of these.
A) Take the Tabs out of my MainActivity and place them in a separate MainFragment, which gets launched with the app. That way when the user launches Fragment 1A, it replaces just the 1 MainFragment with the Tabs.
or
B) Keep the Tabs in the MainActivity and find a way to replace Fragment 1 with Fragment 1A when under Tab1.
Any suggestions would be appreciated. Thank you.
I think you shouldn't do both of points... Frag1 visible under Tab1 should contain all the layout (including initialy hidden) and logic for this view. If you need to show smth new it may be smaller (then popup, dialog etc.) or expand some layout, maybe with some animation (you may still use ViewPager inside Fragment inside ViewPager, disable touch events and manipulate scrolling programmatically...).
When Action picked by user is intended to show smth so-much-improtant that previous screen is not needed at all then you should probably open new Activity
PS. If you insist to replace current "screen" (in fact Activitys content) note that title of Tab1 may not representing what contains Frag1A. It very depends what kind of content you have there. You may consider move TabLayout/RecyclerView to e.g. FrameLayout container and add to it you Frag1A covering whole previous view including Tabs. In current design guidelines you can even find suggested solution for way of showing new fragment - with circular reveal animation
I do not understand very well what you want, but possibly this.
Use FragmentPagerAdapter..
public class TabsPagerAdapter extends FragmentPagerAdapter {
private static final int NUM_TABS = 3;
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch(position){
case 0:
return Tab1Fragment.newInstance();
case 1:
return Tab2Fragment.newInstance();
default:
return Tab3Fragment.newInstance();
}
}
#Override
public int getCount() {
return NUM_TABS;
}
#Override
public CharSequence getPageTitle(int position) {
if (position == 0){
return "Tab 1";
}
if (position == 1){
return "Tab 2";
}
return "Tab 3";
}
In your activity...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Tabs
TabLayout tabs = (TabLayout) findViewById(R.id.tabs);
ViewPager pager = (ViewPager) findViewById(R.id.pager);
TabsPagerAdapter adapter = new TabsPagerAdapter(getSupportFragmentManager());
pager.setAdapter(adapter);
tabs.setupWithViewPager(pager);
}

Loading same data from API on different Tab Fragments

I have 3 tab fragments in my app, each meant to show different data from the same API URL (one call of 10 records).
This is the final result im trying to achieve:
Tab 1: Show records 1-5 from my API URL
Tab 2: Show records 6-10 from my API URL
Tab 3: Show different values from records 1-10
My goal is to load all this data in the first tab (Tab1) and show different results from the the JSON data returned in each tab as explained above.
Progress so far: Coded Tab1 to show records 1-10.
Need your help with:
Make Tab1 show records 1-5 and not all 10. Since I want to load all 10 results in the same run and wont call the API multiple times, I don't wish to limit the number of results.
Code Tab2 show records 6-10 from the JSON DATA received in Tab1. Means I need to communicate from Tab2 to my Tab1
Hope to get direction for this, Thanks!
What I've been thinking about:
Setting 3 static JSONOAdapters and set it with the json result and use it for each tab. but..
Does static JSON Adapters will be a good solution or may cause memory problems?
Another problem: When I use this:
public void updateData(JSONArray jsonArray) {
// update the adapter's dataset
mJsonArray = jsonArray;
notifyDataSetChanged();
}
I get results 1-10, I have no idea how to limit it to the first 5 or results 6-10.
Load data from 1 to 10 in your activity or fragment contains 2 fragments
After that add function setData for each fragment which data you want.
Last, go to your fragment and load data you set before.
Load Data in your activity.
Initiate ViewPager Adapter and send Array of Items in the Constructor.
public class MyPagerAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3;
ArrayList<Item> arr;
public MyPagerAdapter(FragmentManager fragmentManager,ArrayList<Item> arr) {
super(fragmentManager);
this.arr=arr;
}
// Returns total number of pages
#Override
public int getCount() {
return NUM_ITEMS;
}
// Returns the fragment to display for that page
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: // Fragment # 0 - This will show FirstFragment
return FirstFragment.newInstance(arr.subList(0,5));
case 1: // Fragment # 1
return FirstFragment.newInstance(arr.subList(6,10));
case 2: // Fragment # 2
//Change your values according to you need then pass the arr in Frag 3
return SecondFragment.newInstance(arr);
default:
return null;
}
}
// Returns the page title for the top indicator
#Override
public CharSequence getPageTitle(int position) {
return "Page " + position;
}
}
Make static method in your Fragment to receive the array of items.

I've got a weird bug when using a ViewPager inside a Fragment

Okay i'll try and make this as clear as possible. I have a Fragment called CheckerManager which contains a ViewPager. This ViewPager will allow the user to swipe between 3 Fragments all of which are an instance of another Fragment called CheckerFragment. I'm using a FragmentPagerAdapter to handle paging. Here's how it looks
private class PagerAdapter extends FragmentPagerAdapter {
CharSequence mTabTitles[];
public PagerAdapter(FragmentManager fm, CharSequence tabTitles[]) {
super(fm);
mTabTitles = tabTitles;
}
#Override
public Fragment getItem(int position) {
switch(position) {
case 0:
return CheckerFragment.newInstance(MainFragment.DRAW_TITLE_LOTTO);
case 1:
return CheckerFragment.newInstance(MainFragment.DRAW_TITLE_DAILY);
case 2:
return CheckerFragment.newInstance(MainFragment.DRAW_TITLE_EURO);
default:
return null;
}
}
#Override
public CharSequence getPageTitle(int position) {
return mTabTitles[position];
}
#Override
public int getCount() {
return 3;
}
}
I know that the ViewPager will always create the Fragment either side of the current Fragment. So say my 3 CheckerFragments are called A, B and C and the current Fragment is A. B has already been created. But my problem is that even though I am still looking at Fragment A, Fragment B is the 'active' Fragment. Every input I make is actually corresponding to Fragment B and not A. The active Fragment is always the one which has been created last by the ViewPager.
I've looked at quite a few things to see if anyone has had the same problem but i'm finding it difficult to even describe what's wrong. I think it's something to with the fact that all of the ViewPagers fragments are of the same type ie - CheckerFragment. I have a working implementation of a ViewPager inside a fragment elsewhere in the application and the only difference I can tell is that each page is a different type of Fragment.
Any help would be appreciated!
*EDIT
PagerAdapter adapter = new PagerAdapter(getChildFragmentManager(), tabTitles);
ViewPager viewPager = (ViewPager)view.findViewById(R.id.viewPagerChecker);
viewPager.setAdapter(adapter);
I feel pretty stupid but I found out what the issue was. In my CheckerFragment I would call getArguments() to retrieve a String extra and I would use this to determine how to layout the fragment. Problem was I made this extra a static member of CheckerFragment. So every time a new Fragment was created it was using the most recent extra.
Moral of the story - Don't make your fragments extra a static member if you plan on making multiple instances of that fragment.

FragmentPageAdapter works in a strange way

Since in Android L the the Action bar navigation modes are deprecated I'm searching an other way to have the tab and I found that is possible to use the PagerTabStrip (android.support.v4.view.PagerTabStrip), so I created a FragmentPageAdapter in this way:
public class TitleAdapter extends FragmentPagerAdapter {
private final String titles[] = new String[] { "Home", "Events", "People", "Books"};
private final Fragment frags[] = new Fragment[titles.length];
Context context;
public TitleAdapter(FragmentManager fm) {
super(fm);
}
#Override
public CharSequence getPageTitle(int position) {
Log.v("TitleAdapter - getPageTitle=", titles[position]);
return titles[position];
}
#Override
public Fragment getItem(int position) {
Log.v("TitleAdapter - getItem=", String.valueOf(position));
//return frags[position];
switch (position) {
case 0:
return Home.newInstance(0, "Home");
case 1:
return Events.newInstance(1, "Events");
case 2:
return People.newInstance(2, "People");
case 4:
return Books.newInstance(3, "Books");
}
return null;
}
#Override
public int getCount() {
return frags.length;
}
}
The strange way that i see in LogCat is that the method getItem() is called 4 times when the mainActivity starts so I've to wait a lots because in each Tab there is a quite long list and this list is populated via HTTP request calling a web service.
I wish load only a fragment each times and not all. When i used actionbar.Tablistener it was possible but now the method is deprecated so is there a way to do that?
I set the adapter and the viewPager in the onCreate method in this way:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.prova_page_tab_stripes);
mViewPager = (ViewPager) findViewById(R.id.viewpager);
titleAdapter = new TitleAdapter(getSupportFragmentManager());
mViewPager.setAdapter(titleAdapter);
mViewPager.setOffscreenPageLimit(1);
}
The number of pages initialized depends on setOffscreenPageLimit function of ViewPager. As per android doc in http://developer.android.com/reference/android/support/v4/view/ViewPager.html#setOffscreenPageLimit(int),
public void setOffscreenPageLimit (int limit)
Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state. Pages beyond this limit will be recreated from the adapter when needed.
This is offered as an optimization. If you know in advance the number of pages you will need to support or have lazy-loading mechanisms in place on your pages, tweaking this setting can have benefits in perceived smoothness of paging animations and interaction. If you have a small number of pages (3-4) that you can keep active all at once, less time will be spent in layout for newly created view subtrees as the user pages back and forth.
You should keep this limit low, especially if your pages have complex layouts. This setting defaults to 1.
Parameters
limit How many pages will be kept offscreen in an idle state.
Set it to 1 or keep it to default if you wan to limit pages to be retained.
But if you want to load the data only in one page at a time, you can determine when fragment becomes visible and then load the data (insted of loading in onCreateView.)
Refer this question: How to determine when Fragment becomes visible in ViewPager

Swapping fragments in a viewpager

I have a ViewPager with 3 Fragments and my FragmentPagerAdapter:
private class test_pager extends FragmentPagerAdapter {
public test_pager(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
return fragments[i];
}
#Override
public long getItemId(int position) {
if (position == 1) {
long res = fragments[position].hashCode()+fragment1_state.hashCode();
Log.d(TAG, "getItemId for position 1: "+res);
return res;
} else
return fragments[position].hashCode();
}
#Override
public int getCount() {
return fragments[2] == null ? 2 : 3;
}
#Override
public int getItemPosition(Object object) {
Fragment fragment = (Fragment) object;
for (int i=0; i<3; i++)
if (fragment.equals(fragments[i])){
if (i==1) {
return 1; // not sure if that makes a difference
}
return POSITION_UNCHANGED;
}
return POSITION_NONE;
}
}
In one of the page (#1), I keep changing the fragment to be displayed. The way I remove the old fragment is like this:
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction().remove(old_fragment1).commit();
And then just changing the value of fragments[1]
I found that I cannot really add or replace the new one or it will complain the ViewPager is trying to add it too with another tag... (am I doing something wrong here?)
All the fragments I display have setRetainInstance(true); in their onCreate function.
My problem is that this usually works well for the first few replacement, but then when I try to reuse a fragment, sometimes (I have not really figured out the pattern, the same fragment may be displayed several times before this happens) it will only show a blank page.
Here is what I have found happened in the callback functions of my Fragment I am trying to display when the problem happens:
onAttach is called (but at that time, getView is still null)
onCreateView is not called (that's expected)
onViewStateRestored is not called (why not?)
onResume is not called (I really thought it would...)
If it changes anything, I am using the support package, my activity is a SherlockFragmentActivity
EDIT (to answer Marco's comment):
The fragments are instantiated in the onCreate function of the Activity, I fill an ArrayList with those fragments:
char_tests = new ArrayList<Fragment>(Arrays.asList(
new FragmentOptionA(), new FragmentOptionB(), new FragmentOptionC()));
The I pick from that list to set fragments[1] (that's all done in the UI thread)
I fixed this by changing test_pager to extends FragmentStatePagerAdapter instead.
I am still confused as to what PagerAdapter should be used depending on the usage. The only thing I can find in the documentation says that FragmentPagerAdapter is better for smaller number of pages that would be kept in memory and FragmentPagerStateAdapter better for a larger number of pages where they would be destroyed and save memory...
When trying to do (fancy?) things with Fragments, I found FragmentStatePagerAdapter is better when pages are removed and re-inserted like in this case. And FragmentPagerAdapter is better when pages move position (see bug 37990)

Categories

Resources