Should accessing views done from onStart or onActivityCreated method? - android

I have read several documents about this but still fail to conclude where should I write my codes to set some values in textviews/edittext ...
what i have read and seen in video tutorials is, both onStart and onActivityCreated methods get called with different actions (like after fragment initiated, or orientation changed etc.). Moreover, both of them get called after Activity's onCreate method, which means views are available from both Fragment methods.
Anybody can give me some advise regarding this?
(p.s. Currently I put all codes accessing xml views inside onStart, and my application is running without any issue)

I don't know of any potential problems with accessing your layout's views in onStart or onActivityCreated.
Personally, I usually set references to my layout's views and set initial values in a fragment's #onViewCreated(). This is the first opportunity after the layout has been inflated that you have to access a layout's children. The View that was inflated is passed as a parameter so you even have direct access to the parent layout object if you needed it for some reason.
According to the fragment lifecycle onActivityCreated() will be called next and then onStart(). All of these will be executed in that same order when a fragment is returned from the back stack -- so it seems to be personal preference.

Related

When are views attached and detached?

This question is not about how to detect if a view is attached or detached.
In general, when is a view attached or detached? Is there a lifecycle diagram for this?
To clarify, I'm looking for answers to what happens when: Activity sent to background, opaque view placed on top, visibility set to GONE, view inflated, parent detached, etc. This is not an exhaustive list - I just want to understand how attaching and detaching of views works, at a fundamental level.
Update with more examples of what I'm trying to get at:
What about fragments vs. activities?
What about nested views - in what order are views attached/detached (parent->child or child->parent)?
Are views measured before they are attached or after?
What about using addView() to a ViewGroup manually?
Edit: Summary:
For Activities, views are attached in setContentView(). Views are detached in onDestroy() or when setContentView() is called with a different view.
For Fragments, views are attached after onViewCreated() finishes, and are detached after onDestroyView() finishes.
For ViewGroups, views are attached in addView() and detached in removeView()
setVisibility() does not affect the attached state of a view
From the official documentation:
An activity is a single, focused thing that the user can do. Almost
all activities interact with the user...
The first thing that needs to be noted is that it is not a must for activities to be associated with a layout. You can have an activity with no UI (and hence no View). Android even specifies a no UI theme for this.
Moving on to your question - a View gets attached to an Activity at the moment you call setContentView(view). This is usually called within the onCreate() method. The reason you usually have this in the onCreate() method is because most of the initialization is done there. And how could you initialize your widgets if the view has not been inflated and attached to the Activity? Hence, if you have a view, you almost invariable end up calling setContentView() inside your onCreate() method preceding all other initialization.
But does that mean that the view (if it exists) must be tied to the
activity only within the onCreate() method?
To answer this question, let's see what the lifecycle of an Activity looks like. You start your app:
onCreate() -> onStart() -> onResume() // They get called consecutively
The stage you are now in is where all the widgets have been initialized.
So why not inflate and attach the activity in onResume() and do all the
initializations there?
Of course, you could. But imagine what happens when a dialog (a partially opaque view) shows up? The Activity is now partially covered and is in the background. The onPause() method is called. The layout is still attached to the Activity at this point. You take some action and dismiss the dialog. onResume() gets called. The layout would be inflated again. All the initializations would happen again and you would lose your state. Even if you did not have much in the way of initialization, you would still be making a pretty expensive call by calling onCreate() again. And you want to avoid this in resource limited mobile devices.
What happens when an opaque view comes up and the Activity is now in
the background but still running (like an incoming phone call or opening another activity)?
Now the following callbacks happen:
onPause() -> onStop()
When you move back to the original activity
onRestart() -> onStart() -> onResume()
For the same reason as I mentioned in onPause() you do not want to inflate and attach a layout here.
But what happens to the layout itself when an Activity is in the
background. Is the layout still attached?
Yes, it very much is. If another activity comes up which uses the same layout as the original activity, then the new activity has it's own layout and there is no sharing of layouts.
What happens if the user terminates the activity by pressing the Back
button?
Assuming the onBackPressed() method is not overridden to achieve custom behavior (in which case, it is up for grabs), onDestroy() is called and the activity is destroyed and there is no View associated with it anymore.
What happens when the activity is in the background and the Android GC
decides to destroy the activity and reclaim resources?
According to the activity lifecycle within the documentation, onDestroy() will be called. But there is no guarantee of this. At this point, the activity and it's associated view are simply garbage collected and there is no connection.The next time you start the app, onCreate() will be called as usual and you simply start from the beginning again.
What happens when I rotate my device?
The way Android works is to actually destroy the current activity and inflate the new layout again and start from the onCreate() method again. So what technically happens is:
onPause() -> onStop() -> onDestroy() -> onCreate() -> onStart() ->
onResume()
Because of this, you can even have a different layout and view while in landscape mode.
EDIT: Added the relationship between activities, fragments and views
A fragment represents a portion (or behavior) on the screen. A fragment can be made to occupy the full screen or you can have multiple fragments within an Activity. Fragments have their own life cycle but it is closely tied to the host activity's life cycle (and beyond the scope of this answer). Since we are specifically talking about views, I will limit this answer to two methods of interest:
onCreateView()
onViewCreated()
The methods are called in the following order:
onAttach() -> onCreate() -> onCreateView() -> onViewCreated()
You do the actual layout inflation within the onCreateView() and then you do the initializations within the onViewCreated() method. The result of the onCreateView() method is used by Android to inflate the view.
So when is the fragment created in the first place by the Activity?
There are two ways to display fragments - one is to put them within the xml layout of the activity (just like any regular widget where instead of the widget name, you will be using the fully qualified package name of the fragment class) or you can do the addition using FragmentManager programmatically (which is the preferred method).
If you were defining the fragment within the xml layout, you should know that the fragment cannot be removed programmatically. It would be hard to modify this and reuse that screen space for other fragments. Also in this case, the view is attached and tied to the activity. In this case, you will inflate the xml layout of the Activity within the onCreate() method of the activity. So now, the flow will looks something like:
onCreate() [Activity] -> onAttach() [Fragment] -> onCreate()
[Fragment] -> onCreateView() [Fragment] -> onViewCreated() [Fragment]
-> onStart() [Activity] -> onResume() [Activity] -> onActivityCreated() [Fragment]
So first the fragment view is instantiated and attached to the fragment before the onStart() method of the activity is created.
If the fragment is added programmatically, if it is added within the onCreate() method, then it follows the same flow. It can be started anywhere. You simply have to substitute the life cycle of the fragment within the activity in the appropriate place. When you add fragments programmatically, while the fragment is hosted in the activity, the view is attached to the activity. When the fragment is removed from the activity, onDetach() is called and the view is no longer a part of the Activity. The resources taken by the fragment can be released.
What about Nested Views, Nested Fragments, etc.?
In nested views, like one layout container inside another, the rules of the parent container apply to the immediate child container. Always the parent gets initialized first. So for a widget inside a LinearLayout, the parent LinearLayout is constructed first immediately followed by the child. When destroying such views, everything goes when the parent ceases to exist. I have not read about any documentation as to an order in which this may happen. The Android GC may have rules but I am not sure if they are documented anywhere.
You can have nested fragments too - in this case, the parent fragment gets initialized before the child fragment does (and it makes sense doesn't it?). When a parent fragment ceases to exist, the child will also cease to exist. The child cannot exist without the parent but you can have the parent without the child.
The bottom line for nested views is that once the parent view is
destroyed, it takes the child view immediately with it.
Are views measured before they are attached or after?
Views are measure after they are attached. Calling getMeausredWidth() or getMeasuredHeight() will return zero prior to this. But what you can do is call neasure() directly on the view before it is attached and pass MeasureSpecs (I would suggest you read up more on this in the official docs) to set some constraints. But this option is not fool proof as it depends on the parent ViewGroup enforcing it's own constraints which take higher precedence. To simply answer your question, views are measured after they are attached.
What about using addView() to add a view to a ViewGroup manually?
This is simply the same as nested views. The child exist only when they are added and this is controlled by the user. In nested views defined in layout xml, the children are inflated immediately after their parent. Here the control is more in the hands of the user. When the parent view in this case is destroyed, it takes the child view with it.
As a last point, I'd also like to mention that you should not use static handles for views as this would lead to a lot of headaches with the tearing down of views.

How to use EditText from a single fragment across tabs?

I'm having difficulty understanding the lifecycle of my fragment when the same fragment is created across three tabs. The main widget in each fragment is an EditText object that that is tracked in a class I created, one per tab, created when my app first executes. When the app executes the input (which is provided by buttons on the screen, not a keyboard) sets text into the second tab's EditText.
The input appears to be following getItem() inside my FragmentPagerAdapter. I understand that getItem() is called twice when my tabs are created and that this is perfectly normal behavior but what I'm not understanding is if this is a focus issue or a logic issue with my code design (I'm hoping the former). Could it have to do with instantiate() being returned by getItem()? Once instantiate() is called, does focus always follow it since I have this line of code in the onCreateView() of my fragment?
active.editLine = (EditText) v.findViewById(R.id.display);
// active is the currently active object of my app's class.
The reason I suspect instantiate() is causing my error is because when I increased the rendering of pages via setOffscreenPageLimit(2) the text was being sent to the third tab.
At this point I tried to track what was happening where and it was a nice educational exercise. What I found in my struggles was this is probably a focus issue-- however my attempts at clearing focus and setting focus did not appear to be applied, nor am I even sure if that's the underlying issue. I ended up using active.editLine.debug(0) to see if focus was being lost or gained and it wasn't. I read some of this fairly popular post and took my focus out of my XML but that didn't help either.
Just to make sure, where should I be setting my EditText objects? In OnCreateView() in my fragment? That is where the above code currently lives. I create three objects of my custom class in my activity as global variables. I have getters and setters in what I feel are appropriate areas of my code. Should the above code instead live in onCreate() of my fragment? If you think this is a focus issue, what is the accepted method of turning off focus and only using it when I need it? More importantly, when my app first runs, where do I turn on focus? OnTabSelected() is called before getItem() runs twice to render the first two tabs. So where would I gain initial focus if I'm globally turning it off? Can set focus with a type of listener?
Not exactly sure what you're going for here, but here are some suggestions:
have your global active object live in the main activity and call getActivity().someObject from your fragments
findViewById() will search down the view hierarchy until it finds the first instance of R.id.display
your fragment views won't be available until you commit them with a fragment transaction, this means that their views also won't be available until that time
you are "scoping" findViewById() to some view v by calling v.findViewById()
if you are paging through fragments, perhaps try setting your active view in the fragment's onResume() method
the fragment's onCreateView() method is only called once. This will be called between onCreate(Bundle) and onActivityCreated(Bundle).

