android tabs with interactive views - android

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

Related

I want to add fragments to each corresponding actionbar tab at the same time

UPDATE : I don't understand why when tapping on each tab onTabSelected() doesn't show the correct fragment even though it's been added to the fragmentTransaction android.R.id.content.
I call this method before onTabSelected gets called to make sure fragments are not null.
protected void initTabs() {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if(mShowFragment3 == null) {
mShowFragment3 = EpisodeTileFragment.newInstance(getString(R.string.title_section4));
ft.add(android.R.id.content, mShowFragment3);
}
if(mShowFragment2 == null) {
mShowFragment2 = EpisodeTileFragment.newInstance(getString(R.string.title_section3));
ft.add(android.R.id.content, mShowFragment2);
}
}
#Override
public void onTabSelected(ActionBar.Tab tab,
FragmentTransaction ft) {
//this usually works, but if i try to mess with adding the tabs to the
//FragmentTransaction this won't work anymore
if(tab.getPosition() == 1) {
ft.show(mShowFragment2);
}
I have scoured the internet and this may not be possible but I wanted to give this a shot.
I currently have the app working fine, it has 4 tabs and 4 corresponding fragments. When a user opens the app onTabSelected is called and selects the first tab/fragment is added and we're all good.
A user clicks tab2 and the 2nd fragment is added and rendered. When a user clicks the 2nd tab there is an asynctask that gets data and renders this on a fragment. Etc.. this happens on tab 3 and 4 also. When a user clicks a tab the first time I instantiate the fragment and add it to the fragmentTransaction, the next time you click on the tab it's lightning fast because it's already added, and I'm hiding and showing fragments.
The question I have is , is there a way to load up all 4 tabs at the same time, vs. waiting for a user to click on a tab and then have onTabSelected firing and grabbing data etc. Please let me know if there is any questions, the code is working with no errors just not what I want, and I don't know how to instantiate all 4 fragments at the same time.
mSectionsPagerAdapter = new SectionsPagerAdapter(this.getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
LayoutInflater inflater = LayoutInflater.from(getApplicationContext());
View tabView = inflater.inflate(R.layout.tab_title, null);
TextView titleTV = (TextView) tabView.findViewById(R.id.action_custom_title);
titleTV.setText(mSectionsPagerAdapter.getPageTitle(i));
titleTV.setTypeface(Typeface.createFromAsset(getAssets(), getString(R.string.tab_font)));
titleTV.setSingleLine();
titleTV.setTextSize(13);
Tab t = actionBar.newTab()
.setText(mSectionsPagerAdapter.getPageTitle(i))
.setTabListener(this);
t.setCustomView(tabView);
actionBar.addTab(t);
}
this is for adding the tabs. but need to add the fragments for each tab.
Set the offscreen page limit to your number of tabs, and then your ViewPager will render all the fragments when the parent activity is created.
mViewPager.setOffscreenPageLimit(4);
You should also check out the Class Overview of ViewPager, which has a really good example of using a custom FragmentPagerAdapter called TabsAdapter with a ViewPager (instead of using the ADT generated SectionsPagerAdapter). I added the code from the official Android docs below:
public class ActionBarTabsPager extends Activity {
ViewPager mViewPager;
TabsAdapter mTabsAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewPager = new ViewPager(this);
mViewPager.setId(R.id.pager);
setContentView(mViewPager);
final ActionBar bar = getActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
mTabsAdapter = new TabsAdapter(this, mViewPager);
mTabsAdapter.addTab(bar.newTab().setText("Simple"),
CountingFragment.class, null);
mTabsAdapter.addTab(bar.newTab().setText("List"),
FragmentPagerSupport.ArrayListFragment.class, null);
mTabsAdapter.addTab(bar.newTab().setText("Cursor"),
CursorFragment.class, null);
if (savedInstanceState != null) {
bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
}
/**
* This is a helper class that implements the management of tabs and all
* details of connecting a ViewPager with associated TabHost. It relies on a
* trick. Normally a tab host has a simple API for supplying a View or
* Intent that each tab will show. This is not sufficient for switching
* between pages. So instead we make the content part of the tab host
* 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
* view to show as the tab content. It listens to changes in tabs, and takes
* care of switch to the correct paged in the ViewPager whenever the selected
* tab changes.
*/
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 onPageScrollStateChanged(int state) {
}
#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) {
mViewPager.setCurrentItem(i);
}
}
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
}
}

How to use SherlockFragmentActivity with SlidingMenu?

Am using SlidingMenu which uses different types of Fragment/SherlockFragment as menu.
Am in need of SherlockFragmentActivity, as I am using Tabs inside menu.
How to use SherlockFragmentActivity with SlidingMenu?
If it's not possible, is there any other library through which I can have sliding menu & tabs inside sliding menu? Am not sure if it can be achieved using android-menudrawer
Edit : want to achieve the below. ie, Tabs inside Menu
when menu button is clicked, menu Fragment is opened, inside Menufragment I want to add tabs.
   
