Delay initialization when using Fragment in Android - android

When one fragment A is changed to another fragment B, it will call onCreate() and some other functions to do initialization. After the Fragment has changed, the fragments near the current fragment also will be initialized. For example: I have 5 fragments A, B, C, D, E. The current fragment is A. When I change to D, D along with C and E will be initialized. My Problem is:
Some codes are very slow to execute so I want to initialize a part of the data instead of ALL of the data. That is, when the fragment is visible, then some data will be initialized. When its not the current fragment, the initialization will be delayed. Here is a pic:
I use these classes:
android.widget.TabHost
android.support.v4.app.Fragment
android.support.v4.app.FragmentActivity
android.support.v4.app.FragmentPagerAdapter
android.support.v4.view.ViewPager
I read the API but there is no method called when the fragment is VISIBLE. So, I think I can do something with onTabChanged. But I don't know how to get the content fragment through TabHost. If I can, then I will call my own function to do initialization.
How can I know the fragment is hide or its the current one? OR
Can I get the content fragment in TabHost? OR
Other ways to delay the initialization.
Here is some code:
public class TabsAdapter extends FragmentPagerAdapter
implements TabHost.OnTabChangeListener,
ViewPager.OnPageChangeListener{
private final Context mContext;
private final TabHost mTabHost;
private final ViewPager mViewPager;
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
private static final class TabInfo {
private final Class<?> mClss;
private final Bundle mArgs;
TabInfo(String _tag, Class<?> _class, Bundle _args) {
mClss = _class;
mArgs = _args;
}
}
public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager) {
super(activity.getSupportFragmentManager());
mContext = activity;
mTabHost = tabHost;
mViewPager = pager;
mTabHost.setOnTabChangedListener(this);
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
}
/** MainActivity call this func to add tabs */
public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
some code
}
public void onPageSelected(int position) {
TabWidget widget = mTabHost.getTabWidget();
int oldFocusability = widget.getDescendantFocusability();
widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
mTabHost.setCurrentTab(position);
widget.setDescendantFocusability(oldFocusability);
Log.i("demo tab","onPageSelected tab change to--------- " + position);
}
}
MainActivity:
/** these code is to create fragment */
FrameLayout tabContact = (FrameLayout) LayoutInflater.from(this).inflate(R.layout.tab_widget, null);
ImageView imgBackContact = (ImageView) tabContact.findViewById(R.id.tab_widget_back_image);
ImageView imgFrontContact = (ImageView) tabContact.findViewById(R.id.tab_widget_front_image);
imgBackContact.setImageResource(R.drawable.ic_tab_contact_normal);
imgFrontContact.setImageResource(R.drawable.ic_tab_contact_selected);
/** these code is to put fragments into TabHost */
mTabsAdapter.addTab(mTabHost.newTabSpec(XXX), A.class, null);

My Problem Is: some codes are very slow so I want to initialize a part
of the datas instead of ALL of them.
I hope you're not doing heavy operations on the main UI thread.
How can I know the fragment is hide or its the current one? OR Can I
get the content fragment in TabHost? OR Other ways to delay the
initialization.
You could use a trick regarding the FragmentPagerAdapter. In your onPageSelected method you could find out which is the current Fragment and call an initialization method on it:
public void onPageSelected(int position) {
//...
Fragment item = getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.theIdOfTheViewPager + ":" + position);
if (item != null && item.getView() != null) {
item.initializeStuffForThisFragment();// you need to cast it to your Fragment class
}
}

Related

Dividing data amongst 3 actionbar tabs

I have an activity that has 3 actionbar sherlock tabs. This activity has a long list of data that I want divided evenly amongst these 3 tabs. The reason is since this list is long, it's easier for the user to find what he needs if the list is divided alphabetically amongst 3 tabs.
Clicking any tab shows the same list, just populated with different values, so I just wanted 1 fragment, called ListFragment that extends SherlockFragment. In the ListFragment's onCreateView I want to create the adapter using the appropriate list via getActivity().getSecondList, etc.
How can this ListFragment know which tab was selected to grab the right list from it's containing activity?
public class SherlockTabListener<T extends SherlockFragment> implements TabListener {
private SherlockFragment mFragment;
private final SherlockFragmentActivity mActivity;
private final String mTag;
private final Class<T> mClass;
private final int mfragmentContainerId;
public SherlockTabListener(int fragmentContainerId, SherlockFragmentActivity activity,
String tag, Class<T> clz) {
mActivity = activity;
mTag = tag;
mClass = clz;
mfragmentContainerId = fragmentContainerId;
}
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if(mTag.equals("AH")) {
mFragment = ListFragment.newInstance(firstList);
}
else if(mTag.equals("IQ")) {
mFragment = ListFragment.newInstance(middleList);
}
else
mFragment = ListFragment.newInstance(lastList);
ft.add(mfragmentContainerId, mFragment, mTag);
}

