Simple tabs in android - android

How to implement in android simple tabs without any Intents or Views(like in TabHost)?
I need only tab headers and want to listen how tab index changes.
Or advise me how to implement multiposition switches.
UPD: I use api v14 and ActionBar tabs already used for another purpose.

The simplest solution (in my opinion, of course) would be to simply use three different ImageViews or Buttons to mimick "tabs." You can swap out an "unselected tab" image for a "selected tab" image as necessary.
As far as I am aware, there is no simple way outside of TabHosts and other such solutions requiring Activities, Fragments, or Views for the different tabs.

here's an example
suppose se01,se02 and se03 are three buttons in a horizontal LinearLayout or ScrollView(if you want to support more tabs). So, you could set onClickListeners on each of the Buttons and program them to switch to a specific background (which could show the tab is selected) on click. That's the simplest way to do it. Here's a code snippet, which is quite easy to understand. Hope this helps :-)
//initialize the first button to be 'selected' when the activity is started by a background which is similar to the other buttons but shows the selected button/tab as highlighted.
se01.setBackgroundResource(R.drawable.popup_full_dark2);
lastClicked = (Button) findViewById(R.id.ph_s01);//this keeps a track of the last tab/button pressed
toSe01();// a function which performs the changes to the view with respect to the tab selected
//Listeners
se01.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (v.getId() != lastClicked.getId()) {
lastClicked
.setBackgroundResource(R.drawable.popup_bottom_dark);//changes the background to show the tab is selected
v.setBackgroundResource(R.drawable.popup_full_dark2);
lastClicked = se01;
s.startAnimation(new Animation9());
toSe01();
s.scrollTo(0, 0);
}
}
});
se02.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (v.getId() != lastClicked.getId()) {
lastClicked
.setBackgroundResource(R.drawable.popup_bottom_dark);
v.setBackgroundResource(R.drawable.popup_full_dark2);
lastClicked = se02;
s.startAnimation(new Animation9());
toSe02();
s.scrollTo(0, 0);
}
}
});
se03.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (v.getId() != lastClicked.getId()) {
lastClicked
.setBackgroundResource(R.drawable.popup_bottom_dark);
v.setBackgroundResource(R.drawable.popup_full_dark2);
lastClicked = se03;
s.startAnimation(new Animation9());
toSe03();
s.scrollTo(0, 0);
}
}
});

I had a similar project a while back where I just needed to listen for tab changes. Here is a great example from Google:
public class MyPagerAdapter 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 class TabInfo {
#SuppressWarnings("unused")
private final String tag;
private final Class<?> clss;
private final Bundle args;
public TabInfo(String _tag, Class<?> _clss, Bundle _args) {
tag = _tag;
clss = _clss;
args = _args;
}
}
private class DummyFactory implements TabHost.TabContentFactory {
private final Context mContext;
public DummyFactory(Context context) {
mContext = context;
}
public View createTabContent(String tag) {
View v = new View(mContext);
v.setMinimumHeight(0);
v.setMinimumWidth(0);
return v;
}
}
public MyPagerAdapter(FragmentActivity activity, TabHost tabHost, ViewPager viewPager) {
super(activity.getSupportFragmentManager());
mContext = activity;
mTabHost = tabHost;
mViewPager = viewPager;
mTabHost.setOnTabChangedListener(this);
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
}
public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
tabSpec.setContent(new DummyFactory(mContext));
String tag = tabSpec.getTag();
TabInfo tab = new TabInfo(tag, clss, args);
mTabs.add(tab);
mTabHost.addTab(tabSpec);
this.notifyDataSetChanged();
}
#Override
public Fragment getItem(int i) {
TabInfo tab = mTabs.get(i);
Fragment fragment = Fragment.instantiate(mContext, tab.clss.getName(), tab.args);
Log.d("DEBUG", "getItem from view pager called returning hash: " + fragment.hashCode());
return fragment;
}
#Override
public int getCount() {
return mTabs.size();
}
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
public void onPageSelected(int position) {
// Unfortunately when TabHost changes the current tab, it kindly
// also takes care of putting focus on it when not in touch mode.
// The jerk.
// This hack tries to prevent this from pulling focus out of our
// ViewPager.
TabWidget widget = mTabHost.getTabWidget();
int oldFocusability = widget.getDescendantFocusability();
widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
mTabHost.setCurrentTab(position);
widget.setDescendantFocusability(oldFocusability);
}
public void onTabChanged(String tabId) {
int position = mTabHost.getCurrentTab();
mViewPager.setCurrentItem(position);
}
}
The relevant parts here is that the adapter implements TabHost.OnTabChangeListener and in the body of the constructor, the onTabChangedListener is set accordingly (mTabHost.setOnTabChangedListener(this))
You will also notice the inner class DummyFactory
private class DummyFactory implements TabHost.TabContentFactory {
private final Context mContext;
public DummyFactory(Context context) {
mContext = context;
}
public View createTabContent(String tag) {
View v = new View(mContext);
v.setMinimumHeight(0);
v.setMinimumWidth(0);
return v;
}
}
Which is used in addTab to create a content view with height and width of 0, which allows you to add your actual content under the TabHost in your layout.

