Android: AlertDialogs and Fragments - android

I'm switching one of my applications over to fragments (from just activities) and either I'm totally missing something or they have severely complicated the whole process of showing an AlertDialog.
First, here's what I'm trying to do: show one of several alert dialogs with a positive and negative button with callback methods associated to each bottom. Dialog needs to survive (ie: be recreated) a screen rotation.
Before: In the past, all you had to do is create an AlertDialog with the proper callback methods, etc. and show it and the system would take care of everything including screen rotation.
Now: If I create and show an AlertDialog from my fragment, it doesn't get recreated during a screen rotation and according to the LogCat leaks memory during the destruction. According to the new developer docs on fragments, I should be using a DialogFragment to create a AlertDialog so that the fragment manager can handle things like screen rotation, etc. (see here: http://developer.android.com/reference/android/app/DialogFragment.html under the Alert Dialog heading) That's fine, however, the problem is the callback methods. In the provided example, they are hard coded to two methods in the Activity. I have two problems with that, I don't want to have the activity involved in this process, and I need different callback methods for different AlertDialog I create. I really don't want to have to create different classes with hard coded callbacks for each AlertDialog I will be creating. There has to be a simpler way, otherwise this is just stupidity :)
Another way to put things ... fragments are recreated after a screen rotation by the fragment manager using any "arguments" that were saved during the creation process. These arguments are saved inside a Bundle, however, I can't save a callback method inside a Bundle, hence the fragment manager can't recreate a fragment with a passed callback method, only a hard coded one, which means I need separate classes with hard coded callback methods for each type of AlertDialog I will be displaying ... is this stupid or am I just missing something here?
Thanks for any help,
Harry

You can use interfaces to tidy up the hardcoded callbacks a bit.
In the following sample, my dialog fragment class specifies an interface called Host. Activities that want to use this fragment will have to implement MyAlertDialog.Host interface and have the two methods it defines. Of course, instead of generic onOptionOne and onOptionTwo names you could use onReport, onRetry --whatever makes sense for each alert.
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
public class MyAlertDialog extends DialogFragment {
/**
* Host activities have to implement this interface to receive button click
* callbacks.
*
*/
public static interface Host {
public void onOptionOne();
public void onOptionTwo();
}
public static MyAlertDialog newInstance() {
return new MyAlertDialog();
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
DialogInterface.OnClickListener clickListener = new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Host host;
try {
host = (Host) getActivity();
} catch (ClassCastException e) {
String name = getActivity().getClass().getName();
throw new RuntimeException("Class " + name + " doesn't implement MyAlertDialog.Host interface");
}
if (which == DialogInterface.BUTTON_POSITIVE)
host.onOptionOne();
if (which == DialogInterface.BUTTON_NEGATIVE)
host.onOptionTwo();
}
};
return new AlertDialog.Builder(getActivity())
.setMessage("Message here")
.setPositiveButton("Option One", clickListener)
.setNegativeButton("Option Two", clickListener)
.create();
}
}
The upside of this is, MyAlertDialog doesn't reference any specific activities. When an activity chooses to use this dialog, it has to implement its contract. That, instead of other way around, where dialog would be coupled with activities and would hook into them.

Calling setRetainInstance(true) will cause the FragmentManager to save your actual Fragment instance. Instead of destroying and recreating the Fragment, it will just pass the same one along to the new Activity.
The main thing to be aware of using setRetainInstance(true) is that one Fragment instance may see multiple calls to onCreateView() and onDestroyView() during the lifetime of the Fragment as it is associated with different Activities. So if e.g. you registered a BroadcastReceiver in onCreateView() and unregistered it in onDestroy(), your code would work fine with setRetainInstance(false), but not with setRetainInstance(true).
Edit - Not only is this answer wrong, but I am in the discussion for the related issue!
So I deserve all the downvotes I get. :) There is a bug on setRetainInstance(true) that you can fix with a gross hack - call getDialog().setDismissMessage(null) inside your DialogFragment's onDestroyView() to hack around it.

Related

Continuous notification from one Activity to another

How can I have an activity B, which pops up and partially obscures a "parent" activity A, continuously send update info to A?
The normal mechanism, of course, would be to send an Intent back to A. But this can only happen once, when calling finish().
I suppose another way would be to have a handler in A and let B post to the handler. Getting the handler from A to B could be done through a "global" Application member.
Is there a better way?
EDIT: Using DialogFragment appears to be a good solution. However there is a position issue with DialogFragment. Please see my new post: https://stackoverflow.com/questions/30471032/position-dialogfragmet-relative-to-view
As far as I know, an Activity always covers another Activity. At any point, Android could reclaim memory and destroy Activity A.
This means that you should manage your data differently. Either through your Application instance, if there is not much to share.
But you probably should consider another storage mechanism. What kind of data do you want to pass to your Activity A ? And what do you mean by "partially obscures"?
EDIT
I would suggest that your DialogFragment keeps a reference to your Activity. Take a look at the developer page. You can try to implement something like this:
In you Activity, when you would like to show your dialog:
void showDialog() {
DialogFragment newFragment = MyAlertDialogFragment.newInstance(
R.string.alert_dialog_two_buttons_title);
newFragment.setActivity(this);
newFragment.show(getFragmentManager(), "dialog");
}
In your DialogFragment class, simply implement a setter method:
public static class MyAlertDialogFragment extends DialogFragment {
Activity activity;
//rest of the code here
public void setActivity(Activity a){
this.activity = a;
}
private void notifyActivity(){
int level = aMethod();
activity.somethingHappened(level);
}
}
Now, everytime you would like to call a method of your Activity, use the reference you passed previously.
I would also make an Interface, and make your Activity implement it. Like this, you are not dependent on one specific Activity, but it could be any UI component. Hope it helps.