(Android) how to call method in fragment from fragmentActivity

My layout look like this:
my fragmentActivity use ViewPager + TabHost
there are 3 tabs in tab label
the button "save" is in my fragmentActivity
I hope I can save data in all 3 tabs by calling their save() method when I click save button
but I don't know how to call them
is there any way to call method from child fragments?
here is my code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.actreg);
this.initialiseTabHost(savedInstanceState);
intialiseViewPager();
save=(Button)findViewById(R.id.save);
save.setOnClickListener(new OnClickListener() {
.....
}
}
});
}
private void initialiseTabHost(Bundle args) {
tabHost = (TabHost)findViewById(android.R.id.tabhost);
tabHost.setup();
TabInfo tabInfo = null;
FragmentActivity.AddTab(this, this.tabHost, this.tabHost.newTabSpec("Tab1").setIndicator("Tab1"),
(tabInfo = new TabInfo("Tab1", fragment1.class, args)));
this.myHashMapTabInfo.put(tabInfo.tag, tabInfo);
......
tabHost.setOnTabChangedListener(new OnTabChangeListener() {.....
}
});
}
#Override
private static void AddTab(fragmentActivity activity, TabHost tabHost, TabHost.TabSpec tabSpec, TabInfo tabInfo) {
tabSpec.setContent(activity.new TabFactory(activity));
tabHost.addTab(tabSpec);
}
private class TabInfo {
private String tag;
private Class<?> clss;
private Bundle args;
private Fragment fragment;
TabInfo(String tag, Class<?> clazz, Bundle args) {
this.tag = tag;
this.clss = clazz;
this.args = args;
}
}
class TabFactory implements TabContentFactory {
private final Context mContext;
public TabFactory(Context context) {
mContext = context;
}
public View createTabContent(String tag) {
View v = new View(mContext);
v.setMinimumWidth(0);
v.setMinimumHeight(0);
return v;
}
}
Just put the data in global variables, define it in your Activity.
For future Googlers-
Call an activity method from a fragment
From fragment to activty:
((YourActivityClassName)getActivity()).yourPublicMethod();
From activity to fragment:
FragmentManager fm = getSupportFragmentManager();
//if you added fragment via layout xml
YourFragmentClass fragment = (YourFragmentClass)fm.findFragmentById(R.id.your_fragment_id);
fragment.yourPublicMethod();
If you added fragment via code and used a tag string when you added your fragment, use findFragmentByTag instead:
YourFragmentClass fragment = (YourFragmentClass)fm.findFragmentByTag("yourTag");
create a public method that contains the setOnClickListener of the button.
This method, you can call from the Fragment to this way:
((YourActivityClassName)getActivity()).yourPublicMethod();
You have saved all data in myHashMapTabInfo. so you just need to write a method to save myHashMapTabInfo in save button.
save.setOnClickListener(new OnClickListener() {
saveInfo(myHashMapTabInfo);
};

EditText field displaying data on different tabs

I'm having some pretty strange issues with my app. I have an EditText element, lets call it display, in a reused fragment for each tab. Every time I launch the app I see the following initial behaviour when I click a button that sends text to the output display. I do not use the system keyboard, only my buttons send to the display.
The best way to think of this is like a web browser with tabs, you can edit the URL in each tab independently, and the buttons, that live below in the other two sections, supply the input. It's not a web browser, but the analogy works.
Don't click on anything -> Text drawn in B tab
Click on B -> Text drawn in C tab
Click on C -> Text drawn in C tab
Click on B -> Click on C -> App Crashes
I might be running into a fencepost error but I'm not sure. I can get text to draw in A's output but only after a click or two.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.container);
midPager = (ViewPager) findViewById(R.id.function_container);
topPager = (ViewPager) findViewById(R.id.top_container);
midPagerAdapter = new midPageAdapter(getFragmentManager());
midPager.setAdapter(midPagerAdapter);
final ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
TabsAdapter topTabsAdapter = new TabsAdapter(this,topPager);
topTabsAdapter.addTab(actionBar.newTab().setText("1"),
TopFragment.class, null);
topTabsAdapter.addTab(actionBar.newTab().setText("2"),
TopFragment.class, null);
topTabsAdapter.addTab(actionBar.newTab().setText("3"),
TopFragment.class, null);
if (savedInstanceState != null) {
actionBar.setSelectedNavigationItem(savedInstanceState.getInt("tab",0));
}
}
And in my function that manages the tabs this is what I have:
public static class TabsAdapter extends FragmentPagerAdapter
implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
private final Context mContext;
private final ActionBar mActionBar;
private final ViewPager mViewPager;
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
static final class TabInfo {
private final Class<?> clss;
private final Bundle args;
TabInfo(Class<?> _class, Bundle _args) {
clss = _class;
args = _args;
}
}
public TabsAdapter(Activity activity, ViewPager pager) {
super(activity.getFragmentManager());
mContext = activity;
mActionBar = activity.getActionBar();
mViewPager = pager;
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
}
public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
TabInfo info = new TabInfo(clss, args);
tab.setTag(info);
tab.setTabListener(this);
mTabs.add(info);
mActionBar.addTab(tab);
notifyDataSetChanged();
}
#Override
public int getCount() {
return mTabs.size();
}
#Override
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
return Fragment.instantiate(mContext, info.clss.getName(), info.args);
}
#Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
mActionBar.setSelectedNavigationItem(position);
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
Object tag = tab.getTag();
for (int i = 0; i < mTabs.size(); i++)
{
if (mTabs.get(i) == tag)
{
setActiveMap(i);
mViewPager.setCurrentItem(i);
}
}
}
}
In my reusable fragment I have this for my OnCreate function.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.top_fragment, container, false);
Foobar active = Container.getActiveMap();
active.editLine = (EditText) v.findViewById(R.id.display);
active.editLine.setCursorVisible(false);
active.editLine.addTextChangedListener((TextWatcher) this);
((EditText) v.findViewById(R.id.display)).setOnClickListener(this);
return v;
}
One question that would help me debug this is how can I debug EditText within a reused fragment in tabs? What data does an EditText widget use to distinguish itself from tab to tab? I've tried various .tostring()'s of functions without success.
The more ideal solution is a simple error you spot in this code. Thanks for your help.
We could have 3 separate fragments but they all are of the same configuration however that seems unnecessarily ugly.

