Android Fragments not calls a Backstack - android

I have a class "bancoActivity" that extends Fragment implements ActionBar.TabListener that calls another class "pagamentos" extends Fragment implements ActionBar.TabListener.
When I'm in class "pagamentos" and click on the physical button "back" nothing happens, and when i click again the application finish.
I leave there my code so that your can analyze.
Obrigado.
part of the bancoActivity:
#Override
public void onItemClick(AdapterView<?> customviewadapter, View view, int position, long id) {
listViewItem item = items.get(position);
String Titulo = item.Title;
if(Titulo.equals("Pagamentos")) {
FragmentManager fragmentManager2 = getFragmentManager();
FragmentTransaction fragmentTransaction2 = fragmentManager2.beginTransaction();
pagamentos fragment2 = new pagamentos();
fragmentTransaction2.hide(bancoActivity.this);
fragmentTransaction2.add(android.R.id.content, fragment2);
fragmentTransaction2.addToBackStack("banco");
fragmentTransaction2.commit();
}
}
part of the pagamentos:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setContentView(R.layout.pagamentos);
FragmentManager fm = getFragmentManager();
the two activities extends and implements:
public class pagamentos extends Fragment implements ActionBar.TabListener{
public class bancoActivity extends Fragment implements ActionBar.TabListener

I'm a newbie too, but I'll take a stab at it...
I think you need to instantiate both fragments from the activity that holds them, and use 1 fragment manager to do the switching. Its hard for me to tell without your whole code, but I have a similar setup working, here is how:
public class MainActivity extends Activity implements ActionBar.TabListener {
public static final int SEARCH_FRAG = 1;
public static final int MAP_FRAG = 2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragMan = (FragmentManager) getFragmentManager();
mapFrag = ExtendedMapFragment.newInstance();
searchFrag = SearchFragment.newInstance();
if (mapShown == false) {
swapFrags(SEARCH_FRAG);
} else {
swapFrags(MAP_FRAG);
}
ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.setDisplayShowTitleEnabled(true);
searchTab = actionBar.newTab()
.setText(R.string.search_tab_label)
.setTabListener(this);
actionBar.addTab(searchTab);
mapTab = actionBar.newTab()
.setText(R.string.map_tab_label)
.setTabListener(this);
actionBar.addTab(mapTab);
if (savedInstanceState != null) {
actionBar.setSelectedNavigationItem(savedInstanceState.getInt("currentTab"));
}
actionBar = null;
}
public void swapFrags(int whatFrag) {
if (whatFrag == SEARCH_FRAG) {
//switch to frag 1, ie searchFrag
FragmentTransaction trans = fragMan.beginTransaction();
trans.replace(R.id.map, searchFrag);
mapShown = false;
trans.addToBackStack(null);
trans.commit();
}
if (whatFrag == MAP_FRAG) {
//switch to frag 2, ie mapFrag
if (lbServ != null) {
update.autoCenter(lbServ.getCurrentLatLng());
}
FragmentTransaction trans = fragMan.beginTransaction();
trans.replace(R.id.map, mapFrag);
mapShown = true;
trans.addToBackStack(null);
trans.commit();
}
}
}
The swapFrags() method is also called from my onTabSelected callback. I think, since I only have one fragment manager, and the same manager is calling all the addToBackStack() methods, it is more organized. When I open the app, select a new tab, then hit physical back key, it goes back to the previous tab, which is what you are after, no?
One thing I found difficult to learn with fragments is that all calls, keys, etc. go to the activity first. Push a button on the fragment, the activity that holds it gets the callback first (whether it uses it or not), then if there is a listener in the fragment it will get the call also, but fragments can't do really anything outside themselves, and fragment transactions involve objects outside the fragment.
I suppose you could set up an interface between fragment and activity, with a method like swapFrags() in the activity, where the fragment can ask to be swapped, that should do it too I think.

Related

Fragment disappears using SlidingTabLayout

