I have attached a fragment at runtime in Activity's onCreate() method. I am trying to understand the sequence of lifecycle method calls on the fragment. However the behavior seems to be inconsistent from what is expected. I am doing a screen rotation to understand this. The below is the log after screen is rotated. Have put the marker in the log at the point of doubt.
Can someone explain what is happening here?
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(tag, "OnCreate()");
if (savedInstanceState != null) {
Log.d(tag, "SaveInstanceState is NOT NULL");
}
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
mainFragment = new MainFragment();
fm.beginTransaction().add(R.id.fragment_container, mainFragment, "MainFragment").commit();
}
MainFragment﹕ OnDestroy
MainFragment﹕ OnDetach
MainActivity﹕ OnDestroy
MainFragment﹕ onAttach() <<< Getting called before Activity's onCreate()
MainActivity﹕ OnCreate()
MainActivity﹕ SaveInstanceState is NOT NULL
MainFragment﹕ onCreateView()
MainFragment﹕ SaveInstanceState is NOT NULL
MainFragment﹕ onAttach() <<<<<< onAttach() on Fragment called again
MainFragment﹕ onCreateView() <<<<<<< onCreateView() on Fragment again.
Below is the Fragment lifecycle:
As per your question, when the screen is rotated, it means that the fragment and activities need to be reloaded or "recreated". If you follow the lifecycle image, you will see that Fragment is first destroyed (because it was rotated), then detached. The activity gets detached next.
Then the fragment is attached (the fragment is the object that is called before the Activity). Thereafter the activity is created.
Read up on the Android docs for a better understanding, but above answers your doubts.
Related
I'm trying to understand the behavior of fragments when a configuration change occurs.
I have created a project where an activity will host a fragment in onCreate():
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState==null) {
mFragment1 = new Fragment1();
mManager = getFragmentManager();
mManager.beginTransaction().add(R.id.container_1, mFragment1, null)
.add(R.id.container_1, mFragment1, null)
.commit();
}
}
If the transaction happens only if savedInstanceState is null, and activity and fragments are destroyed and recreated,why does my activity host the fragment again when the device is rotated?
My point is: if the savedinstancestate is not null after rotation , why does the fragment manager add the fragments again?
Thank you.
I belive what you are looking for is here
https://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
and
if you want to know the shorcut just put
setretaininstance(true);
inside of Fragments.
Fragments has it’s own life cycle but it always be embedded with an activity so that the fragments life cycle is directly affected by the host activity’s life cycle.
I think this will help: When you rotate your device and the screen changes orientation, Android usually destroys your application's existing Activities and Fragments and recreates them. Android does this so that your application can reload resources based on the new configuration.
if you use SaveInstanceState like below, then its hold the value, and will not be null and you can handle.
#Override
public void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putString(savedName, name);
}
So, what my problem is that in one fragment(w/i a viewpager, I'll call this Fragment A) I click on this dynamically created button that adds a new fragment(I'll call this Fragment B) in a framelayout which allows me to use PayPal service. On PayPal Activity result, Fragment B communicates with the main Activity via a communicator(an interface class) to call Fragment A to change that text. But I'm getting a null pointer exeception crash.
To be specific:
what I did was that I made a global TextView variable that is initialized on click. I did this b/c I have a list of other things that are dynamically inflated and to avoid the TextView from being initialized with wrong layout I initialized it on click.
bidChange.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
eventListChangeKey = keyVal;
eventListChangeIdx = eventListIdx;
eventBiddingChangeIdx = finalI;
priceToChage = (TextView) biddersLayout.findViewById(R.id.single_list_bidder_bid_price);
Bundle bundle = new Bundle();
bundle.putInt("auctionID", auctionId);
bundle.putInt("dateID", dateId);
bundle.putInt("FromWhere", 2);
Fragment fragment = new Fragment_Home_ItemInfo_Bid();
fragment.setArguments(bundle);
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.container_mainScreen, fragment, "itemInfo_bid")
.addToBackStack(null)
.setTransitionStyle(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
}
});
In the main activity
public void changeBidderPrice(String s) {
Fragment fragment = viewPagerAdapter.getItem(1);
((Fragment_List) fragment).changePrice(s);
}
is what I do
back in Fragment A
public void changePrice(String val) {
priceToChage.setText(val);
dataMap.get(eventListChangeKey).get(eventListChangeIdx).getBidList().get(eventBiddingChangeIdx).setPrice(val);
}
I've thought this over an over but I just can't figure this out. I've searched for similar cases in StackOverflow but I wasn't able to get a help.
Would the problem be the way I initialize that TextView? or is it the way I'm calling Fragment A from the main activity?
for fragments onViewCreated() is called after onCreateView() and ensures that the fragment's root view is non-null. Any view setup should happen here. E.g., view lookups, attaching listeners.
source : codepath
for activities onCreate()
I have an activity with a viewPager inside of it, and a static ArrayList of integers that I am shuffling using Collections.shuffle(list) in the activity's onCreate method, this viewPager's fragments are using the ArrayList in parent activity.
The problem is that whenever a new fragment instantiated of the viewPager the onCreate method of parent activity is called, and I don't want that to happen because I want the list to have the same data in all fragments and not reshuffled. Do fragments call the onCreate method of their parent activities everytime there is a new instance? if Yes how can I work around this to keep the list from shuffling every time?
CODE:
Activity Code:
public static final ArrayList<Integer> IDs = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
IDs.add(0);
IDs.add(1);
IDs.add(2);
Collections.shuffle(IDs);
setContentView(R.layout.activity_walkthrough);
pager = (ViewPager) findViewById(R.id.pager);
adapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
pager.setAdapter(adapter);
Fragment Code:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = (View) inflater.inflate(
R.layout.fragment_walkthrough, container, false);
final TypedArray imgs = getResources().obtainTypedArray(R.array.walkthrough_images);
ImageView imageView = (ImageView) v.findViewById(R.id.image);
if (page == 0) {
imageView.setImageResource(imgs.getResourceId(Walkthrough.IDs.get(0), 0));
} else if (page == 2) {
imageView.setImageResource(imgs.getResourceId(Walkthrough.IDs.get(1), 0));
} else {
imageView.setImageResource(imgs.getResourceId(Walkthrough.IDs.get(2), 0));
}
return v;
}
Now I want the ArrayList "IDs" to always have the same data and order when ever I instantiate a new fragment but it is not working, every time I create a new fragment the method onCreate gets recalled and a reshuffle happens!
Fragments are added to activity and therefore fragments get affected by activity.
Activity can cause calling any fragment callback method, but fragment can't
The lifecycle of the activity in which the fragment lives directly affects the lifecycle of the fragment.
For example, when the activity receives onPause(), each fragment in the activity receives onPause().
Fragments have a few extra lifecycle callbacks, however, that handle unique interaction with the activity in order to perform actions such as build and destroy the fragment's UI.
These additional callback methods are like onAttach(), onCreateView(), etc.
It'll clears the somewhat relation between fragment and activity.
Thanks
I have a MainActivity in my application, from where all the fragments are called using Navigation Drawer. And default fragment of the activity is 'A'. So everytime i open the application, 'A' fragment is called. when I hit 'back' from another fragment B, I want to get to default fragment 'A', as what happens in gmail - from any fragment if we hit back, it returns to default fragment "Primary mails".
I tried calling the 'A' fragment by adding the following code to the onPause() of fragment 'B'.
#Override
public void onPause() {
super.onPause();
fragment = new A();
FragmentTransaction fragTransaction = getFragmentManager().beginTransaction();
fragTransaction.replace(R.id.a,fragment ).commit();
}
But when I hit back, fragment 'A' is called for a moment, but then the application closes unexpectedly.
Logcat :
01-14 12:44:42.264: E/WindowManager(4655): android.view.WindowLeaked: Activity com.litchi.iguardian.MainActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView{41da6348 V.E..... R.....ID 0,0-513,243} that was originally added here
Whats the correct way of doing this ?
to back to previous fragment see below code :
fragmentManager.popBackStack(...);
put this in onBack event
to call popBackStack method you first need to call addToBackStack method while calling fragment
Use this code
android.support.v4.app.Fragment detail = new CurrentClass();
detail.setArguments(bundle);
android.support.v4.app.FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.content_frame, detail).addToBackStack("back")
.commit();
You should customize the behavior when you press the back button, because by default it will destroy launched activity and you see your fragment while callbacks goes from onPause() until onStop() and activity is not visible for you anymore. Just override this method, it must solve the problem:
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
// your code
return true;
}
return super.onKeyDown(keyCode, event);
}
But yeah, it is for global control, for switching fragments you may also call addToBackStack(); when you want return previous fragment by pressing back button.
instead of writing that code in onPause() you can write that code in onBackPressed() of your main activity .
When my MainActivity is launched my ActionBar shows the app title. Once you navigate to a fragment through the nav drawer, the title is changed to match the fragment... however, once you navigate back using the back button, the title is left untouched and still has the title of the fragment. I am looking to change it back to the application Title.
I have tried to use the onResume() method in the MainActivity.java but it appears that does not get called once you leave a fragment.
#Override
public void onResume() {
super.onResume();
// Set title
getActionBar().setTitle("FuelR");
invalidateOptionsMenu();
}
Does anyone know what the best way would be to change the title back to the app_name ?
Thanks
Indeed destroying a Fragment within an Activity doesn't mean the activity will call onResume, first you have to notice that the Fragment lives within the Activity life context, and the Fragment do not alter it's life cycle, is just part of it, what you could do is within the fragment get a reference to the activity and set the title back to previous state, as shown below:
//In Fragment
#Override
public void onDestroyView() {
super.onDestroyView();
((Cast If Necessary)getActivity()).getActionBar().setTitle("Previous Title");
}
Or a more OOP approach would be, creating an interface that declares a method setTitlePreviousText, you implement that interface in your Activity class and from the fragment you could do this:
//In Fragment
#Override
public void onDestroyView() {
super.onDestroyView();
Activity act = getActivity()
if(act instanceof SetPreviousTextInterface){
//setTitlePreviousText will be called giving you the chance to just change it back...
((SetPreviousTextInterface)act).setTitlePreviousText();
}
}
This would be the interface file:
public interface SetPreviousTextInterface{
public void setTitlePreviousText();
}
Regards!