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.
Related
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) {
}
}
}
I'm using the ViewPager example with ActionBar tabs taken from the Android documentation here.
Unfortunately, as soon as I call the addTab method, the application crashes with the following exception:
IllegalStateException: The application's PagerAdapter changed the
adapter's content without calling PagerAdapter#notifyDataSetChanged!
Expected adapter item count 0, found 1.
This is the FragmentPagerAdapter code:
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) {
}
}
}
I'm not modifying my adapter in any other part of my code and I'm calling the addTab method from the main thread, the addTab method ends with a call to notifyDataSetChanged. As the documentation recommends to do:
PagerAdapter supports data set changes. Data set changes must occur on
the main thread and must end with a call to notifyDataSetChanged()
similar to AdapterView adapters derived from BaseAdapter.
I had a hard time making my ViewPager working. At the end, it seems that the example in the documentation is wrong.
The addTab method should be as follows:
public void addTab(Tab tab, Class<?> clss, Bundle args) {
TabInfo info = new TabInfo(clss, args);
tab.setTag(info);
tab.setTabListener(this);
mTabs.add(info);
notifyDataSetChanged();
mActionBar.addTab(tab);
}
Notice the order of the last three operations. In the original example, notifyDataSetChanged was called after the mActionBar.addTab function.
Unfortunately, as soon as you call the addTab on the ActionBar, the first tab you add is automatically selected. Because of this, the onTabSelected event is fired and while trying to retrieve the page, it throws the IllegalStateException because it notices a discrepancy between the expected item count and the actual one.
I fixed it by callinig notifyDataSetChanged() once and just before next call of getCount():
private boolean doNotifyDataSetChangedOnce = false;
#Override
public int getCount() {
if (doNotifyDataSetChangedOnce) {
doNotifyDataSetChangedOnce = false;
notifyDataSetChanged();
}
return actionBar.getTabCount();
}
private void addTab(String text) {
doNotifyDataSetChangedOnce = true;
Tab tab = actionBar.newTab();
tab.setText(text);
tab.setTabListener(this);
actionBar.addTab(tab);
}
private void removeTab(int position) {
doNotifyDataSetChangedOnce = true;
actionBar.removeTabAt(position);
}
I was getting this error like you by referencing the tabs within getCount():
#Override
public int getCount() {
return mTabs.size();
}
When instantiated this should either be passed in as a separate variable, or the pager should be set up after the collection has been populated / mutated.
try this. I worked
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
System.out.println("Asyncrona onPostExecute");
/*::::::::::::::::::: Guardo los cambios ::::::::::::::*/
//menuTabs = menuTabs2;
viewPager = (ViewPager) rootView.findViewById(R.id.viewPager_menu);
costumAdapter = new CostumAdapter2(getActivity().getSupportFragmentManager());
costumAdapter.notifyDataSetChanged();
viewPager.setAdapter(costumAdapter);
tabLayout = (TabLayout) rootView.findViewById(R.id.tablayout_menu);
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
System.out.println(" La Seleccionado: " + tab.getText()+", POSICION: "+tab.getPosition());
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
});
viewPager.setAdapter(costumAdapter);
tabLayout.setupWithViewPager(viewPager);
loading.closeMessage();
}
My issue wasn't exactly the same, but similar context.
I have an AlertDialog getting shown in a layout that has ViewPager and is using AppCompatActivity. I was getting the IllegalStateException and crash when rotating the screen.
Thanks to the answers from answer #almisoft and #Zagorax, I tried calling notifyDataSetChanged(); right before I show the dialog, and the problem seems to have gone away.
I had the same problem in FragmentStatePager adapter. in onResume() When reInitialize the adpater and ViewPager, the same error IllegalStateException will be shown. The solution:
When adding tab addtab, choose the tab to be not selected by setting setSelected params to false.
actionBar.addTab(tab,false);
I think the exception appeared because of overriding the onTabSelected() method like the following
viewPager.setCurrentItem(tab.getPosition());
So when calling addTab() it didn't recognize which viewPager and adapter to add the tab to.
add this line into your adapter file where the
public Object instantiateItem(ViewGroup container, int position)
method is called
((ViewPager) container).addView(view);
return view;
Make sure the values in setOffscreenPageLimit and getCount match, otherwise you will get this error:
mViewPager.setOffscreenPageLimit(MAX_TABS);
public class SectionsPagerAdapter extends FragmentPagerAdapter {
...
#Override
public int getCount() {
return MAX_TABS;
}
Call Viewpager.setAdapter(adapter); after item add in your array list.
One more thing to be check here is the adapter for which it gives this error check source list is getting reference from other list or not.
It is possible that your adapter list is referenced from other list and in that list got update and added some items.
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
}
}
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.
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