I've got a fully working activity. However, I want to adapt the activity to a Fragment so I'm able to transition between the initial activity and any future activities.
However, how do I update the UI after I've called the onCreateView();? I've got a task that is repeated constantly by a handler, so the interface needs to be updated every 0.5 seconds. This doesn't seem possible with a fragment as it seems to be static (it's exited after creation).
So how do I edit/update a fragment's interface after it's creation?
Do I do my activity's work inside the FragmentActivity or the Fragment itself?
1) in your Fragment, init Views in the onCreateView() method, use class fields
2) create public methods inside your Fragment and you can call them from Activity
yourFragInstance.yourMethod();
Related
I have the following design:
MainActivity
| /->AddingActivity
|-------Fragment One---\->DialogFragment
|-------Fragment Two
|-------Fragment Three
|-------Fragment Four
MainActivity hold the fragments. Fragment One can start AddingActivity (with context from MainActivity) which adding data to DB and then all fragments should refresh with the new data. DialogFrgment started from FragmentOne is adding some data to SharedPreference and only the FragmentOne should know about this. MainActivity impliments DialogFragment.OnTimeSetListener (The dialog picks time) and then in MainActivity onTimeSet triggers function in Fragment One to refresh the data.
What I did for now is overridden OnCreate in each fragment and the fragment refreshes when it returns back to view. But the app is bit laggy because each fragment refreshes its neighbors and I get some fragment refreshed twice.
Can you please advise me about the correct approach to this design, and how can I make the app work more smooth and correct.
Your approach of MainActivity implementing DialogFragment.OnTimeSetListener and then notifying FragmentOne seems kind of strange.
This is how I would do it:
FragmentOne and MainActivity implement DialogFragment.OnTimeSetListener as well. However, only FragmentOne should be notified from the dialog fragment when the time changes, as it is the one responsible for setting the time. Then when this happens, FragmentOne should notify the activity:
#Override
public void onTimeSet(Time time) {
((DialogFragment.OnTimeSetListener) getActivity().onTimeSet(time);
}
When MainActivity receives the time set event, it must update the other fragments, but if possible, it should not re-create them. Just let them know the time was set so that they can update their views accordingly. Each fragment will update itself depending on what it is displaying, etc.
In case the problem persists and you still have lag, check if you do too much work on the main thread, e.g. a long-running for-loop and try to execute it on another thread.
Maybe you can use LocalBroadcastManager to refresh the the fragments , that way you can have the control to which fragment to refresh and not all at the same time , and you should also check if the DB is causing the lag.
Here is an example how to use LocalBroadcastManager you should implement it on your own way.
https://android--code.blogspot.com/2015/12/android-how-to-send-and-receive-local.html
I have an Activity with two Fragments. I decided to have one presenter for each "view". So 1 presenter for the main Activity, 1 for the first fragment, and 1 presenter for the second fragment.
I have uses-cases in which I don't know where which code goes.
The first is where to manage fragments with fragment manager ? Do I have to do calls like "beginTransaction().add" in the activity or in his presenter ?
The second is, when the user tap on a button in the activity, I've to do some things in the current fragment. Do I have to call the presenter of the activity which will call the fragment's method wanted, or directly in the method onClick in the activity I call this fragment's method?
PS: I don't want to use any lib/framework
The first is where to manage fragments with fragment manager ? Do I
have to do calls like "beginTransaction().add" in the activity or in
his presenter ?
Everything that is Android API related should be inside the Activity. So, beginTransaction() is a method of the FragmentManager and this is part of the Android API. The Activity is the contract between your app and the operating system. The presenter should not even know that it is used for an Android app. If you press a button inside the Activity for instance it goes like this:
Inside event handler method which is inside your Activity you do this:
Call activityPresenter.onButtonClicked() and it will call activityView.presentWhatTheButtonClickDid()
The second is, when the user tap on a button in the activity, I've to
do some things in the current fragment. Do I have to call the
presenter of the activity which will call the fragment's method
wanted, or directly in the method onClick in the activity I call this
fragment's method?
You would indirectly call the Fragment method via its presenter.
Inside event handler method which is inside your Activity:
Call activityPresenter.onButtonClicked() and it will call
fragmentPresenter.onButtonClicked() and it will calls
fragmentView.presentResult()
So, as you see the Activitiy's presenter needs to know the Fragment's presenter.
*You should not name your presenters with "activity" or "fragment" in its name to keep things abstract. I merely did this for simplicity.
Fragment transaction has method add(Fragment fragment, String tag), which does not place fragment to container, so it cannot have view. For what it can be used?
From the Android Documentation:
However, a fragment is not required to be a part of the activity layout; you may also use a fragment without its own UI as an invisible worker for the activity.
How about this purpose ?
Simple example: an Activity starts an AsyncTask, but when device rotated activity restarts, causing AsyncTask to lose connection with the UI Thread. But this Activity can hold a Fragment (invisible, with no UI at all) that can handle all the AsyncTask work. When Activity recreated the Android OS takes care reattaching the Fragment, thus no data loss will occur.
For Dialogs you don't have any container on normal app layer. It is directly added on Window with WindowManager(See WindowManager.LayoutParams for various types of layers).
DialogFragment has an API like DialogFragment.html#show(android.app.FragmentManager, java.lang.String) which corresponds to this.
You can use fragments without UI (container) as a background worker (one benefit is that you can retain it during rotations etc) and for retaining data during rotations and other changes.
Reading http://developer.android.com/guide/components/fragments.html is strongly recommended.
Example of instance retaining: https://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/apis/app/FragmentRetainInstance.java
Also, here are similar questions (so this questions seems to be a duplicated but cannot be flagged due to bounty):
What is the use case for a Fragment with no UI?
Android non-UI Fragment usage
As #Lucius Hipan mentions, it can be used to prevent data loss.
Almost always this king of fragments are used as Retained container ( setRetainInstance(true) called in onCreate method), then after device configuration changes (e.g. orientation changing) fragment will not be recreated but remembers previous state.
It's recommended way to use asynctask.
Here is an example:
There is login activity. The user enters their credentials and presses the Login button. After that configuration change occurs (user rotates phone). So, network task was completed, but your handlers was not listening for it now. If you show any login animation, it can be stored via savedInstance, but listeners not. And instead of creating service you can simply create new retained fragment with persistant asynctask and interface to communicate with activity.
This method is a good compromise for small projects where using bus libraries is overstatement.
By calling the method add(Fragment fragment, String tag) internally calls add(int containerId, Fragment fragment, String tag) with a 0 containerId.That will be add(0, fragment, tag).
If 0 is supplied as containerId, it will not be placed the fragment in a container.
I have been struggling to find out what the correct management of Fragments within a FragmentActivity with a ViewPager is. Before I go into details, a quick summary of the issue that I am facing is the following:
I have a FragmentActivity with a ViewPager. The ViewPager uses a custom, yet very simple FragmentPagerAdapter. Each Fragment within the ViewPager comprises of an ExpandableListView. I also have an action bar button called "Refresh". For now, let's assume that the ViewPager has only one Fragment. The activity is created, and the Fragment's ExpandableListView is populated (so far so good). When the Refresh button is clicked, the handling method within the FragmentActivity iterates over the list of Fragments that are assigned to the FragmentPagerAdapter and calls refresh() on each Fragment to populate its ListView. However, when the orientation of the device changes (e.g. from portrait to landscape), the Activity is recreated and so are the fragments. Clicking the Refresh button now will iterate over non-initialised Fragments.
I know that I am being quite vague, especially without sample code, but please bear with me. I have traced the problem and method calls as follows from the start of the application/activity:
FragmentActivity.onCreate()
FragmentActivity.setContentView()
FragmentActivity.createPagerFragments() <-- this creates an ArrayList of Fragments and assignes them to a new FragmentPagerAdapter which is in turn assigned to the ViewPager.
Fragment.onAttach()
Fragment.onCreate() <-- nothing special here, just calling the super method.
Fragment.onCreateView() <-- nothing special here either, just inflating the layout
Fragment.onActivityCreated() <-- nothing here either.
<< All good, orientation changes here >>
FragmentActivity.onCreate()
Fragment.onAttach()
Fragment.onCreate()
FragmentActivity.setContentView()
FragmentActivity.createPagerFragments()
Fragment.onCreateView()
Fragment.onActivityCreated()
<< Refresh button clicked >>
FragmentActivity.refresh() <-- iterates over the newly created Fragments from #13 (not these by Android!).
<< Crash: NullPointerException for mExpandableListView in Fragment. >>
So the problem, as I see it, is as follows:
When Android re-creates the FragmentActivity and its Views after a change of screen orientation (calls #9-15 above), it creates new Fragment objects with their state restored to what the original ones were. However, these ones appear to be completely managed by the FragmentManager, and not by the FragmentPagerAdapter. In contrast, when the FragmentPagerAdapter is re-created along with the Fragments in the activity's onCreate method (see call #13) the Fragments that get assigned to the adapter never have their Fragment.onCreate() or Fragment.onCreateView() methods called at all. So when the refresh() method is called (see #17) the method iterates over these Fragments that have not been initialised. Therefore, when they try to populate the ExpandableListView, the view's instance variable is NULL. This is to be expected as the instance variable is only assigned in the Fragment.onCreateView() method that never gets called on these Fragments.
So my question is: how does one properly make re-use of the re-recreated (by Android) Fragments after the screen orientation has changed in order to avoid creating new ones that don't get initialised? I need to have a valid reference to them, in order to call their refresh() method that populates them on-demand. Ideally, they should also be assigned to the FragmentPagerAdapter as well.
I hope I have been clear in describing the issue, and the reason that I have not provided sample code is because the problem (as can be seen) is not from the code itself but from a rather incorrect (seemigly) re-creation of Fragments rather than re-use. But if needed, I can give you sample code, I just through this way would be clearer.
Thank you!
It's lot to read, but after reading just introduction and the question and having experience with FragmentStatePagerAdapter, which is similar to FragmentPagerAdapter I can tell you that:
After rotation your adapter will AUTOMAGICALLY attach old fragments. So it seems that although activity creating adapter is being recreated, FragmentManager, which is global and it's instance preserve activity's recreation will detect that new FragmentStatePagerAdapter is combined with the same ViewPager and is asking for the same Fragments and will simply fetch them from Fragment's BackStack.
You as designer of Fragments can notice this behavior by continues invocation of Fragment.onAttach() and Fragment.onDetach(). When onAttach() occurs it's either creation of your Fragment or reusing it after rotation. You should be able to distinguish that Fragment was rotated with use of callback onRestoreRnstanceState().
You will see in your logs many onCreate() and other states logs simultaneously, because FragmentStatePagerAdapter always fetches/creates min 3 Fragments (except if you set that they are only 2 or 1), so also after screen rotation 3 fragments will be reattached from backstack.
I hope that it helped.
I believe that this question, about retrieving the current fragment from a ViewPager, will help you. As already pointed out, fragments are managed by the Fragment(State)PagerAdapter and NOT Activity's or Fragment's lifecycle.
The first time the activity is created, fragments are returned by the getItem method. This method is called only once per fragment, even if the activity gets recreated.
Subsequent times, the fragments are returned by the instantiateItem method. Most probably, this is the place, where you need to get hold of your fragments and call their refresh methods.
How about adding this to Activity Tag in your manifest:
android:configChanges="orientation"
or this for API 13 or higher
android:configChanges="orientation|screenSize"
so it won't recreate your fragments when it changes orientation..
I have a scenario and I an not sure on what path to go.
Scenario
The app has a Home activity which displays various fragments. The data in the fragments can come either from the web or a local database and is retrieved using an asynctask.
From what I saw, I have 2 alternatives:
Put the Asynctask in parent activity and then use fragment.newInstance(parameters) to pass the result to the fragment. However, if in my asynctask I need to update the progress or some info on the fragment, each time I will have to call newInstance with the new set of parameters.
Add the fragment and put the asynctask in it, in this way when progress is needed, I can update the fragment's views, as I have access to them + when the asynctask is done, I can populate the list with the info.
What would be the correct approach ?
LE: actually for point 1 in order to update the fragment I can call fragment's public methods after I find it with findFragmentById in the parent activity
A better way if you have multiple tasks would be to use an IntentService :
http://mobile.tutsplus.com/tutorials/android/android-fundamentals-intentservice-basics/
You would have a better control to what you're requesting and what you want to cancel.
I would go with the second approach.
My primary reason though would be to avoid the issues that can happen on screen orientation change while the AsyncTask is working.
I would go with method 2, but take it a step further.
Have a separate fragment to run your async task. This way, you can handle any configuration changes (not just rotating screen) without any issues.
In another fragment, you can display the data. You can pass the data from your async task fragment via callbacks to the activity, and have the activity call a method in the display fragment to update the data.