Related

force ViewPager to call getItem when going up the backstack

I'm trying to create an Android application which contains a single activity with a container and a navigation drawer. The initialy empty container loads fragments which has a ViewPager inside a tab layout in which I load a frgment with a FragmentTransaction:
public static void replaceFragmentInContainer(FragmentManager fragmentManager, Fragment fragmentToShow,
boolean addToBackStack)
{
FragmentTransaction transaction = fragmentManager.beginTransaction();
if (addToBackStack)
{
transaction.addToBackStack(null);
}
transaction.replace(R.id.container, fragmentToShow);
transaction.commit();
}
The problem I'm facing is with the backstack. When a fragment is added to the backstack and I press the back button, the app does go back to the previous fragment like I want to and I do see the tabs layout itself, but the content of the tabs is empty like there was nothing loaded to that tab. When it happens, I manage to reload the tab's content only when choosing that screen again with the navigational drawer.
After debugging I saw that the pager adapter's getItem method is not getting called when pressing the back button. The adapter itself is FragmentStatePagerAdapter.
I tried overriding the getItemPosition method:
public int getItemPosition(Object object)
{
return POSITION_NONE;
}
but that method wasn't called either when pressing the back button so I end up seeing empty tabs.
And this is the tabs adapter which is also the ViewPager adapter:
public static class TabsAdapter extends FragmentStatePagerAdapter 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>();
public TabsAdapter(final FragmentActivity activity, final TabHost tabHost, final ViewPager pager)
{
super(activity.getSupportFragmentManager());
mContext = activity;
mTabHost = tabHost;
mViewPager = pager;
mTabHost.setOnTabChangedListener(this);
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
}
public ViewPager getViewPager()
{
return mViewPager;
}
static final class TabInfo
{
private final String tag;
private final Class<?> clss;
private final Bundle args;
TabInfo(final String _tag, final Class<?> _class, final Bundle _args)
{
tag = _tag;
clss = _class;
args = _args;
}
}
static class TabFactory implements TabHost.TabContentFactory
{
private final Context mContext;
public TabFactory(final Context context)
{
mContext = context;
}
#Override
public View createTabContent(final String tag)
{
final View v = new View(mContext);
v.setMinimumWidth(0);
v.setMinimumHeight(0);
return v;
}
}
public void addTab(final TabHost.TabSpec tabSpec, final Class<?> clss, final Bundle args)
{
tabSpec.setContent(new TabFactory(mContext));
final String tag = tabSpec.getTag();
final TabInfo info = new TabInfo(tag, clss, args);
mTabs.add(info);
mTabHost.addTab(tabSpec);
notifyDataSetChanged();
}
#Override
public int getCount()
{
return mTabs.size();
}
#Override
public Fragment getItem(final int position)
{
final TabInfo info = mTabs.get(position);
return Fragment.instantiate(mContext, info.clss.getName(), info.args);
}
// #Override
// public Object instantiateItem(ViewGroup container, int position)
// {
//
// // Inflate a new layout from the resources.
// View view = ((Activity) mContext).getLayoutInflater().inflate(
// R.layout.fragment_single_conversation, container, false);
// // Add the newly created View to the ViewPager
// container.addView(view);
//
// // Populate the GUI and views now
// return view;
// }
#Override
public int getItemPosition(Object object)
{
return POSITION_NONE;
}
#Override
public void onTabChanged(final String tabId)
{
final int position = mTabHost.getCurrentTab();
mViewPager.setCurrentItem(position);
}
#Override
public void onPageScrolled(final int position, final float positionOffset,
final int positionOffsetPixels)
{
}
#Override
public void onPageSelected(final int position)
{
// Unfortunately when TabHost changes the current tab, it kindly
// also takes care of putting focus on it when not in touch mode.
// The jerk.
// This hack tries to prevent this from pulling focus out of our
// ViewPager.
final TabWidget widget = mTabHost.getTabWidget();
final int oldFocusability = widget.getDescendantFocusability();
widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
mTabHost.setCurrentTab(position);
widget.setDescendantFocusability(oldFocusability);
}
#Override
public void onPageScrollStateChanged(final int state)
{
}
}
How can this issue be solved?

