How to update my fragment? - android

I'm passing data from fragment to another fragment with a bundle, but I have a map and I want to update this fragment and non replace it...this is the code in the MainActivity:
Mapped map = new Mapped();
final Bundle bundle = new Bundle();
bundle.putString("Position",one);
bundle.putString("ID",two);
map.setArguments(bundle);
getSupportFragmentManager().beginTransaction().replace(R.id.mapView, map).commit();
and every time it delete the old fragment...How can I do to update it and not replace?
EDIT: this is the code of MainActivity:
public class MainActivity extends FragmentActivity implements Connection.SendMessage
{
ViewPager Tab;
TabPagerAdapter TabAdapter;
ActionBar actionBar;
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TabAdapter = new TabPagerAdapter(getSupportFragmentManager());
Tab = (ViewPager)findViewById(R.id.pager);
Tab.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener()
{
public void onPageSelected(int position)
{
actionBar = getActionBar();
actionBar.setSelectedNavigationItem(position);
}
});
Tab.setAdapter(TabAdapter);
actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.TabListener tabListener = new ActionBar.TabListener()
{
public void onTabReselected(android.app.ActionBar.Tab tab,FragmentTransaction ft)
{
}
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft)
{
Tab.setCurrentItem(tab.getPosition());
}
public void onTabUnselected(android.app.ActionBar.Tab tab,FragmentTransaction ft)
{
}};
actionBar.addTab(actionBar.newTab().setText("Connessione").setTabListener(tabListener));
actionBar.addTab(actionBar.newTab().setText("Mappa").setTabListener(tabListener));
}
public void send(String one, String two)
{
Mapped map = new Mapped();
final Bundle bundle = new Bundle();
bundle.putString("Position",one);
bundle.putString("ID",two);
map.setArguments(bundle);
getSupportFragmentManager().beginTransaction().replace(R.id.mapView, map).commit();
}
}

The communication between fragments must be done through the parent FragmentActivity like documentation says:
Often you will want one Fragment to communicate with another, for example to change the content based on a user event. All Fragment-to-Fragment communication is done through the associated Activity. Two Fragments should never communicate directly.
The problem is how to refresh something in the ui. If you call replace to make the back navigation, in the OnCreateView of the new fragment you get the value of the parent activity. If you use the popbackstack to navigate backwards you should implement an interface.if you show the two fragments together, only use the fragment activity to refresh data.You can chage this:
getSupportFragmentManager().beginTransaction().replace(R.id.mapView, map).commit();
for this:
MyFragment myFragment = (MyFragment) getFragmentManager().findFragmentById(R.id.test_fragment);
if(myFragment!=null){
myFragment.update();
}
I'll give you some links to talk about this:
http://developer.android.com/training/basics/fragments/communicating.html
https://stackoverflow.com/a/9977370/944630
How to pass data between fragments
hope you help

You could try this solution:
add Mapped with a tag myMap:
Mapped map = new Mapped();
getSupportFragmentManager().beginTransaction().add(R.id.mapView, map, "myMap").commit();
make a method in Mapped, named addData(Bundle data), then you can pass data by this method when you need.
after data added to Mapped, then refresh it by:
Fragment mapFrg = getSupportFragmentManager().findFragmentByTag("myMap");
getSupportFragmentManager().beginTransaction().detach(mapFrg);
getSupportFragmentManager().beginTransaction().atach(mapFrg).commit();
in Mappde, in onCreateView(), each time check MapView is null or not
Hope this help.

Related

Calling method of Fragment has no effect

