I'm using actionbar tabs because I need the navigation elements to be on every page. I'm using ActionBarSherlock for backwards compatibility (minimum API 8, target API 17). My MainActivity extends SherlockFragmentActivity. In my onCreate() for that, I have
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.setDisplayShowTitleEnabled(true);
Tab tab1 = actionBar.newTab().setText("My Pages")
.setTabListener(new MyPagesFragment());
Tab tab2 = actionBar.newTab().setText("Search")
.setTabListener(new SearchFragment());
Tab tab3 = actionBar.newTab().setText("About")
.setTabListener(new AboutFragment());
// Start with the second tab selected.
actionBar.addTab(tab1, 0, false);
actionBar.addTab(tab2, 1, true);
actionBar.addTab(tab3, 2, false);
All the tab fragments are SherlockListFragments that implement ActionBar.TabListener, and do this
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
ft.replace(android.R.id.content, this, "mypages");
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft.remove(this);
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// Force a complete reload.
onTabSelected(tab, ft);
}
The search page has an EditText and uses its value in an AsyncTask to fetch data from an API and add it to an SQLite database, before calling
((MainActivity) getActivity()).showDetailView(responseCode);
to show the details, which is a method in my MainActivity as follows:
protected void showDetailView(long codeID) {
SherlockFragment detailFragment = new DetailFragment();
Bundle args = new Bundle();
args.putLong("codeID", codeID);
detailFragment.setArguments(args);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(android.R.id.content, detailFragment);
ft.addToBackStack(null);
ft.commit();
}
DetailFragment is a SherlockFragment that uses getArguments() to retrieve the codeID--
Bundle args = getArguments();
if (null != args) {
codeRowID = args.getLong("codeID");
}
--and reads the matching data from the database to display it. Said data often contains links to more details, clicking which causes showDetailView to be called again with the new codeID.
MyPages is a list of all the cached details pages, and it too calls showDetailView:
#Override
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
((MainActivity) getActivity()).showDetailView(pages[position].codeId);
}
Going forward, this appears to work fine. However, when I use the back button, sometimes the fragments stick around, so they're still visible behind the restored fragment. How do I stop this happening?
I think the problem is maybe that the tabs are not being added to the backstack? But when I try to do that, they throw an exception telling me they can't be added to the backstack, so I don't understand how you are supposed to handle this. I don't understand why this thing, which it seems to be should be an incredibly basic navigation thing that lots of people want to do -- the back key has been on every android phone and table I've ever seen, physically or software! --apparently has no known solution. Have I just fundamentally misunderstood their use? Are fragments just not supposed to be used in this situation? How else do you do persistent navigation elements without repeating the same code on every page?
Logging - start app -
07-20 23:33:49.521: D/NAVIGATION_TRACE(7425): MAIN - onCreate
07-20 23:33:50.013: D/NAVIGATION_TRACE(7425): SEARCH - onTabSelected
07-20 23:33:50.021: D/NAVIGATION_TRACE(7425): SEARCH - onCreateView
07-20 23:33:50.060: D/NAVIGATION_TRACE(7425): SEARCH - onActivityCreated
07-20 23:33:50.060: D/NAVIGATION_TRACE(7425): MAIN - onResume
Search now visible. Search for an item to bring up detail view -
07-20 23:34:52.123: D/NAVIGATION_TRACE(7425): SEARCH - handleResponseCode
07-20 23:34:52.123: D/NAVIGATION_TRACE(7425): MAIN - showDetailView - 31
Detail now visible; Search gone. Click mypages tab -
07-20 23:35:37.787: D/NAVIGATION_TRACE(7425): SEARCH - onTabUnselected
07-20 23:35:37.787: D/NAVIGATION_TRACE(7425): MYPAGES - onTabSelected
07-20 23:35:37.826: D/NAVIGATION_TRACE(7425): MYPAGES - onCreateView
07-20 23:35:37.873: D/NAVIGATION_TRACE(7425): MYPAGES - onActivityCreated
MyPages now visible; Detail gone. Click back button -
07-20 23:36:12.130: D/NAVIGATION_TRACE(7425): SEARCH - onCreateView
07-20 23:36:12.201: D/NAVIGATION_TRACE(7425): SEARCH - onActivityCreated
Search and MyPages now both showing.
MainActivity:
public class MainActivity extends SherlockFragmentActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Log.d("NAVIGATION_TRACE", "MAIN - onCreate");
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.setDisplayShowTitleEnabled(true);
Tab tab1 = actionBar.newTab().setText("My Pages")
.setTabListener(new TabListener<MyPagesFragment>(
this, "mypages", MyPagesFragment.class));
Tab tab2 = actionBar.newTab().setText("Search")
.setTabListener(new TabListener<SearchFragment>(
this, "search", SearchFragment.class));
Tab tab3 = actionBar.newTab().setText("About")
.setTabListener(new TabListener<AboutFragment>(
this, "about", AboutFragment.class));
// Start with the second tab selected.
actionBar.addTab(tab1, 0, false);
actionBar.addTab(tab2, 1, true);
actionBar.addTab(tab3, 2, false);
}
#Override
public void onBackPressed()
{
FragmentManager fm = getSupportFragmentManager();
if (0 < fm.getBackStackEntryCount())
{
super.onBackPressed();
} else {
// prompt to quit
AlertDialog.Builder alertErrorResponse = new AlertDialog.Builder(this);
alertErrorResponse.setMessage("Close app?");
alertErrorResponse.setNegativeButton("Cancel", null);
alertErrorResponse.setPositiveButton("OK", new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
finish();
}
});
alertErrorResponse.show();
}
}
public void showDetailView(long codeID) {
Log.d("NAVIGATION_TRACE", "MAIN - showDetailView - "+String.valueOf(codeID));
lastShownCode = codeID;
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
SherlockFragment detailFragment = new DetailFragment();
Bundle args = new Bundle();
args.putLong("codeID", codeID);
detailFragment.setArguments(args);
ft.replace(android.R.id.content, detailFragment, "details");
ft.addToBackStack(null);
ft.commit();
}
public class TabListener<T extends SherlockListFragment> implements ActionBar.TabListener
{
private final SherlockFragmentActivity mActivity;
private final String mTag;
private final Class<T> mClass;
private SherlockListFragment mFragment;
public TabListener (SherlockFragmentActivity activity, String tag, Class<T> clz)
{
Log.d("NAVIGATION_TRACE", "TabListener - "+tag+" - "+clz.getCanonicalName());
mActivity = activity;
mTag = tag;
mClass = clz;
FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
mFragment = (SherlockListFragment) mActivity.getSupportFragmentManager().findFragmentByTag(mTag);
if (mFragment != null && !mFragment.isDetached())
{
Log.d("NAVIGATION_TRACE", "DETACH - "+mTag);
removeDetail(ft);
ft.detach(mFragment);
}
ft.commit();
}
public void clearBackStack()
{
Log.d("NAVIGATION_TRACE", "clearBackStack - "+mTag);
FragmentManager fm = mActivity.getSupportFragmentManager();
if (null != fm && 0 < fm.getBackStackEntryCount())
{
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft)
{
Log.d("NAVIGATION_TRACE", "onTabSelected - "+mTag);
clearBackStack();
ft = mActivity.getSupportFragmentManager().beginTransaction();
if (mFragment == null)
{
Log.d("NAVIGATION_TRACE", "ADD/SHOW - "+mClass.getName());
removeDetail(ft);
mFragment = (SherlockListFragment) SherlockListFragment.instantiate(mActivity, mClass.getName());
ft.add(android.R.id.content, mFragment, mTag);
ft.commit();
}
else
{
Log.d("NAVIGATION_TRACE", "ATTACH/SHOW - "+mClass.getName());
removeDetail(ft);
ft.attach(mFragment);
ft.show(mFragment);
ft.commit();
}
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft)
{
Log.d("NAVIGATION_TRACE", "onTabUnselected - "+mTag);
ft = mActivity.getSupportFragmentManager().beginTransaction();
if (null != mFragment)
{
Log.d("NAVIGATION_TRACE", "HIDE/DETACH - "+mTag);
removeDetail(ft);
ft.hide(mFragment);
ft.detach(mFragment);
ft.commitAllowingStateLoss();
}
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
public void removeDetail(FragmentTransaction ft) {
SherlockFragment detailFragment = (SherlockFragment) mActivity.getSupportFragmentManager().findFragmentByTag("details");
if (null != detailFragment && !detailFragment.isDetached()) {
Log.d("NAVIGATION_TRACE", "DETACH - details");
ft.detach(detailFragment);
}
}
}
}
Example fragment - MyPagesFragment :
public class MyPagesFragment extends SherlockListFragment implements
OnItemClickListener
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
Log.d("NAVIGATION_TRACE", "MYPAGES - onCreateView");
View view = inflater.inflate(R.layout.mypages, null);
// code to set up list adapter here
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d("NAVIGATION_TRACE", "MYPAGES - onActivityCreated");
getListView().setOnItemClickListener(this);
}
#Override
public void onItemClick(AdapterView<?> parent, View v, int position, long id)
{
Log.d("NAVIGATION_TRACE", "MYPAGES - onItemClick");
((MainActivity) getActivity()).showDetailView(pages[position].codeId);
}
}
DetailFragment
public class DetailFragment extends SherlockFragment implements
OnItemClickListener
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.detail, null);
// bunch of display / list set up code goes here
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
lvLinks.setOnItemClickListener(this);
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
// Details page can open other details pages:
((MainActivity) getActivity()).showDetailView(pages[position].id);
}
}
Note: The changes added were made following an answer by Sheldon (who seems to have, annoyingly, deleted their answer and comments), which is why the TabListener code changed between the original section and the later posted code.
I've currently hacked a solution by emptying the backstack on every tab select, and treating back on a top tab as an exit-the-app request, which is okay, I guess, but I really would like users to be able to keep backing through the tabs if this is at all possible (because that way, eg, if I was five details pages deep and I stopped to quickly search for something which, it turned out, didn't exist, I can back to those detail pages and still go up one or more to follow different detail links.)
Try this..
FragmentManager manager = getSupportFragmentManager();
if (manager.getBackStackEntryCount() > 0)
getSupportFragmentManager().popBackStack();
else
finish();
Related
I am developing an app in which there are two fragmenttabs.when pressing the tabs corresponding fragments will appear.that works fine.but what I want an activity inside the fragmenttabs. I am using ABS library for this.
ActionBar bar = getSupportActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.Tab tab1 = bar.newTab();
ActionBar.Tab tab2 = bar.newTab();
tab1.setText("Fragment A");
tab2.setText("Fragment B");
tab1.setTabListener(new MyTabListener<FragmentA>(this, "tab1",
FragmentA.class, null));
tab2.setTabListener(new MyTabListener<FragmentB>(this, "tab1",
FragmentB.class, null));
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// app icon in action bar clicked; go Location selection
Intent intent = new Intent(FragmentDemoActivity.this,
TestActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("tab", getSupportActionBar()
.getSelectedNavigationIndex());
}
listnerclass is
public class MyTabListener<T extends Fragment> implements ActionBar.TabListener {
private final FragmentActivity mActivity;
private final String mTag;
private final Class<T> mClass;
private final Bundle mArgs;
private Fragment mFragment;
public MyTabListener(FragmentActivity activity, String tag, Class<T> clz,
Bundle args) {
mActivity = activity;
mTag = tag;
mClass = clz;
mArgs = args;
FragmentTransaction ft = mActivity.getSupportFragmentManager()
.beginTransaction();
mFragment = mActivity.getSupportFragmentManager().findFragmentByTag(
mTag);
if (mFragment != null && !mFragment.isDetached()) {
ft.detach(mFragment);
}
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
ft = mActivity.getSupportFragmentManager().beginTransaction();
if (mFragment == null) {
mFragment = Fragment
.instantiate(mActivity, mClass.getName(), mArgs);
ft.add(android.R.id.content, mFragment, mTag);
ft.commit();
} else {
ft.attach(mFragment);
ft.commit();
}
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft = mActivity.getSupportFragmentManager().beginTransaction();
if (mFragment != null) {
ft.detach(mFragment);
ft.commitAllowingStateLoss();
}
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
Fragmentclass
public class FragmentB extends Fragment {
Button button;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup group, Bundle saved)
{
return inflater.inflate(R.layout.frag_b, group, false);
}
#Override
public void onActivityCreated (Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
button = (Button) getActivity().findViewById(R.id.button2);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Fragment B");
builder.setMessage("What would you like to do?");
builder.setPositiveButton("Nothing", null);
builder.setNegativeButton("Leave me alone!", null);
builder.show();
}
});
}
}
i wnt to include the following activity in the fragmentB
public class TestActivity extends Activity {
Button b1, b2;
TextView tv;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.testactivity);
b1 = (Button) findViewById(R.id.button1);
b2 = (Button) findViewById(R.id.button2);
tv = (TextView) findViewById(R.id.textView1);
b1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
tv.setText("You Clicked on Button 1");
}
});
b2.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
tv.setText("You Clicked on Button 2");
}
});
}
}
I have lot of created activites to include in the fragment..its un imaginable to recreate in onActivityCreated of fragment class. according to this I have to modify my main application.
A Fragment can't host an activity. Instead of activity, you can use Nested Fragment.
A simple tutorial: http://xperiment-andro.blogspot.com/2013/02/nested-fragments.html
You cannot run an activity inside of a fragment. At best, you can have a fragment inside of a fragment.
so the best thing you can do in this situation is instead of creating an Activity you can create a Fragment and add it inside your FragmentB.
Like Faheem Said "
A Fragment can't host an activity. Instead of activity, you can use Nested Fragment"
Fragments are just like Activities if you read the Docs.Changing your activity to Fragment is easy
This is what developer.android says
To create a fragment, you must create a subclass of Fragment (or an existing subclass of it). The Fragment class has code that looks a lot like an Activity. It contains callback methods similar to an activity, such as onCreate(), onStart(), onPause(), and onStop(). In fact, if you're converting an existing Android application to use fragments, you might simply move code from your activity's callback methods into the respective callback methods of your fragment.
Usually, you should implement at least the following lifecycle methods:
onCreate()
The system calls this when creating the fragment. Within your implementation, you should initialize essential components of the fragment that you want to retain when the fragment is paused or stopped, then resumed.
onCreateView()
The system calls this when it's time for the fragment to draw its user interface for the first time. To draw a UI for your fragment, you must return a View from this method that is the root of your fragment's layout. You can return null if the fragment does not provide a UI.
onPause()
The system calls this method as the first indication that the user is leaving the fragment (though it does not always mean the fragment is being destroyed). This is usually where you should commit any changes that should be persisted beyond the current user session (because the user might not come back).
This is the code activity inside Fragment.
YourActivityName.getSupportFragmentManager();
I have a ListView inside of a Fragment attached to a CursorAdapter. The Fragment has setRetainInstance(true). Under the Fragment's onCreate() method, I instantiate the CursorAdapter (storing it in variable adapter). I then call listView.setAdapter(adapter) under my Fragment's onCreateView method. The Cursor in the CursorAdapter is loaded by a CursorLoader. Inside my LoaderCallbacks' onLoadFinished(), I call adapter.swapCursor(cursor).
In sum: Everything seems to be in place such that the ListView should not scroll to top when changing between tabs and back. But it still does! Could I be missing something?
Here's some code.
Fragment
public class Home extends Fragment{
...
private HomeFeedAdapter adapter; // HomeFeedAdapter extends CursorAdapter
private AutoQuery autoQuery; // AutoQuery extends LoaderCallbacks<Cursor>
... // (See inner class, at the end)
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
if(adapter == null)
adapter = new HomeFeedAdapter(getActivity(), null);
if(autoQuery == null)
autoQuery = new AutoQuery();
getLoaderManager().initLoader(LOADER_INITIAL, null, autoQuery);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Layout
View v = inflater.inflate(R.layout.home, container, false);
l = (ListView) v.findViewById(R.id.listview);
l.setAdapter(adapter);
return v;
}
private class AutoQuery implements LoaderCallbacks<Cursor>{
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
...
return new CursorLoader(getActivity(), uri,
null, null, null, null);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
adapter.swapCursor(cursor);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
}
}
Activity
public class MainActivity extends SherlockFragmentActivity {
...
private class TabsListener implements ActionBar.TabListener {
private Fragment fragment;
private String tag;
public TabsListener(Fragment fragment, String tag) {
this.fragment = fragment;
this.tag = tag;
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// Do nothing
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
ft.replace(R.id.fragment_container, fragment, tag);
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft.remove(fragment);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Layout
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setHomeButtonEnabled(false);
getSupportActionBar().setDisplayShowTitleEnabled(false);
setContentView(R.layout.activity_main);
// Loads fragment
Fragment fHome, fActivity, fProfile;
if((fHome = getSupportFragmentManager().findFragmentByTag(HOME)) == null) fHome = new Home();
if((fActivity = getSupportFragmentManager().findFragmentByTag(ACTIVITY)) == null) fActivity = new FriendsList();
if((fProfile = getSupportFragmentManager().findFragmentByTag(PROFILE)) == null) fProfile = new Profile();
ActionBar.Tab tab;
tab = getSupportActionBar().newTab();
tab.setTabListener(new TabsListener(
fHome,
HOME
));
getSupportActionBar().addTab(tab, false);
tab = getSupportActionBar().newTab();
tab.setTabListener(new TabsListener(
fActivity,
ACTIVITY
));
getSupportActionBar().addTab(tab, false);
tab = getSupportActionBar().newTab();
tab.setTabListener(new TabsListener(
fProfile,
PROFILE
));
getSupportActionBar().addTab(tab, false);
...
}
}
With Greg Giacovelli's help in leading me in the right direction, I've found a solution to my problem.
I'll begin with a disclaimer that I don't quite understand how ListView positions are saved. My ListView instance is recreated every time that my Fragment's onCreateView() is called. This happens when the screen rotates, for example. And yet, in the specific case of screen rotations, even though onCreateView() is called and thus the ListView is reinstantiated, the ListView's state is nonetheless restored. So if the ListView is being recreated, something else must be telling it what its position previously was... and I don't know what that is. I think that it's attributable to the machinery of setRetainInstance(true).
But let's look at my main issue: Why did the ListView scroll to top between tab changes? As Greg suggested, it was because I was re-adding the Fragment with replace(), and thus destroying my Fragment and re-creating every time the user flipped to another tab and back.
My solution was to check if the tab was already added; if so, then not add it; else, add it. Then, when the user clicks out of a tab, I simply detach the fragment (not remove it), and attach a new one. This way the unselected tab's Fragment is still alive, though detached.
// Tabs listener
private class TabsListener implements ActionBar.TabListener {
private Fragment fragment;
private String tag;
public TabsListener(Fragment fragment, String tag) {
this.fragment = fragment;
this.tag = tag;
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
if(fragment instanceof ScrollableToTop) ((ScrollableToTop) fragment).scrollToTop();
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if(!fragment.isAdded()){
ft.add(R.id.fragment_container, fragment, tag);
}
ft.attach(fragment);
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft.detach(fragment);
}
}
I have read alot on stackoverflow on fragments issues but I cant find a solution to my problem.
I have a Tabhost and when I change rotation of the device and then select another tab, the view from the first tab is also visible. So both tabs content is on top of each other.
I'm using a a custom tablistener and every tab is a fragment. I could bypass this with android:configChanges="keyboardHidden|orientation|screenSize"but this solution gave me a list of other problems and I read that this is a bad solution.
public class TabListener<T extends Fragment> implements ActionBar.TabListener {
private Fragment fragment;
private final FragmentActivity activity;
private final String tag;
private final Class<T> myClass;
private long id;
public TabListener(FragmentActivity a, String t, Class<T> c, long id) {
tag = t;
myClass = c;
activity = a;
this.id = id;
}
/** The following are each of the ActionBar.TabListener callbacks */
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// Check if the fragment is already initialized
if (fragment == null) {
fragment = Fragment.instantiate(activity, myClass.getName());
// Sends stored TimerClass id to fragment
if(id != 0) {
Bundle b = new Bundle();
b.putLong("id", id);
fragment.setArguments(b);
}
ft.add(android.R.id.content, fragment, tag);
} else { // If it exists, simply attach it in order to show it
ft.attach(fragment);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
if (fragment != null)
ft.detach(fragment);
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
editNameDialog();
}
}
I dont know it this should be handled in the fragment, the activity or in the TabListener. The tabs content is viewd correctly until I change the screens orientation.
I found parts of the answer here on stackoverflow.
Solution
This is how I solved the problem. In the tab listeners I put:
fragment = activity.getSupportFragmentManager().findFragmentByTag(tag);
in both onTabSelect() and onTabUnselect()
Select correct tab after orientation change
When the screen rotates the activity is recreated(?) we need to store the last selected tab index.
Save last selected tab (this goes in the activity):
#Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putInt("lastTab", actionBar.getSelectedNavigationIndex());
}
To load tab index, put this in onCreate() of the activity, this way we retreive the last tab index and selects it:
if (savedInstanceState != null) {
actionBar.selectTab(actionBar.getTabAt(savedInstanceState.getInt("lastTab")));
}
Initialize controllers in the fragment
To prevent the controls to diplay and behave odd I moved all controls inits in the fragment to onCreateView() like this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.timer, container, false);
initializeControls(v);
setSeekBars(v);
return v;
}
I hope this will help someone else out there.
Yesterday I downloaded new HoloEverywhere library.
Currently, I have problem with tab navigation after screen rotation.
My Home Activity:
public class MainActivity extends Activity implements TabListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setUpTabs();
}
private void setUpTabs() {
String[] titles = {
"First", "Second",
};
ActionBar supportActionBar = getSupportActionBar();
for (int i = 0; i < titles.length; i++) {
ActionBar.Tab tab = supportActionBar.newTab();
tab.setText(titles[i]);
tab.setTag(MyFragment.TAG);
tab.setTabListener(this);
supportActionBar.addTab(tab, false);
}
supportActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
supportActionBar.setSelectedNavigationItem(0);
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction fragmentTransaction) {
final String fragmentTag = tab.getTag().toString();
Fragment fragment = getSupportFragmentManager().findFragmentByTag(fragmentTag);
if (fragment == null) {
fragment = new MyFragment();
fragmentTransaction.add(android.R.id.content, fragment, fragmentTag);
} else {
fragmentTransaction.attach(fragment);
}
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction fragmentTransaction) {
Fragment fragment = getSupportFragmentManager().findFragmentByTag((String) tab.getTag());
if (fragment != null) {
fragmentTransaction.detach(fragment);
}
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction fragmentTransaction) {
}
}
And my Fragment class.
public class MyFragment extends Fragment {
public static final String TAG = MyFragment.class.getCanonicalName();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = new View(getActivity());
view.setBackgroundColor(Color.BLACK);
return view;
}
}
When I rotate the screen fragment not displaying. It displays when i select tab (which is not currently selected) manually.
I just solve the problem.
I post my code here and see if those can help you :D
if (savedInstanceState == null){
TabHomeFragment homeFragment = new TabHomeFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.container, homeFragment, "home_fragment").commit();
}else{
TabHomeFragment homeFragment = (TabHomeFragment) getSupportFragmentManager().findFragmentByTag("home_fragment");
}
Those code are located in OnCreate method.
When the Device rotate and Ortiention change, the fragment will recreate again. So add a if clase to check if there is already one here.
But I am using normal Fragment in Android. Hope it can help you a little.
I using sherlock bar (http://actionbarsherlock.com/). There is code of TabListener:
public class TabListener implements ActionBar.TabListener {
private String mTag;
private Fragment mFragment;
public TabListener(String tag) {
mTag = tag;
// Check to see if we already have a fragment for this tab, probably
// from a previously saved state. If so, deactivate it, because our
// initial state is that a tab isn't shown.
mFragment = getSupportFragmentManager().findFragmentByTag(mTag);
if (mFragment != null && !mFragment.isDetached()) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.detach(mFragment);
ft.commit();
}
}
public void onTabSelected(Tab tab) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (mFragment == null) {
if((tab.getPosition()==0) || (tab.getPosition()==1)) {
mFragment = new MainListFragment(ctx, ONLINE_TYPE);
} else {
mFragment = new SimpleFragmentf();
}
ft.add(com.lib.reader.R.id.root2, mFragment, mTag);
ft.addToBackStack(null);
ft.commit();
} else {
ft.attach(mFragment);
ft.commit();
}
}
public void onTabUnselected(Tab tab) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.detach(mFragment);
ft.commit();
}
#Override
public void onTabReselected(Tab tab) {
//To change body of implemented methods use File | Settings | File Templates.
}
}
There is part of View's code:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
LinearLayout v = (LinearLayout)inflater.inflate(R.layout.main, container, false);
v.addView(pager);
return v;
}
All work, but when I selected any tab in second time - I get error:
The specified child already has a parent. You must call removeView() on the child's parent first.
I think, that error in attach function, but how solve this?
You can not add a view to a viewpager like that. Check the demos of Jake Wharton for proper implementation of viewpagers for example.
https://github.com/JakeWharton/Android-ViewPagerIndicator