I am using a SlidingTabLayout to implement the sliding tabs. The problem is, when backing from a fragment to the tab fragment, it disappears.
I am going to show the application flow to make things more clear.
First, I call an Activity whithin a Fragment:
public class ScreenSlidePageFragment extends Fragment{
...
public View onCreateView(args...){
...
gridView.setOnItemClickListener(new OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id){
switch(position){
case 0:
Intent intent = new Intent(getActivity(), FrequencyActivity.class);
startActivity(intent);
break;
}
}
}
...
}
}
The code of the FrequencyActivity is below:
public class FrequencyActivity extends AppCompatActivity{
...
protected void onCreate(Bundle savedInstance){
...
toolbar.setNavigationOnClickListener(new OnClickListener(){
#Override
public void onClick(View view){
onBackPressed();
}
});
}
final FragmentManager fm = getSupportFragmentManager();
Fragment fragment = Fragment.instantiate(getBaseContext(), "com.example.Fragment.FragmentFrequency");
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.home, fragment, "FragFrequency");
fragmentTransaction.addToBackStack("frequency");
fragmentTransaction.commit();
fm.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
finish(); //When there is no back stack, finish the activity
} else {
//Testing purpose
int top = fm.getBackStackEntryCount();
Log.d("BACKSTACK", "Backstack count: " + String.valueOf(top));
Log.d("BACKSTACK", "Backstack name: " + fm.getBackStackEntryAt(top - 1).getName());
}
}
});
}
The FragmentFrequency is the one which contains the SlidingTabLayout, the code can be seen below:
public class FragmentFrequency extends Fragment{
...
//Creates ViewPager adapter
adapter = new ViewPagerAdapter(activity.getSupportFragmentManager(), titles, numOfTabs);
//ViewPager
viewPager = (ViewPager) layout.findViewById(R.id.pager);
viewPager.setAdapter(adapter);
//SlidingTabLayout code
...
}
And finally, the ViewPagerAdapter which loads the Fragments of the tabs
public class ViewPagerAdapter extends FragmentStatePagerAdapter{
...
#Override
public Fragment getItem(int position){
if(position == 0)
return new FragmentTab1();
else
return new FragmentTab2();
}
...
}
For example when the first tab is selected, the FragmentTab1 is loaded, which contains:
Fragment f = Fragment.instantiate(getActivity(), "com.example.Fragment.FragmentLaunchingFrequency");
FragmentTransaction tx = getActivity().getSupportFragmentManager().beginTransaction();
tx.replace(R.id.home, f, "FragLaunchingFrequency");
tx.addToBackStack("launchingfrequency");
tx.commit();
The problem is, when the back action is done, the FrequencyActivity loses the reference of the Fragment and it shows a blank. Also, the sliding tabs stop working properly.
Does anyone know how to fix this? I am really out of alternatives.
Thanks
I think you have 2 major questions in your post. Perhaps make another post for the other question.
For now, I can address your question "The problem is, when the back action is done, the FrequencyActivity loses the reference of the Fragment and it shows a blank".
Your code:
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.home, fragment, "FragFrequency");
fragmentTransaction.addToBackStack("frequency");
fragmentTransaction.commit();
fm.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
...
Notes:
You cannot call addToBackStack() and use OnBackStackChangedListener() in the same FragmentManager. This seems complicated.
Code suggestions:
Remove the use of addOnBackStackChangedListener() and see what happens.
Specific code suggestion:
fm.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
finish(); //When there is no back stack, finish the activity
} else {
// Call FragmentManager.findFragmentByTag(String tag)
// Call FragmentTransaction.show() after getting the Fragment.
}
}
});
Note: Notice the else block manages the Fragment instead of depending on the BackStack (addToBackStack method).

Android - getActivity() returning null from dialogFragment