And first of all thank you anyway for your help.
This is a difficult question for me.
Please I have an activity that contains 5 Fragments; on user interaction the Fragments get swapped.
I am using the ACL.
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
stackArray =new ArrayList<Integer>();
favQ =new ArrayList<Stock>();
tablet=true;
mBound = false;
fragmentActivity = this;
setContentView(R.layout.splashmain);
splashfragment =new splashFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.splashview,splashfragment);
fragmentTransaction.commit();
/*
other stuff....
*/
fragmentlista = new listafragment();
fragmentfavourites= new favouritesFragment() ;
worstbest = new WorstBest();
searchfragment = new searchFragment();
/*
other stuff....
*/
lt = mService.ritira();
worst=mService.ritiraWorst();
best=mService.ritiraBest();
favQ.clear();
favQ.addAll(mService.ritiraFav());
fragmentlista.prendiLista(lt);
worstbest.prendiListaWorst(worst);
worstbest.prendiListaBest(best);
if(favQ.size()>0)fragmentfavourites.prendiLista(favQ);
// --->>>>HERE THE SAME METHOD enableAll() WORKS!!! <---
// --->>>>HERE THE SAME METHOD enableAll() WORKS!!! <---
splashfragment.enableAll();
// --->>>>HERE THE SAME METHOD enableAll() WORKS!!! <---
// --->>>>HERE THE SAME METHOD enableAll() WORKS!!! <---
/*
other stuff....
*/
}
//Method invoked to setup the configuration of the screen is layoutSchermo(int conf)
public static void layoutSchermo(int conf){
//Check if it is a Tablet in Landscape mode or not
//if it finds v2 than we are on a LARGE screen, then we check ORIENTATIO
fragmentActivity.setContentView(R.layout.main);
View v2 =(View)fragmentActivity.findViewById(R.id.view2);
if(v2==null
&
fragmentActivity.getResources().getConfiguration().orientation==
Configuration.ORIENTATION_PORTRAIT)
tablet=false;
//Calls the screen configuration LIST
if(conf==LIST){
fragmentActivity.setContentView(R.layout.main);
FragmentManager fragmentManager = fragmentActivity.getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.remove(splashfragment);
fragmentTransaction.commit();
fragmentManager.executePendingTransactions();
//Remove old Fragment splashfragment
//At this point I expect the fragment splashfragment is destroyed
//OR NOT???
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right);
if(!tablet){fragmentTransaction.replace(R.id.view1, fragmentlista);}
if(tablet){
fragmentTransaction.replace(R.id.view1, splashfragment);
fragmentTransaction.replace(R.id.view2,fragmentlista );
} fragmentTransaction.addToBackStack(null);
stack= fragmentTransaction.commit();
stackArray.add(stack);
//Brand new fragments added
// --->>>>HERE THE SAME METHOD enableAll() NOT WORKING!!! <---
// --->>>>HERE THE SAME METHOD enableAll() NOT WORKING!!! <---
splashfragment.enableAll();
}
------------
So basically what happens and where the problem is:
The problem is in the method
layoutSchermo(int conf)
In the method layoutSchermo(int conf),
I detach a Fragment (splashfragment) and reattach it (together with another one).
It is not clear to me if when I call
remove(splashfragment)
Actually the Fragment is destroyed or not?
Additionally, whenever the Fragment freshly added is a new one or the old one,
why the call to
splashfragment.enableAll();
Has no effect ?
I expect it to work either it is the new or old Fragment!
Please enlighten me!
Thanks
maurizio
----------
EDIT EDIT EDIT EDIT EDIT EDIT
Here is the code of the fragment (I do not think it helps much)
ublic class splashFragment extends Fragment {
public View v;
public Button buttonfav;
public Button buttonBW;
public Button buttonSe;
public Button buttonLi;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
v=inflater.inflate(R.layout.splashnew, container, false);
RelativeLayout box1 = (RelativeLayout)v.findViewById(R.id.box1);
//box1.setBackgroundColor(Color.BLUE);
buttonfav=(Button)v.findViewById(R.id.heart);
buttonBW=(Button)v.findViewById(R.id.star);
buttonSe=(Button)v.findViewById(R.id.search);
buttonLi=(Button)v.findViewById(R.id.lista);
buttonfav.setBackgroundResource(R.drawable.hearth_gray_tansp);
buttonBW.setBackgroundResource(R.drawable.star_gray_trans);
buttonSe.setBackgroundResource(R.drawable.search_gray_transp);
buttonLi.setBackgroundResource(R.drawable.list_gray_trans);
buttonfav.setEnabled(false);
buttonBW.setEnabled(false);
buttonSe.setEnabled(false);
buttonLi.setEnabled(false);
buttonfav.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
Quotes.layoutSchermo(Quotes.FAVOURITES);
}});
buttonBW.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
Quotes.layoutSchermo(Quotes.BESTWORST);
}});
buttonSe.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
Quotes.layoutSchermo(Quotes.SEARCH);
}});
buttonLi.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
Quotes.layoutSchermo(Quotes.LIST);
}});
return v;
}
#Override
public void onSaveInstanceState(Bundle outState) { }
public void enableAll(){
buttonfav.setEnabled(true);
buttonfav.setBackgroundResource(R.drawable.hearth);
buttonBW.setEnabled(true);
buttonBW.setBackgroundResource(R.drawable.star);
buttonLi.setEnabled(true);
buttonLi.setBackgroundResource(R.drawable.list);
buttonSe.setEnabled(true);
buttonSe.setBackgroundResource(R.drawable.search);
}
}
When exactly fragments are destroyed can't be known with any certainty. All you know is that it's called after onStop() and before onDetach().
As for your splashFragment.enableAll(), you haven't showed us what that method is so how can we know why it isn't working... Also, you haven't showed us the more general context of this layoutSchermo method. I say this because I suspect you're doing this all wrong. You have a static method, referencing activities somehow...(not clear how that's happening), setting the contentview on that activity reference..the whole thing just sets off some red flags.
SplashFragment.enableAll is most likely something that needs to be called inside of that Fragment's onAttach or onResume, but again it's impossible to know without some explanation from you.
EDIT
Ok, so I think you're going about this incorrectly. What you are effectively trying to accomplish is to "configure" your Fragment in a certain way (depending on some state) when you display it again. The issue here is that you don't know exactly when the View hierarchy of a Fragment is inflated or when exactly it's attached to the Activity, etc. In other words, trying to call methods that affect the UI of your fragment simply on the basis of having a reference to the object of a Fragment is a mistake. You need to hook into the lifecycle of your Fragment and do things "the correct way."
Here's what I recommend: create a static constructor for your Fragment that makes it easy to create the properly configured Fragment that you want. Here's what that might look like:
public class SplashFragment extends Fragment {
public static SplashFragment newInstance(Bundle bundle) {
SplashFragment splashFragment = new SplashFragment()
splashFragment.setArguments(bundle);
return splashFragment;
}
// or alternatively
public static SplashFragment newInstance(int favResource, int bwResource, int liResource, int seResource,
boolean favEnabled, boolean bwEnabled, boolean liEnabled, boolean seEnabled) {
SplashFragment splashFragment = new SplashFragment()
Bundle bundle = new Bundle();
bundle.putInt("fav_res", favResource);
bundle.putInt("bw_res", bwResource);
bundle.putInt"li_res", liResource);
bundle.putInt("se_res", seResource);
bundle.putBoolean("fav_enabled", favEnabled);
//...And so on
splashFragment.setArguments(bundle);
return splashFragment;
}
//Then....
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//setup your view as normal...then
buttonFav.setBackgroundResource(getArguments().getInt("fave_res"));
//.....etc
}
}
Now if you really need to be able to manipulate a Fragment without creating a new instance, then the only way I can think to do this is to add the fragment with a tag, as in the
replace(int containerViewId, Fragment fragment, String tag)
and
add (Fragment fragment, String tag)
varieties.
Then, later you can try to ask the fragment manager to find those fragment for you, i.e.
SplashFragment splashFragment = (SplashFragment) getFragmentManager().findFragmentByTag("some tag here");
Check that it's not null and then call your method on it...