Illegalstateexception activity has been destroyed fragmenttransaction.commit()

I have seen a few versions of this question before, but the reasons for this exception were different than my own it seems.
What I am trying to do:
-Main Activity class has a toolbar at the bottom, clicking the buttons will display a series of fragments, one after another.
- A class EditItemFragmentManager, which is instatiated on a button click, and has methods that display specific fragments based on the toolbar button clicked.
I would like to use this manager class I created because it cleans my code up significantly and will make adding more features later helpful.
Here is my EditItemFragmentManager class, I am not sure if extending Activity is a good idea or not, I think that it will put my MainActivity on pause
public class EditItemFragmentManager extends Activity{
//instance variables
public EditItemFragmentManager(){
// initialization of some variables
}
public void editItem(){
editItemSequence();
}
private void editItemSequence(){
EditNameFragment enf = new EditNameFragment();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(editNameFragment, EDIT_FRAG_TAG);
fragmentTransaction.addToBackStack(EDIT_FRAG_TAG);
fragmentTransaction.commit();
}
}
So it blows up when commit(); is called, giving me
java.lang.IllegalStateException: Activity has been destroyed
This is how I am trying to get this fragment from my MainActivity,
#Override
public void onClick(View view) {
EditIteFragmetManager manager = new EditIteFragmetManager();
manager.editItem();
}
I am still learning about the Acvtivity lifecycle in Android. I think my problem is something due to this class extending Activity, which puts my Main on pause, and the FragmentTransaction has nothing to commit to? If so, I need to get the existing instance of my main activity and call it on that? This is where I'm a bit lost, if anyone who understands the lifecycle of Activities/Fragments explain how I could go about implementing this while still having a helper class such as this?
If you're using the SupportFragmentManager, then you need to extend from FragmentActivity, and not just Activity. Also make sure that you imported the Fragment from the v4 support library, and not android.app.
Other than that, you seem to be instantiating a subclass of Activity with "new", which is terrible. Create activities only using Intents.
I solved this issue by moving my manager class to become a private inner class of my main, since they are so tightly coupled. No fragment issues now.

Where to put the Fragment functional code?

