Fragment onCreate called multiple times when reload the Fragment - android

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.

Related

why the app crashes when I use onclicklistener with Fragment?

public class MainActivity extends AppCompatActivity {
Context context;
LinearLayout menuClcick,gallerClcik,eventsClick;
LayoutInflater inflater;`enter code here`
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//we don't need to set view, our fragment will handle it
setPointer();
//Fragment Manger
FragmentManager fm = getFragmentManager();
//create instance of Fragment Transaction to handle fragment replace and animation
FragmentTransaction ft=fm.beginTransaction();
int displayMode = getResources().getConfiguration().orientation;
Log.e("WTF", "onCreate: "+displayMode );
//choose which fragment to display according to screen orientation
if (displayMode==1) //portrait
{
// that's the Fragment that I use to display a layout in the portrait and other layout in the landscape//
//create instance of our portrait fragment
Fragment1 f1=new Fragment1();
//change content of the screen to our new fragment
ft.replace(android.R.id.content,f1);
}
else
{
Fragment2 f2=new Fragment2();
ft.replace(android.R.id.content,f2);
}
//choose animation
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
//commit our changes
ft.commit();
}
private void setPointer() {
this.context=this;
menuClcick=findViewById(R.id.menuClick);
gallerClcik=findViewById(R.id.gallerClcik);
eventsClick=findViewById(R.id.eventsClick);
//this is the problem the app have no problem to find the buttons but it stops working when I try to put onclick listener in it//
menuClcick.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, "portrait", Toast.LENGTH_SHORT).show();
}
});
}
You are trying to access views that are not yet created,
As the documentation lifecycle shows, you should implement onCreateView() to inflate your layout and only there you have access to your R.id.menuClick.
So basically, you should call your setPointer() method on onCreateView().

Saving Multiple Fragments State

I implemented AppCompatActivity that i used for fragment transaction. Only one AppCompatActivity like:
public class UIActivity extends AppCompatActivity {
...
}
The fragment can begin another transaction, leading to having more than one fragment of the same class in the back stack. (illustration)
public class UiFragment extends Fragment implements FileChooserListener {
mybutton.setOnclickListener(){
// do some stuff, met some condition and add fragment ( of UiFragment), possibly inflating another view, add previous fragment to back stack
}
}
This works fine except that even when i implemented onSaveInstance,on restore instance inflate the firstfragment while other are lost. I want to resume from where the user stopped and being able to access fragment in back stack when the user press the back button.
IMPLEMENTATION
in UIActivity
UIFragment uif;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
overridePendingTransition(R.anim.push_up_in, R.anim.push_up_out);
setContentView(R.layout.activity_ui);
if (savedInstanceState == null) {
uif = new UIFragment();
Bundle bundle = new Bundle();
bundle.putSerializable("step", getIntent().getExtras().getSerializable("step"));
bundle.putString(UIFragment.STEP_KEY, getIntent().getExtras().getString(UIFragment.STEP_KEY));
uif.setArguments(bundle);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.content_frame, uif, "myfragment").commitAllowingStateLoss();
}
}
public void onSaveInstanceState(Bundle outState){
getSupportFragmentManager().putFragment(outState,"myfragment",uif);
}
public void onRestoreInstanceState(Bundle inState){
uif = (UIFragment)getSupportFragmentManager().findFragmentByTag("myfragment");
}
snippet that adds UIFragment to the stack
public void addNewUIFragment(){
UIFragment uif= new UIFragment ();
Bundle bundle = new Bundle();
bundle.putSerializable("step", step);
bundle.putString(UIFragment.STEP_KEY, sData);
uif.setArguments(bundle);
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.addToBackStack(null);
transaction.setCustomAnimations(R.anim.slide_in, R.anim.slide_out, R.anim.back_slide_in,
R.anim.back_slide_out);
transaction.add(R.id.content_frame, uif, "myfragment").commitAllowingStateLoss();
}

Add and remove fragment null pointer

