I have implemented a Sherlock ActionBar with navigation tabs using a tabs adapter. Below is my tab adapter class:
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);
}
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels)
{
}
public void onPageSelected(int position)
{
mActionBar.setSelectedNavigationItem(position);
}
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);
}
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft)
{
}
public void onTabReselected(Tab tab, FragmentTransaction ft)
{
}
}
My problem is how to use addToBackStack() with Fragment.instantiate()(or if there is any other way for handling back button)?
My problem is how to use addtobackstack with Fragment.instantiate or
any other way for handling back button
You really shouldn't do this. If you're going to use tabs with fragments in a ViewPager it's safe to assume the user could switch/swipe a lot the tab fragments as he uses the app. Having the BACK button recreating his steps when he wants out of the activity could be very frustrating(imagine trying to get out of this activity after switching tabs for 15-20 times).
If you really want to do that then store the user's navigation path in a list/array of integers(when swiping/switching the tabs store the int position of the ViewPager's page where the user has gone). Then override the onBackPressed method of the activity and have it pop(every time the BACK button is clicked) the last position from the previous list/array moving the ViewPager to that position(along with highlighting the proper tab).
Related
I saw this code in the documentation of view pager. I want to pass a string from my activity to a fragment class. I figured I can pass it using the bundle args. How would I access it in my fragment class? Also can someone explain why we extend Fragment Activity here?
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) {
}
}
}
We extend from FragmentActivity when we want to use Fragments for devices running on Android < 3.0. In order to use Fragments for android 2.3.3 or similar you need FragmentActivity which is in the Android support library.
Also you can access the bundle passed into the fragment in this way:-
private void LoadBundle()
{
Bundle b = getArguments();
if (b != null) {
int value=b.getInt("yourkey")
}
}
Call the LoadBundle() method anywhere in the fragment to load data passed into the fragment.
You can transfer data from Activity to Fragment like this:
FragmentA fragmentA = new FragmentA();
Bundle bundle = new Bundle();
bundle.putXXX("YourDataKey", YourDataValue);
fragmentA.setArguments(bundle);
getFragmentManager().beginTransaction().add(fragmentA);
In my opion , the reason why extends FragmentActivity is that it can then manage Fragment easily . The advatage of using Fragment is not that simple. please read the google android doc:
http://developer.android.com/guide/components/fragments.html
I want to handle one fragment within tabsadapter which is a static class in my Activity. The problem is how can i get the getfragmentbytag in the onTabSelected?
From the onCreate i have this code:
Tab tab1 = actionBar.newTab().setText("ABOUT");
tabsAdapter.addTab(tab1, PoiAboutFragment.class, null);
and my tabs adapter class is the following which works with no problem.
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>();
private Object gridTag;
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,
ActionBar actionBar) {
super(activity.getSupportFragmentManager());
mContext = activity;
mActionBar = actionBar;
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);
if(clss.equals(GridFragment.class))
gridTag = tab.getTag();
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);
}
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
public void onPageSelected(int position) {
mActionBar.setSelectedNavigationItem(position);
}
public void onPageScrollStateChanged(int state) {
}
public void onTabSelected(Tab tab, FragmentTransaction ft) {
Object tag = tab.getTag();
Log.i(TAG, "onTabSelected has been called");
if(tag.equals(gridTag))
Log.i(TAG, "gridtab has been called");
for (int i = 0; i < mTabs.size(); i++) {
if (mTabs.get(i) == tag) {
mViewPager.setCurrentItem(i);
}
}
}
the problem is that i want to handle one of my fragments in both the oncreate method and in the tabsAdapter but i don't have neither a fragment tag neither an fragment Id. How can i handle their methods from my Activity?
The FragmentPagerAdapter source's makeFragmentName methods shows that fragments are created with the following tags:
"android:switcher:" + viewId + ":" + id
Where viewId is the id of the ViewPager, i.e., use
activity.getSupportFragmentManager().findFragmentByTag(
"android:switcher:" + pager.getId() + ":0"
to get the first Fragment.
I found the answer I was looking for in this site android-er.blogasppot
I have a working state pager project using actionbarsherlock but I'm having trouble with getting the current fragment that was displayed. Is it possible to get the position of the current fragment?
Yes it is possible. If you are using a ViewPager you can do that :
_viewPager.getCurrentItem();
or that
_pageListener = new PageListener();
_viewPager.setOnPageChangeListener(_pageListener);
and keep trace of current page with something like that :
private int _currentPage;
private static class PageListener extends SimpleOnPageChangeListener{
public void onPageSelected(int position) {
Log.i(TAG, "page selected " + position);
_currentPage = position;
}
}
Or else you can easily do that with a TabAdapter like this :
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);
}
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
public void onPageSelected(int position) {
mActionBar.setSelectedNavigationItem(position);
((SherlockFragmentActivity) mContext).supportInvalidateOptionsMenu();
}
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);
}
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
}
You only need to keep the current page with one/or two of the methods. For example in onTabSelected
hope it helps you
good luck
onCreateView for my first fragment in the FragmentPagerAdapter is not very quick.
So, changing curent tab to the first has a delay.
How to disable recreate first Fragment in FragmentPagerAdapter on tab Selected?
private class TabsAdapter extends FragmentPagerAdapter implements ViewPager.OnPageChangeListener, ActionBar.TabListener {
private final Context context;
private ActionBar bar;
private final ViewPager viewPager;
private final ArrayList<TabInfo> tabs = new ArrayList<TabInfo>();
final class TabInfo {
private final Class<?> clss;
private final Bundle args;
TabInfo(Class<?> _class, Bundle _args) {
clss = _class;
args = _args;
}
}
TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) {
super(activity.getSupportFragmentManager());
context = activity;
bar = activity.getSupportActionBar();
viewPager = pager;
viewPager.setAdapter(this);
viewPager.setOnPageChangeListener(this);
}
public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
TabInfo info = new TabInfo(clss, args);
tab.setTag(info);
tab.setTabListener(this);
tabs.add(info);
bar.addTab(tab);
notifyDataSetChanged();
}
#Override
public int getCount() {
return tabs.size();
}
#Override
public Fragment getItem(int position) {
TabInfo info = tabs.get(position);
Fragment fragment = Fragment.instantiate(context, info.clss.getName(), info.args);
return fragment;
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
#Override
public void onPageScrollStateChanged(int state) {}
#Override
public void onPageSelected(int position) {
bar.setSelectedNavigationItem(position);
}
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
Object tag = tab.getTag();
for (int i = 0; i < tabs.size(); i++) {
if (tabs.get(i) == tag)
viewPager.setCurrentItem(i);
}
}
#Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {}
#Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {}
}
Right now i am stuck with the same problem.
when you are on a Tab:(n), only Tab:(n-1) and Tab:(n+1) will be alive in the memory, for memory usage optimization. Rest all Tabs will be destroyed, thats the reason why when you come back to the first Tab, its onCreateView is being called again.
Actually Tab:1's onCreateView will be called even if you click Tab:2 because its the neighbourhood Tab.
One solution i got is:
change the OffscreenPageLimit of the ViewPager. Its default value is 1
viewPager.setOffscreenPageLimit(total no of Tabs - 1);
This way all the Tabs will be alive even if its offScreen. But this is recommended only if you have decent no of Tabs(<=5)
If you have huge no.of Tabs, better use
FragmentStatePagerAdapter
I have following problem:
I have one activity in which I have two tabs which are made both as fragments using FragmentPagerAdapter
In some moment I would like to change View of one of these fragments, but I am not able to findFragmentById() or findFragmentByTag() I am unable to set id, because I am not declaring Fragment in XML, because of the FragmentPagerAdapter
And when I am trying to set tag I am getting following problem:
08-15 21:46:23.805: E/AndroidRuntime(9297): java.lang.IllegalStateException: Can't change tag of fragment Companies{416d7b78 id=0x7f04002e companies}: was companies now android:switcher:2130968622:1
My code of the pager FragmentPagerAdapter:
public class TabsAdapter extends FragmentPagerAdapter implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
private final FragmentActivity mContext;
private final ActionBar mActionBar;
private final ViewPager mViewPager;
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
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);
FragmentTransaction ft=mContext.getSupportFragmentManager().beginTransaction();
Fragment fragment=Fragment.instantiate(mContext, info.clss.getName(), info.args);
if(position==1){
ft.add(mViewPager.getId(), fragment, "companies");
}else{
ft.add(mViewPager.getId(), fragment);
}
Log.v("FRAGMENT ID", "fragment tag: "+fragment.getTag());
ft.commit();
Log.v("FRAGMENT ID", "fragment tag: "+fragment.getTag());
return fragment;
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
mActionBar.setSelectedNavigationItem(position);
new Thread(new Runnable() {
#Override
public void run() {
MainActivity.this.hideKeyboard();
}
}).start();
}
#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) {
}
}
Thank you in advance for your answers.
The Fragments supplied by the FragmentPagerAdapter are auto-tagged when they're instantiated. You can retrieve the tag with this method:
private static String makeFragmentName(int viewPagerId, int index) {
return "android:switcher:" + viewPagerId + ":" + index;
}
Reference: reusing fragments in a fragmentpageradapter
With this function you can find the fragment by pager adapter position.
public Fragment findFragmentByPosition(int position) {
FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
return getSupportFragmentManager().findFragmentByTag(
"android:switcher:" + getViewPager().getId() + ":"
+ fragmentPagerAdapter.getItemId(position));
}
Sample code for v4 support api.
I found a slightly less gross way to get a handle on a Fragment created by a FragmentPagerAdapter.
Instead of imitating the way tags are created, override instantiateItem(), get the Fragment returned by the superclass, and save the tag in TabInfo. This works for me on API >= 15, may work with lower. This still makes some assumptions about private code.
static final class TabInfo {
private final Class<?> clss;
private final Bundle args;
private String tag; // ** add this
TabInfo(Class<?> _class, Bundle _args) {
clss = _class;
args = _args;
}
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
final Fragment fragment = (Fragment) super.instantiateItem(container, position);
final TabInfo info = mTabs.get(position);
info.tag = fragment.getTag(); // set it here
return fragment;
}
Or in API < 16, I think instantiateItem() takes a View() instead of ViewGroup(), like this:
public Object instantiateItem(View container, int position);
Then allow a way to get the Fragment, keeping the hack contained.
public Fragment getFragment(int index) {
return mContext.getFragmentManager().findFragmentByTag(mTabs.get(index).tag);
}
For that to work, the declaration for mContext needs to change to Activity, it's passed in as an Activity anyway:
private final Activity mContext;
A simpler approach to this is to get the Tags directly from the Fragment Manager; like this:
fm.getFragments().get(0).getTag()
You can replace the position, depending on the fragment you need the tag for. Hope this helps others!.
It is very late to answer this question but i have searched a lot no one tell me the exact answer, Might be some one found it useful.
I have set my fragments through FragmentPagerAdapter and i did communication between fragments
https://developer.android.com/training/basics/fragments/communicating.html#Deliver
following this link and for FindFragmentById i simply used viewpager.getId() on the mainactivity .