implementing tabs with simple views and fragments

After one week struggle with common ways of implementing tabs like TabActivity and fragments and viewPager I failed to get a instance of Activities or fragments of tabs and couldn't find a way to solve the issue. So I decided to implement it in a different way. First I make a tabWidget with a simple button. In android developer website I find out a way to replace fragment in runtime. so the only thing remains is that how to get access to tab fragments to invoke methods from my FragmentActivity.
here is my FragmentActivity using pageViewer. I get nullpointer when I want to get fragment object of my tab in setup() method:
public class MainActivity extends FragmentActivity implements
OnTabChangeListener, OnPageChangeListener {
private TabHost mTabHost;
private ViewPager mViewPager;
private HashMap<String, TabInfo> mapTabInfo = new HashMap<String, MainActivity.TabInfo>();
private PagerAdapter mPagerAdapter;
private TabInfo mLastTab = null;
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;
/**
* #param context
*/
public TabFactory(Context context) {
mContext = context;
}
/**
* (non-Javadoc)
*
* #see android.widget.TabHost.TabContentFactory#createTabContent(java.lang.String)
*/
public View createTabContent(String tag) {
View v = new View(mContext);
v.setMinimumWidth(0);
v.setMinimumHeight(0);
return v;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
Log.d("checking", "setup tabs...");
setContentView(R.layout.activity_main);
// //
initialiseTabHost(savedInstanceState);
if (savedInstanceState != null) {
mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab"));
}
intialiseViewPager();
// //
setup();
// ///
}
protected void onSaveInstanceState(Bundle outState) {
outState.putString("tab", mTabHost.getCurrentTabTag()); // save the tab
// selected
super.onSaveInstanceState(outState);
}
private void intialiseViewPager() {
List<Fragment> fragments = new Vector<Fragment>();
fragments
.add(Fragment.instantiate(this, CoachFragment.class.getName()));
fragments
.add(Fragment.instantiate(this, LogingFragment.class.getName()));
fragments.add(Fragment.instantiate(this,
HistoryFragment.class.getName()));
this.mPagerAdapter = new PagerAdapter(
super.getSupportFragmentManager(), fragments);
//
this.mViewPager = (ViewPager) super.findViewById(R.id.viewpager);
this.mViewPager.setAdapter(this.mPagerAdapter);
this.mViewPager.setOnPageChangeListener(this);
mViewPager.setOffscreenPageLimit(1000000);
}
private void initialiseTabHost(Bundle args) {
mTabHost = (TabHost) findViewById(android.R.id.tabhost);
mTabHost.setup();
mTabHost.getTabWidget().setDividerDrawable(R.drawable.tab_divider);
TabInfo tabInfo = null;
View tabView1 = createTabView(this, coach");
MainActivity.AddTab(this, this.mTabHost, this.mTabHost
.newTabSpec("Tab1").setIndicator(tabView1),
(tabInfo = new TabInfo("Tab1", CoachFragment.class, args)));
this.mapTabInfo.put(tabInfo.tag, tabInfo);
View tabView2 = createTabView(this, logbook");
MainActivity.AddTab(this, this.mTabHost, this.mTabHost
.newTabSpec("Tab2").setIndicator(tabView2),
(tabInfo = new TabInfo("Tab2", LogingFragment.class, args)));
this.mapTabInfo.put(tabInfo.tag, tabInfo);
View tabView3 = createTabView(this, "history");
MainActivity.AddTab(this, this.mTabHost, this.mTabHost
.newTabSpec("Tab3").setIndicator(tabView3),
(tabInfo = new TabInfo("Tab3", HistoryFragment.class, args)));
this.mapTabInfo.put(tabInfo.tag, tabInfo);
mTabHost.setOnTabChangedListener(this);
}
private static void AddTab(MainActivity activity, TabHost tabHost,
TabHost.TabSpec tabSpec, TabInfo tabInfo) {
// Attach a Tab view factory to the spec
tabSpec.setContent(activity.new TabFactory(activity));
tabHost.addTab(tabSpec);
}
public void setup() {
....
CoachFragment fragment=(CoachFragment) mPagerAdapter.getRegisteredFragment(mViewPager.getCurrentItem());
fragment.setTempView(R.id.welcome);
fragment.animate();
....
}
public void onTabChanged(String tag) {
// TabInfo newTab = this.mapTabInfo.get(tag);
int pos = this.mTabHost.getCurrentTab();
this.mViewPager.setCurrentItem(pos);
}
private static View createTabView(final Context context, final String text) {
View view = LayoutInflater.from(context)
.inflate(R.layout.tabs_bg, null);
TextView tv = (TextView) view.findViewById(R.id.tabsText);
tv.setText(text);
return view;
}
#Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
#Override
public void onPageSelected(int position) {
// TODO Auto-generated method stub
this.mTabHost.setCurrentTab(position);
}
}
my pageAdapter:
public class PagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
private HashMap<Integer, Fragment> registeredFragments=new HashMap<Integer, Fragment>();
/**
* #param fm
* #param fragments
*/
public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
/* (non-Javadoc)
* #see android.support.v4.app.FragmentPagerAdapter#getItem(int)
*/
#Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
/* (non-Javadoc)
* #see android.support.v4.view.PagerAdapter#getCount()
*/
#Override
public int getCount() {
return this.fragments.size();
}
}
and my LogingFragment. the other 2 fragments are exactly the same one:
public class LogingFragment extends Fragment{
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (container == null) {
// We have different layouts, and in one of them this
// fragment's containing frame doesn't exist. The fragment
// may still be created from its saved state, but there is
// no reason to try to create its view hierarchy because it
// won't be displayed. Note this is not needed -- we could
// just run the code below, where we would create and return
// the view hierarchy; it would just never be used.
return null;
}
return (LinearLayout)inflater.inflate(R.layout.coach_activity, container, false);
}
}
Every Thing works perfect and all looking and swiping works fine but in setup() method I get nullPointer. please help me with it.
I strongly recommend running and testing the sample Effective Navigation which uses Tabs and ViewPager
http://developer.android.com/training/implementing-navigation/index.html
Finally I figured it out what is the cause of nullpointer. pager adapter should be like this:
public class PagerAdapter extends FragmentPagerAdapter {
Fragment screens[];
public PagerAdapter(FragmentManager fm, MainActivity context) {
super(fm);
screens = new Fragment[3];
screens[0] = Fragment.instantiate(context,CoachFragment.class.getName() );
screens[1] = Fragment.instantiate(context,LogingFragment.class.getName() );
screens[2] = Fragment.instantiate(context,HistoryFragment.class.getName() );
}
#Override
public Fragment getItem(int index) {
if(index <= screens.length)
{
return screens[index];
}
return null;
}
#Override
public int getCount() {
return screens.length;
}
}
so instantiating view pager changes like below:
private void intialiseViewPager() {
this.mPagerAdapter = new PagerAdapter(super.getSupportFragmentManager(),this);
//
this.mViewPager = (ViewPager) super.findViewById(R.id.viewpager);
this.mViewPager.setAdapter(this.mPagerAdapter);
this.mViewPager.setOnPageChangeListener(this);
//
this.mViewPager = (ViewPager) super.findViewById(R.id.viewpager);
this.mViewPager.setAdapter(this.mPagerAdapter);
this.mViewPager.setOnPageChangeListener(this);
}
Thanks to #Bruno Mateus
It seems to me like you are new to android, so let me point you in the right direction (if you are not new to android, my apologies):
If you want to use tabs in your app, the newest and best way to do this is to use Action Bar tabs.
This only involves editing your menu.xml file and adding an onCreateOptionsMenu() method to your main activity.
Also, to answer your question,
so the only thing remains is that how to get access to tab fragments to invoke methods from my FragmentActivity
You shouldn't use your fragments to invoke methods from your fragment activity.
Again, I will point you in the right direction with this video, "Programming Android with fragments". It is a little long, but it will explain how to use fragments.
Use the video's example of fragments and combine it with example code from the action bar tabs page.
Best of luck
**Quick edit to clarify: You should use action bar tabs because that is the newest and easiest way of using tabs. It is the new 'best practice'. TabWidget and TabActivity are older, more complicated, and might even be deprecated for all I know.
Try GrilledUI library. It will do all the dirty work for you, even load tab list from XML.

