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.
Related
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?
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 am using a cursor loader to create the tabs in an activity. I am using swipable tabs as detailed on the android View Pager documentation.
http://developer.android.com/reference/android/support/v4/view/ViewPager.html
So far I haven't implemented putting any of my pages that are loaded onto the back stack, so when I click back it jumps to the previous activity. (This is fine for now.) Clicking either the back or up button results in a Run time Exception java.lang.RuntimeException: Unable to destroy activity: java.lang.IllegalStateException: Activity has been destroyed
The only differences I know of between what I have and the example is the use of the cursor, and that the example sets the ViewPager itself as the content view, whereas I've used a layout file that contains a ViewPager. I'm also using a FragmentStatePagerAdapter rather than FragmentPagerAdapter, but switching between these two made no difference.
I'm not sure what in the android onDestroy call would try to destroy the activity twice. My code is below. Any help is appreciated, and I'd be glad to provide more info. Thanks.
public class WorksheetActivity extends FragmentActivity implements android.support.v4.app.LoaderManager.LoaderCallbacks<Cursor> {
public String user;
private static final int DAY_LOADER = 0;
public int program_index;
public int program_id;
public int item_id;
public int type_key;
ViewPager mViewPager;
TabsAdapter mTabsAdapter;
LoaderManager.LoaderCallbacks<Cursor> mCallbacks;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_worksheet);
mCallbacks = this;
ActionBar bar = getActionBar();
//this sets up to include tabs, and to show the title on each tab
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
//enables navigation up, when clicked calls on options item selected for home button.
bar.setDisplayHomeAsUpEnabled(true);
Bundle extras = getIntent().getExtras();
if (!(extras.isEmpty())){
user = extras.getString("user");
//used for moving up to the Program Activity and restoring the selection.
program_index = extras.getInt(ProgramActivity.PROGRAM_KEY);
program_id = extras.getInt(ProgramActivity.PROGRAM_ID);
item_id = extras.getInt(ProgramActivity.ITEM_ID);
type_key = extras.getInt(ProgramActivity.TYPE_KEY);
}
Bundle tabData = new Bundle();
tabData.putAll((extras.isEmpty()? savedInstanceState : extras));
mViewPager = (ViewPager) findViewById(R.id.pager);
mTabsAdapter = new TabsAdapter(this, mViewPager, null, tabData);
LoaderManager lm = getSupportLoaderManager();
lm.initLoader(0, tabData, mCallbacks);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_worksheet, menu);
return true;
}
#Override
public void onSaveInstanceState(Bundle out){
out.putString("user",user);
//used for moving up to the Program Activity and restoring the selection.
out.putInt(ProgramActivity.PROGRAM_KEY, program_index);
out.putInt(ProgramActivity.ITEM_ID, item_id);
out.putInt(ProgramActivity.PROGRAM_ID, program_id);
out.putInt(ProgramActivity.TYPE_KEY, type_key);
}
#Override
public void onRestoreInstanceState(Bundle in){
user = in.getString("user");
program_index = in.getInt(ProgramActivity.PROGRAM_KEY);
program_id = in.getInt(ProgramActivity.PROGRAM_ID);
item_id = in.getInt(ProgramActivity.ITEM_ID);
type_key = in.getInt(ProgramActivity.TYPE_KEY);
}
#SuppressLint("NewApi")
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Intent upIntent = new Intent(this, ProgramActivity.class);
upIntent.putExtra("user", user);
upIntent.putExtra(ProgramActivity.PROGRAM_KEY, program_index);
if (NavUtils.shouldUpRecreateTask(this, upIntent)){
TaskStackBuilder.create(this)
.addNextIntent(new Intent(this, Marta_QC.class))
.addNextIntent(upIntent).startActivities();
finish();
}else{
NavUtils.navigateUpTo(this, upIntent);
}
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle arg1) {
CursorLoader cl = null;
switch(id){
case DAY_LOADER:
cl = new CursorLoader(getApplicationContext(), Tasks.CONTENT_URI, new String[]{Tasks.DAY_COL},Tasks.PROGRAM_COL+" = ?",
new String[]{Integer.toString(program_id)}, Tasks.DAY_COL+" ASC");
break;
default:
}
return cl;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
switch(loader.getId()){
case DAY_LOADER:
mTabsAdapter.swapCursor(c);
break;
default:
}
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
switch(loader.getId()){
case DAY_LOADER:
mTabsAdapter.swapCursor(null);
break;
default:
}
}
public static class TabsAdapter extends FragmentStatePagerAdapter
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 Bundle mArgs;
private int currentPosition;
static final class TabInfo{
private final Class<?> clss;
private final Bundle args;
public final int position;
TabInfo(Class<?> _class, Bundle _args, int _position){
clss = _class;
args = _args;
position = _position;
}
}
public TabsAdapter (FragmentActivity activity, ViewPager pager, Cursor days, Bundle args){
super(activity.getSupportFragmentManager());
mContext = activity;
mActionBar = activity.getActionBar();
mViewPager = pager;
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
mArgs = args;
}
public void swapCursor(Cursor c){
mActionBar.removeAllTabs();
mViewPager.removeAllViews();
//add the exceptions tab. Always first.
ActionBar.Tab tab = mActionBar.newTab();
Bundle tabArgs = new Bundle();
tabArgs.putAll(mArgs);
this.addTab(tab, ExceptionsSheet.class, tabArgs);
//add each of the day tabs to the set, which stores unique values only
LinkedHashSet<String> days = new LinkedHashSet<String>();
if(c.moveToFirst()){
do{
days.add(c.getString(c.getColumnIndex(Tasks.DAY_COL)));
}while (c.moveToNext());
}
//iterate through the unique days
for(String day:days){
ActionBar.Tab dayTab = mActionBar.newTab();
Bundle dayArgs = new Bundle();
dayArgs.putAll(mArgs);
dayArgs.putString("day", day);
this.addTab(dayTab, Worksheet.class, dayArgs);
}
//start with the first day
mActionBar.setSelectedNavigationItem(1);
}
public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args){
//implemented adding a position to the tab info class.
//gets the current tab count prior to adding this tab.
TabInfo info = new TabInfo(clss, args, this.getCount());
tab.setTag(info);
tab.setTabListener(this);
mTabs.add(info);
if (clss.equals(ExceptionsSheet.class)){
//tab.setCustomView(R.layout.exceptions_tab);
tab.setText(mContext.getText(R.string.exceptions_tab_title));
}else{
if(clss.equals(Worksheet.class)){
tab.setText("Day "+args.getString("day"));
}else{
Log.d("unknown class", clss.toString());
}
}
mActionBar.addTab(tab);
notifyDataSetChanged();
}
#Override
public void onPageSelected(int pos) {
// set the tab to match the page, following a swipe action.
currentPosition = pos;
mActionBar.setSelectedNavigationItem(pos);
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
//this will return our tab info
TabInfo minfo = (TabInfo) tab.getTag();
int pos = minfo.position;
this.getItem(currentPosition);
mViewPager.setCurrentItem(pos);
currentPosition = pos;
}
//this is the FragmentPager Adapter main method
//here we will use the arguments bundled into the tab to set the
//fragment for the page.
#Override
public Fragment getItem(int pos) {
TabInfo tabInfo = mTabs.get(pos);
return Fragment.instantiate(mContext, tabInfo.clss.getName(), tabInfo.args);
}
}
//class for exceptions display
public static class ExceptionsSheet extends Fragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
int i = 1;
i = i+1;
//must include false, since we're attaching to the ViewPager sheet, not the root view.
return inflater.inflate(R.layout.exception, container, false);
}
}
//class for standard worksheet display
public static class Worksheet extends Fragment{
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
return inflater.inflate(R.layout.worksheet, container, false);
}
}
}
I found the problem. When the Activity is destroyed, the onLoaderReset() method is called. Here I swapped in a null cursor to remove references to the cursor (as with non-custom adapters), but my swapCursor method did not check to see if the cursor was null. Once I fixed this the code no longer tries to update the UI after the activity has been destroyed.
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.
I am trying to keep a view pager inside a fragment. And the View pager itself contains 3 other fragment. This is the Root fragment which contain the view pager
import android.support.v13.app.FragmentPagerAdapter;
import android.support.v13.app.FragmentStatePagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import com.android.browser1.UI.ComboViews;
public class MyFragment extends Fragment implements
TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener, CombinedBookmarksCallbacks {
private TabHost mTabHost;
private ViewPager mViewPager;
public static final String EXTRA_COMBO_ARGS = "combo_args";
Bundle bundle;
Bundle extra;
public static final String EXTRA_INITIAL_VIEW = "initial_view";
public static BookmarkFragment bookmarkFragmentForPageA = null;
public static BookmarkFragment bookmarkFragmentForPageB = null;
Controller controller;
TabsAdapter mTabsAdapter;
FirstFragment first;
SecondFragment seconde;
ThirdFragment third;
public void setBundle(Bundle bundle) {
extra = bundle;// getActivity().getIntent().getExtras();
this.bundle = extra.getBundle(EXTRA_COMBO_ARGS);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mTabHost.setup();
mViewPager.setOffscreenPageLimit(2);
mTabsAdapter = new TabsAdapter(getActivity(), mTabHost, mViewPager);
new setAdapterTask().execute();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.bookmark_view_pager, null);
mTabHost = (TabHost) view.findViewById(R.id.tabHost);
mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
view.setBackgroundColor(Color.BLACK);
return view;
}
private class setAdapterTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... params) {
return null;
}
#Override
protected void onPostExecute(Void result) {
String bookmark = getResources().getString(R.string.tab_bookmarks);
String history = getResources().getString(R.string.tab_history);
String scrapmemo = getResources().getString(R.string.tab_snapshots);
mTabsAdapter.addTab(
mTabHost.newTabSpec(bookmark).setIndicator(bookmark), BrowserBookmarksPage.class, null);
mTabsAdapter.addTab(
mTabHost.newTabSpec(history).setIndicator(history), BrowserHistoryPage.class, null);
mTabsAdapter.addTab(
mTabHost.newTabSpec(scrapmemo).setIndicator(scrapmemo), BrowserSnapshotPage.class, null);
String svStr = extra.getString(EXTRA_INITIAL_VIEW, null);
ComboViews startingView = svStr != null ? ComboViews.valueOf(svStr)
: ComboViews.Bookmarks;
switch (startingView) {
case Bookmarks:
mTabHost.setCurrentTab(0);
mViewPager.setCurrentItem(0);
break;
case History:
mTabHost.setCurrentTab(1);
mViewPager.setCurrentItem(1);
break;
case ScrapMemo:
mTabHost.setCurrentTab(2);
mViewPager.setCurrentItem(2);
break;
}
}
}
public void onTabChanged(String tag) {
ComboViews startingView = tag != null ? ComboViews.valueOf(tag)
: ComboViews.Bookmarks;
switch (startingView) {
case First:
mTabHost.setCurrentTab(0);
break;
case Second:
mTabHost.setCurrentTab(1);
break;
case Third:
mTabHost.setCurrentTab(2);
break;
}
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
this.mTabHost.setCurrentTab(position);
}
#Override
public void onPageScrollStateChanged(int state) {
}
public 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>();
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;
}
}
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(Activity activity, TabHost tabHost, ViewPager pager) {
super(activity.getFragmentManager());
mContext = activity;
mTabHost = tabHost;
mViewPager = pager;
mTabHost.setOnTabChangedListener(this);
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
mViewPager.setOffscreenPageLimit(3);
}
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();
getItem(0);
}
#Override
public int getCount() {
return mTabs.size();
}
#Override
public Fragment getItem(int position) {
Fragment fr = null;
TabInfo info = mTabs.get(position);
// Create a new fragment if necessary.
switch (position) {
case 0:
fr = new FirstFragment();
fr.setArguments(info.args);
break;
case 1:
fr = new SecondFragment ();
fr.setArguments(info.args);
break;
case 2:
fr = new ThirdFragment ();
fr.setArguments(info.args);
break;
default:
fr = new FirstFragment ();
fr.setArguments(info.args);
break;
}
return fr;
}
#Override
public int getItemPosition(Object object) {
if (object instanceof FirstFragment
|| object instanceof SecondFragment
|| object instanceof ThirdFragment )
return POSITION_NONE;
return POSITION_UNCHANGED;
}
#Override
public void onTabChanged(String tabId) {
// called when the user clicks on a tab.
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) {
}
}
}
And when I attach this fragment in my activity for the first time we can see all the three fragment by swiping but after the back press we remove the fragment. After that again I attach the fragment in the same activity at run time but only first 2 fragments are visible and the content of 3rd one is empty.
I am trying to keep a view pager inside a fragment. And the View pager itself contains 3 other fragment.
Sorry, but fragments inside of other fragments is officially not supported, as indicated by Dianne Hackborn, a core Android team member.