I am reasonably new to fragments, so I apologise if this is quite basic.
My app contains a ViewPager to swipe between several different tabs with ListViews. On clicking one particular ListView item, I would like to open a DialogFragment displaying some options. My intention is to have the DialogFragment deal with the click event and feed the selected dialog item back to the fragment that called it. I am doing this by providing a callback to MainActivity (which contains the ViewPager) and feeding that information down to the correct ViewPager fragment.
What I have noticed is that my DialogFragment is returning null on getActivity(), which I believe is because my DialogFragment is not correctly attached to my MainActivity. How can I achieve this? I find it a little harder to navigate through this issue considering I am new to callbacks and most other examples dealing with fragments are for standard fragments, not those from ViewPager.
How can my dialog identify the correct activity? Should I be attaching the fragment to a FragmentManager? If so, should it be attached to the same FragmentManager as the ViewPager fragments?
This is my code, though I'm not sure how helpful it will be.
CalibrationFragment.java - Calling the DialogFragment. (Not sure if this is the correct way to do it.)
ModeDialogFragment modeDialog = ModeDialogFragment.newInstance(R.string.mode_calibration);
modeDialog.onCreateDialog(null);
ModeDialogFragment.java - My DialogFragment
public class ModeDialogFragment extends DialogFragment{
public static ModeDialogFragment newInstance(int title) {
ModeDialogFragment frag = new ModeDialogFragment();
Bundle args = new Bundle();
args.putInt("title", title);
frag.setArguments(args);
return frag;
}
public void onAttach(Activity activity){
super.onAttach(activity);
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
int title = getArguments().getInt("title");
final Context mContext = getActivity();
getActivity().getFragmentManager().beginTransaction().add(newInstance(R.string.mode_calibration), "my_fragment").commit();
// Trying to add my dialog to the MainActivity, not working.
final CharSequence[] modeItems = {
"Option 1", "Option 2", "Option 3"
};
final int checkedItem = 2; // Make final for now, resolve this later
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(getResources().getString(R.string.mode_calibration));
builder.setSingleChoiceItems(modeItems, checkedItem, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
// Handle the selection here
((MainActivity)getActivity()).updateModeData();
}
});
return(builder.create());
}
}
MainActivity.java
public class MainActivity extends FragmentActivity implements ActionBar.TabListener {
// Initialise variables
private Context mContext;
// [Others...]
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialisation
viewPager = (ViewPager) findViewById(R.id.pager);
actionBar = getActionBar();
mAdapter = new TabsPagerAdapter(getSupportFragmentManager());
}
public Fragment findFragmentByPosition(int position) {
int viewId = R.id.pager;
//FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
return getSupportFragmentManager().findFragmentByTag(
makeFragmentName(viewId, position));
}
// Needed to identify a fragment from ViewPager
private static String makeFragmentName(int viewId, int position)
{
return "android:switcher:" + viewId + ":" + position;
}
public void updateModeData(){
// Make call to the fragment to deal with the data
CalibrationFragment calFragment = (CalibrationFragment) findFragmentByPosition(3);
((CalibrationFragment) calFragment).doUpdateModeData();
}
}
Any help or guidance would be appreciated!
EDIT: The problem was indeed that I was not adding the fragment to a FragmentManager and calling it correctly. This is the code I used. My code still does not run through in its entirety, but for the purpose of this question, my DialogFragment can now access MainActivity.
CalibrationFragment.java - Calling DialogFragment
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ModeDialogFragment modeDialog = ModeDialogFragment.newInstance(R.string.mode_calibration);
String title = makeFragmentName(R.id.pager, 3); // makeFragmentName as specified in MainActivity.java
modeDialog.show(ft, title);
ModeDialogFragment.java is the same as before, but with the following line removed:
getActivity().getFragmentManager().beginTransaction().add(newInstance(R.string.mode_calibration), title).commit();
You shouldn't call onCreateDialog by yourself. Just call show method with FragmentTransaction:
FragmentTransaction ft = getFragmentManager().beginTransaction();
ModeDialogFragment modeDialog = ModeDialogFragment.newInstance(R.string.mode_calibration);
modeDialog.show(ft, null);
Right usage of DialogFragment is described here: http://developer.android.com/reference/android/app/DialogFragment.html

How to include the activity in fragment tabs ?

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();

Fragment onCreate called multiple times when reload the Fragment

Below is my code:
public class MyListFragmentActivity extends FragmentActivity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
System.out.println("DEBUG : MLFA onCreate");
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction().replace(fragmentID, new MyListFragment())
.replace(detailFragmentID, new MyDetailFragment()).commit();
}
}
#Override
protected void onRestart() {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
Fragment prevFrag = getSupportFragmentManager().findFragmentById(detailFragmentID);
if (prevFrag != null) {
fragmentTransaction.remove(prevFrag);
getSupportFragmentManager().beginTransaction().replace(detailFragmentID, new MyDetailFragment()).commitAllowingStateLoss();
} else {
getSupportFragmentManager().beginTransaction().replace(detailFragmentID, new MyDetailFragment()).commitAllowingStateLoss();
}
}
MyListFragment
public class MyListFragment extends Fragment{
//When we click on each item in list view call detail fragment to relad its layout
OnItemClickListener onItemClickListener = new OnItemClickListener() {
/** Getting the fragmenttransaction object, which can be used to add, remove or replace a fragment */
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
/** Getting the existing detailed fragment object, if it already exists.
* The fragment object is retrieved by its tag name
* */
Fragment prevFrag = getFragmentManager().findFragmentById(detailFragmentID);
/** Remove the existing detailed fragment object if it exists */
if (prevFrag != null) {
fragmentTransaction.remove(prevFrag);
MyDetailFragment mydetailFragment = new MyDetailFragment();
fragmentTransaction.replace(detailFragmentID, mydetailFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.show(getFragmentManager().findFragmentById(detailFragmentID));
fragmentTransaction.commit();
}
}
MyDetailFragment
public class MyDetailFragment extends Fragment{
onCreate() // on create being called multiple times ? why ?????????????
onCreateView()
}
When i click on my list item MyDetailFragment onCreate() is called only once, but when i tilt the device to portrait or landscape then MyDetailFragment onCreate() is called multiple times ?
Why so? What am i doing wrong here and how to fix this ?
Every time you change the orientation, it is as good as restarting the app. You need to handle changes appropriately like releasing the resources, acquiring them again, stopping any work you were doing and resuming them and so on.
You aren't doing anything wrong.

Tabs content is visible on top of another after screen rotation

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.

Categories

Resources