Android tabs adapter shows nulled out fragments depending on which tab I'm in

I implemented a ActionBarSherlock with a ViewPager and a TabsAdapter. It works fine until I try to communicate between fragments.
I've 3 Tabs in my Application, and I can click on each of the tabs no problem, but when communicating through an interface, in two out of three tabs, one of my fragments in my tab is null. This happens when I select a menu item. I want selecting a menu item to be communicated to all fragments in the ViewPager. However, when I'm in tab[0], tab[2] is null but tabs[0] and tab[1] are not null. When I'm in tab[2], tab[0] is null, but tab[1] and tab[2] are not null. However, when I'm in tab[1], no fragments are null.
All the fragments are visible when I click on each of the tabs. That's not a problem.
The Code:
public class GPSTrackingActivity extends SherlockFragmentActivity implements DistanceFragment.OnCoordinatesAddedListener, ReportsFragment.ReportStartDateListener
{
long insertedID = 0;
private Menu menu;
//for shared preferences
private static final String KEY_UNITS = "units";
private static final String KEY_START_POSITION = "start";
ViewPager mViewPager;
TabsAdapter mTabsAdapter;
String TAG = "GPSTrackingActivity";
//set 0 for miles, 1 for kilometers
int mMilesOrKilometers = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);
//create a new ViewPager and set to the pager we have created in Ids.xml
mViewPager = new ViewPager(this);
mViewPager.setId(R.id.pager);
setContentView(mViewPager);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowHomeEnabled(false);
actionBar.setNavigationMode ( ActionBar . NAVIGATION_MODE_TABS );
//if user has previous settings, get them from shared prefs.
getSharedPrefs();
mTabsAdapter = new TabsAdapter(this, mViewPager);
mTabsAdapter.addTab(actionBar.newTab().setText(" Track").setIcon(R.drawable.browser_compass_icon),
DistanceFragment.class, null);
mTabsAdapter.addTab(actionBar.newTab().setText(" Trips").setIcon(R.drawable.folder_chart_icon),
TripsFragment.class, null);
mTabsAdapter.addTab(actionBar.newTab().setText(" Report").setIcon(R.drawable.mail_compose_icon),
ReportsFragment.class, null);
}
/* set the units of measurement for all the fragments
*/
public void ChangeUnitsOfMeasure() {
try {
DistanceFragment DistanceFrag = (DistanceFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":0");
if (DistanceFrag != null && DistanceFrag.getView() != null) {
Log.d(TAG,"Class=" + DistanceFrag.getClass());
Log.d(TAG,"Found the Distance Fragment");
DistanceFrag.ClearData();
DistanceFrag.setMileOrKilometers(mMilesOrKilometers);
}
TripsFragment TripsFrag = (TripsFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":1");
if (TripsFrag != null && TripsFrag.getView() != null) {
Log.d(TAG,"Class=" + TripsFrag.getClass());
if (TripsFrag.getClass() == TripsFragment.class) {
Log.d(TAG,"Found the Trips Fragment");
TripsFrag.setMileOrKilometers(mMilesOrKilometers);
}
}
ReportsFragment ReportsFrag = (ReportsFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":2");
if (ReportsFrag != null && ReportsFrag.getView() != null) {
Log.d(TAG,"Class=" + ReportsFrag.getClass());
Log.d(TAG,"Found the Reports Fragment");
ReportsFrag.setMileOrKilometers(mMilesOrKilometers);
}
}
//in case we change the getCurrentItem() value to anything other than 1
//would expect a ClassCastException
catch (Exception e) {
Log.d(TAG,String.valueOf(e));
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
//check selected menu item
switch (item.getItemId()) {
case R.id.miles:
mMilesOrKilometers = 0;
ChangeUnitsOfMeasure();
return true;
case R.id.kilometers:
mMilesOrKilometers = 1;
ChangeUnitsOfMeasure();
return true;
//quit program
case R.id.menu_quit:
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
//called from ReportsFragment
public void getCurrentIdOfFragment() {
int mCurrentItem = mViewPager.getCurrentItem();
Log.d(TAG,"Current View Page=" + String.valueOf(mCurrentItem));
}
// create TabsAdapter to create tabs and behavior
public 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>();
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 void onPageScrollStateChanged(int state) {
// TODO Auto-generated method stub
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// TODO Auto-generated method stub
}
#Override
public void onPageSelected(int position) {
// TODO Auto-generated method stub
mActionBar.setSelectedNavigationItem(position);
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
#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) {
// TODO Auto-generated method stub
}
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
//Fragment mFragment = Fragment.instantiate(mContext, info.clss.getName(), info.args);
return (Fragment) Fragment.instantiate(mContext, info.clss.getName(), info.args);
}
#Override
public int getCount() {
return mTabs.size();
}
}
}
It's all happening when I call the ChangeUnitsOfMeasure() function due to menu items being selected. I know the fragments are null because I test for the fragments being null before calling a function in the fragments. My LogCat (see code) is reporting showing only fragments[0] and [1] or fragments[1] and [2] or fragments [0], [1] and [2] being found, depending on what tab I am in.
Really Weird behavior!
I found the answer. It has to do with the ViewPager.setOffscreenPageLimit(), which is set by default to 1. Increasing the limit to 2 allowed me to access all fragments at one time.
mViewPager.setOffscreenPageLimit(2);
I found the answer here: My fragments in viewpager tab dont refresh.