Calling addToBackStack() on a Fragment that is added via ActionBar

So basically, I have a simple Fragment that uses an AsyncTask to download a bitmap from a URL and then displays it inside an ImageView.
This is the Fragment's code:
public class TestFragment extends Fragment {
public TestFragment () {}
private String pictureUrl = "https://fbcdn-sphotos-d-a.akamaihd.net/hphotos-ak-prn1/532762_10150739418088211_1186720820_n.jpg";
private TextView sampleText;
private ImageView sampleImage;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_view, container, false);
String textToSet = "Section Three";
sampleText = (TextView) root.findViewById(R.id.textView1);
sampleText.setText(textToSet);
sampleImage = (ImageView) root.findViewById(R.id.imageView1);
DownloadImageTask task = new DownloadImageTask(pictureUrl, root, sampleImage.getId());
task.execute();
return root;
}
}
This same fragment is displayed three times, one per each tab inside an ActionBar. The problem I am having is that each time the user selects a different tab, the previous one is destroyed, and when the user navigates back to the original tab, the content needs to be redownloaded. I want to make sure that the least amount of resources are consumed (saving both Data, and valuable overHead processing).
Here is the Fragment Container activity:
public class LayoutContainer extends Activity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout_container);
if (savedInstanceState != null) {
return;
}
final ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
Tab tab = actionBar.newTab()
.setText(R.string.title_section1)
.setTabListener(new TabListener<TestFragment>(
this, "one", TestFragment.class));
actionBar.addTab(tab);
tab = actionBar.newTab()
.setText(R.string.title_section2)
.setTabListener(new TabListener<TestFragment>(
this, "two", TestFragment.class));
actionBar.addTab(tab);
tab = actionBar.newTab()
.setText(R.string.title_section3)
.setTabListener(new TabListener<TestFragment>(
this, "three", TestFragment.class));
actionBar.addTab(tab);
}
public static class TabListener<T extends Fragment> implements ActionBar.TabListener {
private Fragment mFragment;
private final Activity mActivity;
private final String mTag;
private final Class<T> mClass;
public TabListener(Activity activity, String tag, Class<T> clz) {
mActivity = activity;
mTag = tag;
mClass = clz;
}
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if (mFragment == null) {
mFragment = Fragment.instantiate(mActivity, mClass.getName());
}
ft.replace(android.R.id.content, mFragment, mTag);
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft.addToBackStack(null);
ft.commit();
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
}
I currently have it set to call ft.replace() on the OnTabSelected() method, but I had previously tried with ft.add(). Same goes for my current ft.addToBackStack() call inside OnTabUnselected(), I initially had it set for simply calling ft.detach() but that was doing the same thing I described as being my problem. It loads that way, but it has to download the image every single time.
Now, I have tried calling addToBackStack() but the app force closes stating the FragmentTransaction CANNOT be added to the back stack. I also tried in the past to cache the image offline, to avoid data consumption, the app would still need to parse the image from a local URI and use processing power.
Any ideas I can try?
FYI, Yes, I know that the same fragment is loading the same image on all three tabs. This is merely a test project that is intended to help me better understand the lifecycle of Fragments.
So After a lot of reading I realized that the important code block is not done on your TabListener but in fact done on each Fragment that you use.
The fragment WILL be detached when navigating away from it, but that does not mean I cannot retain it state using onSaveInstanceState(...).
Here is simple implementation of what was needed to retain the Fragment's state after it being detached.
public class TimelineFragment extends ListFragment{
...
public TimelineFragment () {}
public void onSaveInstanceState(Bundle outState){
getActivity().getFragmentManager().putFragment(outState, TAG, this);
}
public void onRetoreInstanceState(Bundle inState){
getActivity().getFragmentManager().getFragment(inState, TAG);
}
...
}
We are saving the Fragment by calling putFragment(), and restoring it by calling getFragment(). Pretty darn simple, and I never thought of it.
What I do when a user need an image from the web I first check if I've cached it or the image is at the SD. If the image can't be found at SD or cache I download the image, store it to SD and add it to the cache.
It usually goes something like this:
Bitmap cacheBitmap = Database.getImageFromMemory(exercise.image, getActivity());
if(cacheBitmap != null) {
image.setImageBitmap(cacheBitmap);
progressBar.setVisibility(View.GONE);
}
Bitmap localBitmap = ImageDownloader.decodeSampledBitmap(exercise.image, width, height, getActivity());
if(localBitmap != null) {
image.setImageBitmap(localBitmap);
progressBar.setVisibility(View.GONE);
Database.addBitmapToMemoryCache(exercise.image, localBitmap, getActivity());
} else {
ImageDownloader imageDownloader = new ImageDownloader(this.getActivity().getApplicationContext(), image, progressBar, width, height);
imageDownloader.execute(exercise.image);
}
Now this is not all the code but it gives you an idea about how to handle it. If you need more code feel free to ask, but it seems like you are on the right track.