Android - add a view that will exist despite setContentView()

Anytime one calls setContentView() multiple times, previous views get destroyed. How does one create a view that will exist, as an overlay or under the main window, despite multiple calls to setContentView()?
OK, I think I'll answer myself: After some reading I believe I shouldn't be calling setContentView multiple times.
Instead I should use one layout that has the view I wanted to exist despite layout changes and also put ViewFlipper and use that for layout changes not setContentView.

What comes between onCreate and onStart for Android?

I see from Android Developers (http://developer.android.com/reference/android/app/Activity.html) that there is a nice flowchart showing onCreate leading to onStart then to onResume, and so forth. My question is: what other on****() methods appear in between onCreate and onStart?
For example, I've been doing research on the topic, and I know other methods such as onMeasure and onSizeChanged, onDraw, and others exists. Where do they fit in to that flow chart?
Thank you.
The methods you mentioned aren't related to the Activity lifecycle.
For instance, it would be incorrect to include the call to onMeasure in the Activity lifecycle flow chart. onMeasure is called whenever the layout changes (i.e. when requestLayout is called) or the first time a window is laid out. The call to onMeasure isn't directly related to the system's calls to onCreate and onStart.
Those other methods exist, but they don't really fit in any one place on that flowchart, nor are they part of the activity lifecycle. in fact, that's why they're not on the chart. The ones you mentioned are really more of the view lifecycle which is separate from (though admittedly related to) the activity lifecycle.
This image can clearly depict what you want.

Android Fragment onCreateView vs. onActivityCreated

I know that a fragment's view hierarchy has to be inflated in onCreateView, but what other functionality can be in onCreateView vs. what should wait for onActivityCreated? My current implementation uses separate activities for everything, and a typical activity does most of its work in its onCreate method, including inflating the view, setting the content view, initializing the various widgets with data, setting up listeners, etc.
So can this probably all be moved into onCreateView, or should some functions be put into an onActivityCreated method instead?
If your view is static, then moving any code to the onActivityCreated method is not necessary. But when you - for instance, fill some lists from the adapter, then you should do it in the onActivityCreated method as well as restoring the view state when setRetainInstance used to do so.
Also accessing the view hierarchy of the parent activity must be done in the onActivityCreated, not sooner.
onActivityCreated() is deprecated in fragment 1.3.0-alpha02 and there is a recommendation to use onViewCreated() instead. View is already created here and you can set listeners, observe LiveData from ViewModel, initialize recyclerView, etc.
For a better understanding, you can take a look at my blog post, where I describe the Android Fragment lifecycle in 137 seconds.

Categories

Resources