I have two fragments that are in the main activity and i want to refresh them when something occurs.
Now the code works for second fragment, but won't work for the first, and i am not sure why.
I have been looking at the code for about an hour, and i can't seem to find a reason.
Here is the code
public class MainActivity extends FragmentActivity {
Fragment frag,frag2;
FragmentManager fm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String screen = getString(R.string.screen_type);
/*
* Get a reference to fragment manager
* Wire the container to represent fragment
*/
fm = getSupportFragmentManager();
frag = fm.findFragmentById(R.id.container);
if(screen.equals("large")){
frag2 = fm.findFragmentById(R.id.containerDetails);
loadFragments(frag,frag2,fm);
}
/*Loads the fragment into the activity*/
else
loadFragment(frag,fm);
}
private void loadFragments(Fragment frag, Fragment frag2, FragmentManager fm) {
if(frag == null && frag2 == null){
frag = new DisplayFragment();
frag2 = new DetailsFragment();
fm.beginTransaction().add(R.id.container,frag).add(R.id.containerDetails, frag2).commit();
}
}
private void loadFragment(Fragment frag, FragmentManager fm) {
if(frag == null){
frag = new DisplayFragment();
fm.beginTransaction().add(R.id.container,frag).commit();
}
}
public void updateDetails(int position) {
// Reload current fragment
if(frag2!=null)fm.beginTransaction().remove(frag2).commit();
frag2 = new DetailsFragment();
Bundle b = new Bundle();
b.putInt("Id",position);
frag2.setArguments(b);
fm.beginTransaction().add(R.id.containerDetails, frag2).commit();
}
public void updateDisplay() {
// Reload current fragment
if(frag!=null)fm.beginTransaction().remove(frag).commit(); //THIS IS ALWAYS NULL FOR SOME REASON
frag = new DisplayFragment();
fm.beginTransaction().add(R.id.container, frag).commit();
}
public void refreshDetails() {
// Reload current fragment
if(frag2!=null)fm.beginTransaction().remove(frag2).commit();
frag2 = new DetailsFragment();
fm.beginTransaction().add(R.id.containerDetails, frag2).commit();
}
}
The first fragment is always null, and it doesn't get removed, instead another fragment is pasted over that, and creates a mess.
Try using replace() method rather than add()

savedInstanceState is always null in fragments

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{
#Override
public void onCreate(Bundle savedInstanceState) {
setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (savedInstanceState != null) {
// it is not entering the inside here
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
// saving some values
}
When i title my device after me setting the setRetainInstance(true); the savedInstanceState is always null , so how can i get my saved values here ?
Why so? What am i doing wrong here and how to fix this ?
I think that you loose your instanceState because you alway create a new Fragment instance in your onRestart() method.
Try it this way:
#Override
protected void onRestart() {
Fragment prevFrag = getSupportFragmentManager().findFragmentById(detailFragmentID);
if (prevFrag == null || !(prevFrag instanceof MyDetailFragment)) {
getSupportFragmentManager().beginTransaction().replace(detailFragmentID, new MyDetailFragment());
}
}
This way you only attach a new instance of your Fragment only if there's no another valid Fragment of the same type.
Note that setRetainInstance(true); prevents the FragmentManager to destroy your Fragment instance when a configuration change happens.
So it has no sense to manually destroy your Fragment (by calling .remove(...)) and then init a new one with .replace(..., new MyDetailFragment()). This is why you always get an empty savedInstanceState: you are in a new instance, so no previous saved states!
Also remember that calling .commitAllowingStateLoss() on a FragmentTransaction allows the Fragment Manager it to avoid saving the savedInstanceState, so you should use it only if you really know what you're doing.
Have a nice day! :)
According to Android:
onSaveInstanceState() will be called by default for a view if and only it has an id.
The default implementation takes care of most of the UI per-instance state for you by calling onSaveInstanceState() on each view in the hierarchy that has an id, and by saving the id of the currently focused view (all of which is restored by the default implementation of onRestoreInstanceState(Bundle)).

Android Fragments not calls a Backstack

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.

Categories

Resources