Android - Viewpager and fragments, methods not working

I have a ViewPager with two Fragments which I instantiate in onCreate of my FragmentActivity.
private List<Fragment> fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this,Frag_1.class.getName()));
fragments.add(Fragment.instantiate(this,Frag_2.class.getName()));
this.vPagerAdapter = new Adapt(super.getSupportFragmentManager(),fragments);
vPager = (ViewPager) super.findViewById(R.id.pager);
vPager.setAdapter(vPagerAdapter);
My second Fragment has a method inside that I call to update my ListView - refreshList():
public class Frag_2 extends Fragment {
private ListView list;
private ArrayList<data> data;
private boolean firstCreation=true;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setRetainInstance(false);
}
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout, container, false);
list = (ListView) view.findViewById(R.id.lst);
//this.setRetainInstance(true);
return view;
}
public void refreshList(ArrayList <data> data){
if(data!=null){
ArrayAdapter<data> adapter = new Item_data_adapter(getActivity(),data);
list.setAdapter(adapter);}
}
}
Called from my FragmentActivity
//Something
Frag_2 fr = (Frag_2) vPagerAdapter.getItem(1);
if (fr.getView() != null) {
fr.refreshList(data);
}
It works fine until I change the orientation of the screen. Correct me if I'm wrong, but I was searching for hours and I didn't find a solution or a good explanation, the FragmentActivity is created only one time and the Fragments are attached to it but the Fragments recreate on configuration changes.
Now, when the orientation changes I don't get the View from onCreateso when I try to get the View from the Fragment it returns null and my refreshList() method isn't called. How can I fix this?
I fixed the problem this way:
In the onCreate of the FragmentActivity
if(savedInstanceState!=null){
frag1 = (frag_1) getSupportFragmentManager().getFragment(savedInstanceState, frag_1.class.getName());
frag2 = (frag_2) getSupportFragmentManager().getFragment(savedInstanceState, frag_2.class.getName());
}
else{
frag1 = (frag_1) Fragment.instantiate(this,frag_1.class.getName());
frag2 = (frag_2) Fragment.instantiate(this,frag_2.class.getName());
}
fragments.add(frag1);
fragments.add(frag2);
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getSupportFragmentManager().putFragment(outState, frag_1.class.getName(), frag1);
getSupportFragmentManager().putFragment(outState, frag_2.class.getName(), frag2);
}
Maybe it's not the best solution in the universe, but it looks like it works...
When u want to refresh the List do something like this :
public void setView() {
Frag_2 fr = (Frag_2) vPagerAdapter.getItem(1);
fragmentManager.beginTransaction().detach(fr).commit();
fragmentManager.beginTransaction().attach(fr).commit();
}
If you are using a dynamic fragment, you need to test first to prevent creating a second instance of a fragment.
To test whether the system is re-creating the activity, check whether the Bundle argument passed to your activity’s
onCreate() is null.
If it is non-null, the system is re-creating the activity. In this case, the activity automatically re-instantiates existing
fragments.
If it's null you can safely instantiate your dynamic fragment. For example:
public void onCreate(Bundle savedInstanceState) {
// ...
if (savedInstanceState != null) {
FragmentManager fragmentManager = getFragmentManager()
// Or: FragmentManager fragmentManager = getSupportFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
}
}
The Fragment class supports the onSaveInstanceState(Bundle) method (but not the onRestoreInstanceState() method) in much the same way as the Activity class.
The default implementation saves the state of all the fragment’s views that have IDs.
You can override this method to store additional fragment state information.
If the system is re-creating the fragment from a previous saved state, it provides a reference to the Bundle containing that state to the onCreate(), onCreateView(), and onActivityCreated() methods; otherwise, the
argument is set to null.
If you want a detailed info, here's a good talk by Ken Jones of Marakana