EDIT:
I wanted to take this design as a challenge and see what is the result:
As I suggested in the comments, I used PagerSlidingTabStrip andViewPager.
I did not include the ActionBarSherlock, but if needed that will be easy to modify: the MainActivity will be required to extend from SherlockFragmentActivity, and the theme #style/Theme.Sherlock.Light added to manifest file, and that is all.
(Just to be sure you got the idea, PagerSlidingTabStrip is the one who creates the tabs at the bottom)
Here are the steps I took to integrate PagerSlidingTabStrip and ViewPager with SlidingMenu:
1 - Import SlidingMenu library into Eclipse workspace
2 - Import PagerSlidingTabStrip library into Eclipse workspace
3 - Add Android Support Library to your project (and copy the same .jar into SlidingMenu and PagerSlidingTabString libraries, otherwise eclipse might complain)
4 - A minimal example of MainActivity:
public class MainActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SlidingMenu menu = new SlidingMenu(this);
menu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
menu.setBehindOffset(50);
menu.setFadeDegree(0.35f);
menu.attachToActivity(this, SlidingMenu.SLIDING_CONTENT);
menu.setMenu(R.layout.left_menu);
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
PagerSlidingTabStrip tabs = (PagerSlidingTabStrip) findViewById(R.id.tabs);
tabs.setViewPager(pager);
}
}
5 - The layout of sliding menu, R.layout.left_menu:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/left_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#CCC"
android:orientation="vertical" xmlns:app="http://schemas.android.com/apk/res/org.grec">
<com.astuetz.viewpager.extensions.PagerSlidingTabStrip
android:id="#+id/tabs"
app:shouldExpand="true"
android:layout_width="match_parent"
android:layout_alignParentBottom="true"
android:layout_height="48dip" />
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/tabs" />
</RelativeLayout>
6 - The ViewPagerAdapter:
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
private final int PAGES = 3;
private String[] titles={"Tab 1", "Tab 2", "Tab 3"};
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new TabFragment1();
case 1:
return new TabFragment2();
case 2:
return new TabFragment3();
default:
throw new IllegalArgumentException("The item position should be less or equal to:" + PAGES);
}
}
#Override
public CharSequence getPageTitle(int position) {
return titles[position];
}
#Override
public int getCount() {
return PAGES;
}
}
7 - And an example of fragment, TabFragment1.java (the other 2 are similar):
public class TabFragment1 extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_tab_1, container, false);
}
}
8 - And the layout of the fragment R.layout.fragment_tab_1 which is pretty simple, it contains a single TextView, so I won't include it here.
I hope this example addresses your issue and will help you getting started on the right track.
Full source on github: https://github.com/vgrec/SlidingMenuWithViewpager
1) Add SherlockActionBar library to SlidingMenu
2) Change SlidingFragmentActivity extends FragmentActivity to SlidingFragmentActivity extends SherlockFragmentActivity
3) Then add SlidingMenu library to your project and your project should use MyProjectActivity extends SlidingFragmentActivity
You can use SherlockActionBar to create sliding menu with tabs inside
Here sample code to create Tab with view pager
For sliding, you can look at their documentation
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(SherlockFragmentActivity 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();
}
#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 onPageScrollStateChanged(int state) {
}
public void onTabSelected(Tab tab, FragmentTransaction ft) {
Object tag = tab.getTag();
for (int i = 0; i < mTabs.size(); i++) {
if (mTabs.get(i) == tag) {
mViewPager.setCurrentItem(i);
}
}
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
}
mViewPager = new ViewPager(this);
mViewPager.setId(R.id.pager);
setContentView(mViewPager);
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
mTabsAdapter = new TabsAdapter(this, mViewPager);
mTabsAdapter.addTab(ab.newTab().setText("tab1"), FirstFragment.class, null);
mTabsAdapter.addTab(ab.newTab().setText("tab2"), SecondFragment.class, null);
mTabsAdapter.addTab(ab.newTab().setText("tab3"), ThirdFragment.class, null);
As what you have said the SlidingMenu uses fragments and you want to to have Tabs inside a Menu. For me, using Tabs inside a Menu needs nested fragments which means that you can't use SherlockFragmentActivity since it is like the Main Activity. You can't contain an Activity inside a fragment.
I have not implemented this kind of approach but maybe you can try implementing TabListener inside a fragment.
I am using this code for my activity not the fragment,
class MainActivity extends SherlockFragmentActivity implements ActionBar.TabListener
{
}
maybe you can have,
class FragmentA extends SherlockFragment implements TabListener{
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
}

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.

Delay initialization when using Fragment in 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
}
}

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.

Categories

Resources