Implementing Tab Navigation using the ActionBar and a Viewpager

I have 3 tabs, of which the 1st one has actual data(a listview) and rest 2 are empty. I was just trying to implement tab navigation.
in Activity's onCreate i have:
mViewPager = new ViewPager(this);
mViewPager.setId(R.id.pager);
setContentView(mViewPager);
actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
PagerAdapter adapter = new PagerAdapter((SlidingFragmentActivity) this,
mViewPager);
adapter.addTab(actionBar.newTab().setText("Tab-1"),
Fragment1.class, null);
adapter.addTab(actionBar.newTab().setText("Tab-2"),
Fragment2.class, null);
adapter.addTab(actionBar.newTab().setText("Tab-3"),
Fragment2.class, null);
and the PagerAdapter is:
public static class PagerAdapter extends FragmentPagerAdapter implements
ActionBar.TabListener, ViewPager.OnPageChangeListener {
private final Context mContext;
private final ActionBar mActionBar;
private final ViewPager mViewPager;
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
static final class TabInfo {
private final Class<?> clss;
private final Bundle args;
TabInfo(Class<?> _class, Bundle _args) {
clss = _class;
args = _args;
}
}
public PagerAdapter(SlidingFragmentActivity activity, ViewPager pager) {
super(activity.getSupportFragmentManager());
mContext = activity;
mActionBar = activity.getSupportActionBar();
mViewPager = pager;
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
}
public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
TabInfo info = new TabInfo(clss, args);
tab.setTag(info);
tab.setTabListener(this);
mTabs.add(info);
mActionBar.addTab(tab);
notifyDataSetChanged();
}
public int getCount() {
return mTabs.size();
}
public SherlockFragment getItem(int position) {
TabInfo info = mTabs.get(position);
return (SherlockFragment) Fragment.instantiate(mContext,
info.clss.getName(), info.args);
}
public void onPageSelected(int position) {
mActionBar.setSelectedNavigationItem(position);
}
public void onPageScrollStateChanged(int state) {
}
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
public void onTabSelected(Tab tab, FragmentTransaction ft) {
mViewPager.setCurrentItem(tab.getPosition());
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
}
Here Fragment1 has a listview which gets data from a web server using an AsyncTask.
Fragment2 is just an empty fragment
public class Fragment2 extends SherlockFragment {
}
Problem:
when i select Tab-2, then Tab-3 and comeback to Tab-2, Fragment1's onCreateView is being called. Though the Tab-2 shows white screen, Fragment1's onCreateView is being called, i checked using logcat.
Didnt i implement PagerAdapter the right way?
There is nothing wrong with this behaviour.
The ViewPager tries to minimize the memory impact that the fragments have. If you use a FragmentPagerAdapter the view of your fragment can be destroyed once it leaves the screen.
Typically the ViewPager will hold the view to the left and the right of the current view in memory to enable fast tab switching.
If you have only say 5 Tabs and you want to have all the views of this tabs in memory you can modify the number of views that are kept with ViewPager.setOffscreenPageLimit. This method lets you specify how many views are kept for each side. If you have only 5 tabs you could set 4 as offScreenPageLimit to have each view kept in memory even if the user is at one of the outer tabs.
Keep in mind that the views are destroyed for a reason. This is a performance optimization and even if offScreenPageLimit of 10 may work on your device it can crash devices with fewer memory. Set the limit to an reasonable number and implement the onDestroyView and oncreateView methods of your fragments correct.