Android, How to mix ActionBar.Tab + View Pager + ListFragment

I've been trying to test how to mix all these things together and I'm having problems!!
I just want an app with three tabs using the ActionBar.Tab. For example, this tabs can be movies genres Action, Adventure and Animation, the user can swipe through the tabs, so it will use the ViewPager and each tab will show a list of movies of that genre. There's no need to have three different fragments classes because all tabs will be the same format a simple list.
And I'm having problems because when I select the second tab, the position for onPageSelected is 1,
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mCollectionPagerAdapter);
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
mActionBar.setSelectedNavigationItem(position);
}
});
This causes the call to the method public Fragment getItem(int i) inside the CollectionPagerAdapter class, but then the value of i is 2 NOT 1, so then it calls the createView for the TabFragment class with a value of 2 NOT 1, so tabs are not refreshing successfully.
Any help will be really appreciated!!
Code to create the tabs,
// For each of the sections in the app, add a tab to the action bar.
for (int i = 0; i < mCollectionPagerAdapter.getCount(); i++) {
mActionBar.addTab(mActionBar.newTab()
.setText(mGenres.get(i).getName())
.setTabListener(this));
//Let's request the movies for the first three genres
new GetMoviesByGenre().execute(mGenres.get(i).getId());
}
When a tab is selected,
#Override
public void onTabSelected(ActionBar.Tab tab, android.app.FragmentTransaction arg1) {
//Let's update the dataset for the selected genre
TabFragment fragment =
(TabFragment) getSupportFragmentManager().findFragmentByTag(
"android:switcher:"+R.id.pager+":"+tab.getPosition());
if(fragment != null) // could be null if not instantiated yet
{
if(fragment.getView() != null)
{
fragment.updateDisplay(tab.getPosition()); // do what updates are required
}
}
mViewPager.setCurrentItem(tab.getPosition());
}
CollectionPageAdapter class
public class CollectionPagerAdapter extends FragmentPagerAdapter {
final int NUM_ITEMS = 3; // number of tabs
List<Fragment> fragments = new ArrayList<Fragment>();
public Fragment getItem(int pos) {
return fragments.get(pos);
}
public void addFragment(Fragment f) {
fragments.add(f);
}
public CollectionPagerAdapter(FragmentManager fm) {
super(fm);
//Let's add the fragments
for (int i=0;i<NUM_ITEMS;i++)
{
Fragment fragment = new TabFragment();
Bundle args = new Bundle();
args.putInt(TabFragment.ARG_OBJECT, 0);
fragment.setArguments(args);
addFragment (fragment);
}
}
#Override
public int getCount() {
return NUM_ITEMS;
}
}
TabFragment class
public class TabFragment extends ListFragment {
public static final String ARG_OBJECT = "object";
private MoviesAdapter m_Adapter;
private ArrayList <Movie> mMovies = new ArrayList<Movie>();
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// you only need to instantiate these the first time your fragment is
// created; then, the method above will do the rest
if (m_Adapter == null) {
m_Adapter = new MoviesAdapter(getActivity(), mMovies);
}
setListAdapter(m_Adapter);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
int position = getArguments().getInt(ARG_OBJECT); // to check is the right fragment
View rootView = inflater.inflate(R.layout.tabs, container, false);
return rootView;
}
public void updateDisplay (int type)
{
GlobalVars gv = (GlobalVars)getActivity().getApplicationContext();
switch (type)
{
case 0:
mMovies = gv.getActionMovies();
break;
case 1:
mMovies = gv.getAdventureMovies();
break;
case 2:
mMovies = gv.getAnimationMovies();
break;
}
m_Adapter.notifyDataSetChanged();
}
}
I don't what I'm doing wrong, I guess that the fragments are messed up, because when I press the second tab, data from the first tab is updated, and so on ...
Thanks!
Instead of using CollectionPageAdapter, I changed to use the TabsAdapter class shown in Android documentation of ViewPager and it works!
public 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 final List<Fragment> fragments = new ArrayList<Fragment>();
static final class TabInfo {
private final Class<?> clss;
private final Bundle args;
TabInfo(Class<?> _class, Bundle _args) {
clss = _class;
args = _args;
}
}
public TabsAdapter(FragmentActivity activity, ViewPager pager) {
super(activity.getSupportFragmentManager());
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 int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
Fragment fr = Fragment.instantiate(mContext, info.clss.getName(), info.args);
//addFragment (fr, position);
return fr;
}
public void addFragment(Fragment f, int location) {
if (fragments.size() == 0)
fragments.add(f);
else
fragments.add(location, f);
}
#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, android.app.FragmentTransaction ft) {
Object tag = tab.getTag();
for (int i=0; i<mTabs.size(); i++) {
if (mTabs.get(i) == tag) {
updateDatasetMovies (i);
mViewPager.setCurrentItem(i);
}
}
}
#Override
public void onTabReselected(Tab tab, android.app.FragmentTransaction ft) {
// TODO Auto-generated method stub
}
#Override
public void onTabUnselected(Tab tab, android.app.FragmentTransaction ft) {
// TODO Auto-generated method stub
}
public void updateDatasetMovies (int pos)
{
//Let's update the dataset for the selected genre
TabFragment fragment =
(TabFragment) ((FragmentActivity)mContext).getSupportFragmentManager().findFragmentByTag(
"android:switcher:"+R.id.pager+":"+pos);
//TabFragment fragment = (TabFragment) getItem(pos);
if(fragment != null) // could be null if not instantiated yet
{
if(fragment.getView() != null)
{
// no need to call if fragment's onDestroyView()
//has since been called.
fragment.updateDisplay(pos); // do what updates are required
}
}
}
}`
I agree with nirvik on this, however there is one cruciual improvement that should be made. This implementation has one flaw - when swiping to a next fragment the onPageSelected method is invoked, which in turn invokes onTabSelected method by calling mActionBar.setSelectedNavigationItem(position);. This causes a flicker in the animation.
This:
viewPager.setCurrentItem(i);
should be replaced by this:
if(i != viewPager.getCurrentItem()) {
viewPager.setCurrentItem(i);
}
resulting in a smooth transition.
You can try including the following code in your FragmentPagerAdapter and see if this addresses the issue.
public int getItemPosition(Object object) {
return POSITION_NONE;
}
You could also try using a FragmentStatePagerAdapter.

Android Viewpager Tabs updating tab fragment

I have an Activity which scrapes a webpage in a CustomLoader.
When loader finished I want to update the contents of 3 tabs with the data retrieved.
I'm using the sample code provided by android dev samples to implement the Viewpager/Tabs/Fragments on the activity.
When the fragment is created for the tab the onCreateView is correctly called, all the widgets on the view are correctly located and mapped to the variables.
However, when I attempt to find the fragment from the Activity and call a method on the fragment to update its contents the variables are null. Furthermore, calling getView also returns null - The instance of the fragment I'm retrieving from the TabsAdapter is not the correct instantiated instance ?
I've cut the code down to a single tab fragment, code in question is the call from onLoaderFinished to update the fragment.
All 3 tab fragments will be populated with data from a single loader, hence the loader being on the activity, not on the fragments. I just need a way to tell the fragments to paint their new data.
public class InfoBloodStocksActivity extends SherlockFragmentActivity
implements LoaderManager.LoaderCallbacks<BloodStocksLoaderResponse> {
TabHost mTabHost;
ViewPager mViewPager;
TabsAdapter mTabsAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.info_bloodstocks_tabpager);
mTabHost = (TabHost)findViewById(android.R.id.tabhost);
mTabHost.setup();
mViewPager = (ViewPager)findViewById(R.id.infoBloodStocksTabPager);
mViewPager.setOffscreenPageLimit(3);
mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager);
mTabsAdapter.addTab(mTabHost.newTabSpec("Daily stock").setIndicator("Daily stocks"),
InfoBloodStocksPageFragment.class, null);
if (savedInstanceState != null) {
mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab"));
}
loadStockDetailsFromWebsite();
}
private void loadStockDetailsFromWebsite() {
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getSupportLoaderManager().initLoader(0, null, this);
}
#Override
public Loader<BloodStocksLoaderResponse> onCreateLoader(int arg0, Bundle arg1) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader with no arguments, so it is simple.
return new BloodStocksCustomLoader(this);
}
#Override
public void onLoadFinished(Loader<BloodStocksLoaderResponse> loader, BloodStocksLoaderResponse response) {
if (response.isNewStocksLoaded()) {
InfoBloodStocksPageFragment fragment = (InfoBloodStocksPageFragment) mTabsAdapter.getItem(0);
fragment.setNewImage(response.getDailyStocksImageURL());
}
}
#Override
public void onLoaderReset(Loader<BloodStocksLoaderResponse> arg0) {
// TODO Auto-generated method stub
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("tab", mTabHost.getCurrentTabTag());
}
/**
* 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 TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
private final Context mContext;
private final TabHost mTabHost;
private final ViewPager mViewPager;
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
static final class TabInfo {
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(FragmentActivity activity, TabHost tabHost, ViewPager pager) {
super(activity.getSupportFragmentManager());
mContext = activity;
mTabHost = tabHost;
mViewPager = pager;
mTabHost.setOnTabChangedListener(this);
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
}
public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
tabSpec.setContent(new DummyTabFactory(mContext));
String tag = tabSpec.getTag();
TabInfo info = new TabInfo(tag, clss, args);
mTabs.add(info);
mTabHost.addTab(tabSpec);
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 onTabChanged(String tabId) {
int position = mTabHost.getCurrentTab();
mViewPager.setCurrentItem(position);
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
// Unfortunately when TabHost changes the current tab, it kindly
// also takes care of putting focus on it when not in touch mode.
// The jerk.
// This hack tries to prevent this from pulling focus out of our
// ViewPager.
TabWidget widget = mTabHost.getTabWidget();
int oldFocusability = widget.getDescendantFocusability();
widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
mTabHost.setCurrentTab(position);
widget.setDescendantFocusability(oldFocusability);
}
#Override
public void onPageScrollStateChanged(int state) {
}
}
}
I'm calling setNewImage from the activity.
public class InfoBloodStocksPageFragment extends SherlockFragment {
private ImageView mImageView;
private ProgressBar mProgress;
private TextView mTitle;
private TextView mDescriptionText;
private String mImageURL;
private ImageLoader mImageLoader = ImageLoader.getInstance();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.info_bloodstocks_page, container, false);
mImageView = (ImageView)view.findViewById(R.id.bloodStocksPageImage);
mProgress = (ProgressBar)view.findViewById(R.id.bloodStocksPageProgress);
mTitle = (TextView)view.findViewById(R.id.bloodStockPageTitle);
mDescriptionText = (TextView)view.findViewById(R.id.bloodStocksPageText);
mProgress.setVisibility(View.VISIBLE);
mDescriptionText.setVisibility(View.GONE);
return view;
}
public void setNewImage(String imageURL) {
// why is mImageView null here ?
// why does getView() return null here ?
View view = getView();
mImageView = (ImageView)view.findViewById(R.id.bloodStocksPageImage);
DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder()
.showImageForEmptyUri(R.drawable.loading_stock_figures)
.cacheOnDisc()
.imageScaleType(ImageScaleType.EXACT)
.build();
mImageLoader.displayImage(imageURL, mImageView, displayImageOptions, new ImageLoadingListener() {
#Override
public void onLoadingStarted() {
mProgress.setVisibility(View.VISIBLE);
}
#Override
public void onLoadingFailed(FailReason failReason) {
String message = null;
switch (failReason) {
case IO_ERROR:
message = "Input/Output error";
break;
case OUT_OF_MEMORY:
message = "Out Of Memory error";
break;
case UNKNOWN:
message = "Unknown error";
break;
}
mProgress.setVisibility(View.GONE);
mImageView.setImageResource(android.R.drawable.ic_delete);
}
#Override
public void onLoadingComplete() {
mProgress.setVisibility(View.GONE);
Animation anim = AnimationUtils.loadAnimation(getActivity(), R.anim.fade_in);
mImageView.setAnimation(anim);
anim.start();
}
#Override
public void onLoadingCancelled() {
// Do nothing
}
});
}
}
I've just started using http://square.github.com/otto/ for doing this. The documentation on the site should be enough to get you started with it.

Categories

Resources