Just a general question about working with Fragments and Activitys for android development: where does the business end of the functional code go for Fragments loaded into an Activity dynamically? (i.e. a fragment's OnClickListeners, OnCheckedChangedListeners, button logic methods...)
Do they go in the Fragment class, or the Activity class?
All the GUI logic for views attached to a fragment should be contained inside the fragment itself.
Thus a fragment should be as self contained as possible.
You can, though, if necessary do callbacks to your activity based on fragment GUI interaction. This can easily be done like this inside the fragment:
#Override
public void onAttach(Activity activity) {
if (!(activity instanceof SherlockFragmentActivity)) {
throw new IllegalStateException(getClass().getSimpleName()
+ " must be attached to a SherlockFragmentActivity.");
}
mActivity = (SherlockFragmentActivity) activity;
super.onAttach(activity);
}
In this specific case the reason for gaining a reference to SherlockFragmentActivity is to gain access to the support menu inflater mActivity.getSupportMenuInflater(), hence the construction can of course also serve to gain information from the underlying activity.
This probably depends on how much the Fragment's functionalities have in common, and how many, let's say Buttons, have to be handled.
I personally (and it's probably most common practice) handle onClick(...) events separately for each Fragment, meaning that I let each Fragment implement it's own OnClickListener.
Furthermore, when handling everything through the Activity, probably not all the components that react to click-events are in memory at all times and can be reached via findViewById(...), depending on which Fragment is currently displayed and how your user-interface is built up in general.
they always in fragment class because fragment is one type of component in android which we can reuse it. if we put onclick and oncheckchanged in activity then what meaning of reusing that component??
for more information about please go through following step:
Link 1 for basic level of information about fragment and how to handle them
Link 2 for dealing with multi pane fragment
Standard site for fragment
It depends:
If fragment can handle logic which is self sufficient(complete) then that code can be handled by fragment. e.g. on click call phone number.
If fragment have UI whose action is activity specific, then you want to add listener in activity.
e.g. master detail view like email client, on tablet user click on title fragment1 which have list of email titles, then handler on click in activity can show detail fragment2 in activity.
In all you want to keep fragment reusable.

Why are the fragments communicating through the container Activity are called reusable?

I'm wondering why are the Fragments communicating through the container Activity are called reusable.
From: http://developer.android.com/guide/components/fragments.html I know that:
You should design each fragment as a modular and reusable activity
component. That is, because each fragment defines its own layout and
its own behavior with its own lifecycle callbacks, you can include one
fragment in multiple activities, so you should design for reuse and
avoid directly manipulating one fragment from another fragment.
Let's take an example; I have a DateSetFragment which contains two buttons; first button fires TimePickerDialog (FragmentDialog) which allows user to pick an hour and the second one DatePickerDialog (FragmentDialog) which allows user to pick a day. At the end gathered data should be sent back to the DateSetFragment.
According the: http://developer.android.com/training/basics/fragments/communicating.html:
All Fragment-to-Fragment communication is done through the associated
Activity. Two Fragments should never communicate directly.
communication between fragments should be done via interfaces through the container activity. Given that I should send my collected data from both Fragment Dialogs to the container activity and then from the Activity back to the DateSetFragment. I don't see how this make my DateSetFragment reusable and modular in any way. Doing so I have to implement fragment interfaces and some crucial logic in my container Activity which makes it connected with it.
The question is; Is it wrong in this situation if Fragment Dialogs will communicate directly with the DateSetFragment ?
It is "modular" because those interfaces are well defined and explicitly implemented by the hosting activity.
Anywhere you drop that fragment in, if the activity implements the callback interface defined by the fragment events the activity can choose what to do depending on what environment the fragment is attached in.
Example,
DatePickerFragment extends Fragment {
public interface DatePickerFragmentEventListener {
public void onDateSelected(DateTime dt);
}
}
Activity1 extends Activity implements DatePickerFragmentEventListener {
DatePickerFragment mDatePickerFragment;
OtherFragment mFragment2;
#Override
public void onDateSelected(DateTime dt) {
mFragment2.setSomeViewsText(dt.toString());
}
}
Activity2 extends Activity implements DatePickerFragmentEventListener {
DatePickerFragment mDatePickerFragment;
#Override
public void onDateSelected(DateTime dt) {
SharedPrefClient c = SharedPrefClient.getInstance();
c.setExpirationDateTime(dt);
}
}
I have a dateTimePickerFragment (or whatever), in one activity I have 2 fragments, when you change the date on the picker I want to update the other fragment's text view to display that date. In another activity I might use that exact same callback to write the chosen date to SharedPreferences.
The point is that the Fragment is a contained piece of UI interaction, and certain events it will notify the enclosing Activity of what just happened to it, so that the activity dictates that the result of an action on the fragment does to either other fragment or the application itself. There is no reason to implement a custom fragment for each and every situation.

Use Activity or Fragment in Android Application

I am new in android. I often use Activity to change from one screen to another screen with other function. Example from Home Page to Popular page. After that, i know about fragment but i never use it before. So, if i have a application with multi tab on a screen, not use TabHost here. Function of every tab very diffrent, ex : tab Home, tab Popular, tab News, tab Profile ... like Instagram App. I must use that
Activity to change Screen to another Screen, it means: i have Home Activity, Popular Activity, ... and change Activity when change Sreen. Each Activity have each layout.
Use fragment within one Activity. We have multi fragment, example HomeFragment, Popular Fragment... chang replace Fragment when change Screen.
What way is better ?
I want to ask when use only phone screen. ( small size screen, not for tablet).
It's important to think of Android devices as more of a spectrum, than clear "phone" vs. "tablet" buckets. There are many instances where you might want to show more information on screen on medium and large screens. Sometimes, this translates to showing two "Activities" at once.
Using Fragments requires little overhead, but adds measurable flexibility, especially when considered early in the development process. If you use Fragments properly, adapting to larger screens is extremely simple. However, there are a few "gotchas" that may make Fragments appear to be more daunting that they actually are:
Fragment classes must always be declared public (if it's a nested class, it must be static).
In the parent Activity (or FragmentActivity), only add the root Fragment if savedInstanceState == null. If you are managing the state of your Fragment properly, everything is handled for you (scroll position, EditText values, etc).
The parent Activity must call through to onSavedInstanceState in order for the Fragment to properly restore it's state.
setRetainInstance(true) should only be used for "headless" Fragments. This is when you use a Fragment that has no UI, and isn't added to the back stack, which is typically used to do life-cycle dependent work.
Fragments declared in XML cannot be used in a FragmentTransaction (and vice-versa).
Think of a Fragment as a modular view, that provides hooks (callbacks) to it's Activity when something important happens. The Activity decides, based on the available space, whether to launch a new Activity, or show a new Fragment.
You can use either way. If you decide to use the Activity solution, create a base activity class that contains all the Tab functionality. You don't want to implement that in every Activity over and over again.
public class BaseActivity extends Activity {
#Override
public void onCreate(...) {
// Init tabs
}
// Methods for tab handling
}
Every Activity (Popular, Profile, Home, ...) extends BaseActivity
public class PopularActivity extends BaseActivity {
#Override
public void onCreate(...) {
super.onCreate(...);
// Init only the popular activity elements here
}
}
This way you implement the tab functionality only once and get it in every activity.

Categories

Resources