AndroidBar Tab on Fragments/EU4You_6

How are you Mark? I have a question regarding the program you have written in the book "CommonsWare The Busy Coders Guide to Android Development".
I am talking about Fragments/EU4You_6 on Chapter 28 page 377. I want to expand this by adding an ActionBar Tab.
Without making any changes on your original program, what I did was that I copied EU4You.java to EU5You.java, which represents Tab2. EU4You.java will be the default Tab1.
The following are my approach:
I created a java program called EU4Main.java, which represents the MAIN program instead of the original EU4You. Of course, I changed the manifest to android:name=".EU4Main"
The EU4Main.java is where I put the ActionBar Tab. The trouble is and making me frustrated is in the TabListener setup. I have this setup .setTabListener(new TabListener(EU4You.class))); , which passes a Class. It did not work. Do you have any advise on this instead of passing a class?
Also, the onTabSelected on the code snippets below, did not work properly. If I clicked Tab2, it will show the list for Tab2 but it will automatically returns to Tab1. It won't stay at Tab2. I don't know why?
I would appreciate of any help if you can provide a better and working approach for both .setTabListener and onTabSelected
I have also extended FragmentActivity to EU4Main or shall I just use extends Activity instead?
Thanks in advance.
I have included EU4Main below with incorrect and incomplete codes( I just can't make it work...)
public class EU4Main extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActionBar bar = getActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
bar.addTab(bar
.newTab()
.setText("Countries")
.setTabListener(new TabListener(EU4You.class)));
bar.addTab(bar
.newTab()
.setText("Artists")
.setTabListener(new TabListener(EU5You.class)));
.
.
.
private class TabListener implements ActionBar.TabListener {
public TabListener(Activity activity) {
mActivity = activity;
}
public void onTabSelected(ActionBar.Tab tab, android.app.FragmentTransaction unused) {
if (tab.getPosition() == 0) {
Intent intent = new Intent();
String packageName = "com.commonsware.android.eu4you";
String className = "com.commonsware.android.eu4you.EU4You";
intent.setClassName(packageName, className);
startActivity(intent);
else{
Intent intent = new Intent();
String packageName = "com.commonsware.android.eu4you";
String className = "com.commonsware.android.eu4you.EU5You";
intent.setClassName(packageName, className);
startActivity(intent);
}
}
public void onTabUnselected(ActionBar.Tab tab, android.app.FragmentTransaction unused) {
FragmentManager fragMgr = getSupportFragmentManager();
FragmentTransaction xaction=fragMgr.beginTransaction();
}
public void onTabReselected(ActionBar.Tab tab,
android.app.FragmentTransaction xaction) {
// NO-OP
}
}
Your TabListener has a constructor that takes an Activity. You are calling the constructor with a Class. A Class is not an Activity.
Furthermore, you are using two separate TabListener instances, but your code for TabListener does not do anything different based upon the supplied parameter.
And, you are starting activities when tabs are selected, which is not going to be especially useful.
When a TabListener is called with onTabSelected(), it needs to affect a change to the existing UI. Starting a whole new activity does not constitute a change to the existing UI. Rather, TabListener should do something like:
execute a FragmentTransaction
set a fresh ListAdapter in the ListFragment managed by the tabs
update ordinary widgets in the current activity
etc.

Transaction between fragments only inside one ActionBar Tab

I have an app with three tabs (ActionBar Tabs), each one with one fragment at a time.
TabListener
TabsActivity
Tab1 -> ListFragment1 -> ListFragment2 -> Fragment3
Tab2 -> Tab2Fragment
Tab3 -> Tab3Fragment
The problem is when I create the FragmentTransaction (inside OnListItemClicked) from ListFragment1 to ListFragment2, the fragments inside the other tabs also change to ListFragment2.
In the end, I want to change fragments only inside on tab and preserve the state of the other tabs.
I'm already saving the state (OnSavedInstance()).
Do you know what I'm missing here?
Some of the code:
public class TabsActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tabs);
// setup Action Bar for tabs
ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// instantiate fragment for the tab
Fragment networksFragment = new NetworksFragment();
// add a new tab and set its title text and tab listener
actionBar.addTab(actionBar.newTab().setText("Tab1")
.setTabListener(new TabsListener(ListFragment1)));
// instantiate fragment for the tab
Fragment historyFragment = new HistoryFragment();
// add a new tab and set its title text and tab listener
actionBar.addTab(actionBar.newTab().setText("Tab2")
.setTabListener(new TabsListener(Tab2Fragment)));
// instantiate fragment for the tab
Fragment settingsFragment = new SettingsFragment();
// add a new tab and set its title text and tab listener
actionBar.addTab(actionBar.newTab().setText("Tab3")
.setTabListener(new TabsListener(Tab3Fragment)));
}
}
public class TabsListener implements ActionBar.TabListener {
private Fragment frag;
// Called to create an instance of the listener when adding a new tab
public TabsListener(Fragment networksFragment) {
frag = networksFragment;
}
#Override
public void onTabReselected(Tab arg0, FragmentTransaction arg1) {
// TODO Auto-generated method stub
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
ft.add(R.id.fragment_container, frag, null);
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft.remove(frag);
}
}
public class ListFragment1 extends ListFragment {
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
getListView().setItemChecked(position, true);
ListFragment2 fragment2 = ListFragment2.newInstance(position);
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.fragment_container, fragment2);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(null);
ft.commit();
}
}
You're not missing anything (or I'm missing it too).
I searched long and hard for a way to do this "properly" but I couldn't find anything. What I ended up doing is writing my own backstack logic.
Unfortunately my employer owns my code so I can't share any of that verbatim, but here was my approach:
Create an enum with one entry for each of your tabs. Let's call it TabType.
Now create an instance variable tabStacks of type HashMap<TabType, Stack<String>>. Now you can instantiate one stack for each tab - each stack is a list of tags, as specified by Fragment.getTag(). This way you don't have to worry about storing references to Fragments and whether they're going to disappear on you when you rotate the device. Any time you need a reference to a Fragment, grab the right tag off the stack and use FragmentManager.findFragmentByTag().
Now whenever you want to push a Fragment onto a tab, generate a new tag (I used UUID.randomUUID().toString()) and use it in your call to FragmentTransaction.add(). Then push the tag on top of the stack for the currently displayed tab.
Be careful: when you want to push a new fragment on top of an old one, don't remove() the old one, since the FragmentManager will consider it gone and it will be cleaned up. Be sure to detach() it, and then attach() it later. Only use remove() when you're permanently popping a Fragment, and only use add() the first time you want to show it.
Then, you'll have to add some relatively simple logic to your TabListener. When a tab is unselected, simply peek() at its stack and detatch() the associated Fragment. When a tab is selected, peek() at the top of that stack and attach() that fragment.
Lastly, you'll have to deal with Activity lifecycle quirks (like orientation changes). Persist your Map of Stacks as well as the currently selected tab, and unpack it again in your onCreate(). (You don't get this packing and unpacking for free, but it's pretty easy to do.) Luckily your TabType enum is Serializable so it should be trivial to put into a Bundle.

Categories

Resources