android tabs with interactive views

I am trying to avoid using intents and activities within tabhost and tabwidget
This code is starting to get messy just using views for everything.
My problem is that I am having difficulty retaining information. In one tab I have one view which has 4 buttons, each button loads another view and this takes up the visual information on screen in that tab. The problem is that when I go "back" to load the previous view in that tab, none of the information is that view is retained, even the button listeners won't re-instantiate.
How do I approach this? I have seen some very rudimentary examples of views within tabs, but nothing interactive that loads more views.
(viewflippers and action bars are not an option, and I am trying to avoid using tabs with activities)
Forget Activity Group. Forget Tab Host. It's all about ActionBar Tabs and ViewPager Fragments now. The API Demos sample app (which is also the Android Compatibility Library sample app) provides an implementation that combines both Tab and ViewPager navigation between Fragments, but it sort of fakes the Tabs (i.e., they are not true ActionBar tabs). See FragmentTabsPager.java. You can take this and make it work with true ActionBar tabs, too.
Here's the interesting bit. (I deleted a bunch of stuff, so don't look for a complete working solution here.)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_tabs_pager);
mViewPager = (ViewPager)findViewById(R.id.pager);
// This block thanks to https://stackoverflow.com/q/9790279/517561
ActionBar bar = getSupportActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.setDisplayShowTitleEnabled(true);
bar.setDisplayShowHomeEnabled(true);
//
mTabsAdapter = new TabsAdapter(this, mViewPager);
mTabsAdapter.addTab("simple", "Simple",
FragmentStackSupport.CountingFragment.class, null);
mTabsAdapter.addTab("contacts", "Contacts",
LoaderCursorSupport.CursorLoaderListFragment.class, null);
mTabsAdapter.addTab("custom", "Custom",
LoaderCustomSupport.AppListFragment.class, null);
mTabsAdapter.addTab("throttle", "Throttle",
LoaderThrottleSupport.ThrottledLoaderListFragment.class, null);
}
public static class TabsAdapter extends FragmentPagerAdapter
implements ViewPager.OnPageChangeListener, ActionBar.TabListener {
private final SherlockFragmentActivity mContext;
private final ViewPager mViewPager;
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
static final class TabInfo {
#SuppressWarnings("unused")
private final String tag;
private final Class<?> clss;
private final Bundle args;
TabInfo(String _tag, Class<?> _class, Bundle _args) {
tag = _tag;
clss = _class;
args = _args;
}
}
static class DummyTabFactory implements TabHost.TabContentFactory {
private final Context mContext;
public DummyTabFactory(Context context) {
mContext = context;
}
#Override
public View createTabContent(String tag) {
View v = new View(mContext);
v.setMinimumWidth(0);
v.setMinimumHeight(0);
return v;
}
}
public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) {
super(activity.getSupportFragmentManager());
mContext = activity;
mViewPager = pager;
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
}
public void addTab(String tag, CharSequence label, Class<?> clss, Bundle args) {
ActionBar.Tab tab = mContext.getSupportActionBar().newTab();
tab.setText(label);
tab.setTabListener(this);
mContext.getSupportActionBar().addTab(tab);
TabInfo info = new TabInfo(tag, clss, args);
mTabs.add(info);
notifyDataSetChanged();
}
#Override
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
return Fragment.instantiate(mContext, info.clss.getName(), info.args);
}
#Override
public void onPageSelected(int position) {
mContext.getSupportActionBar().setSelectedNavigationItem(position);
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
mViewPager.setCurrentItem(mContext.getSupportActionBar().getSelectedNavigationIndex());
}
}
My complete solution: http://code.google.com/p/sherlock-demo/
Note: Requires ActionBarSherlock.
Note: Thanks to ActionBarSherlock and FragmentTabsPager
User Activity Group for tabs and put any beautiful layout you create for tab or activities.
Here we go: http://ericharlow.blogspot.com/2010/09/experience-multiple-android-activities.html

Categories

Resources