Tak więc próbuję zrozumieć, jak rozwiązać ten problem, ale nie mogę go naprawiać. W tej chwili mam jeden fragment z jednym przyciskiem. Po naciśnięciu tego przycisku uruchomi się niestandardowy DialogFragment
za pomocą przycisków ok/cancel.Uruchamianie okna dialogowego z innego fragmentu: IllegalStateException: Nie można wykonać tej akcji po parametrze onSaveInstanceState
Jeśli naciśniesz przycisk OK, uruchomi się kolejny niestandardowy DialogFragment
, tym razem jest to fragment ProgressDialog
. Problem polega na tym, że gdy pojawia się okno dialogowe ok/anulowania, gdy się obracam, a następnie naciśnij przycisk ok, a następnie wywołaj fragment ProgressDialog
otrzymuję ten błąd. Jeśli obracam tylko podczas wyświetlania fragmentu progressdialog, nie ma problemu. Korzystam z pakietu pomocy v4. Oto klas:
główną działalność:
public class MainActivity extends FragmentActivity implements OnFragmentAttachedListener, Callbacks{
boolean mResumed = false;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.main_id, new EmptyFragmentWithCallbackOnResume());
fragmentTransaction.commitAllowingStateLoss();
}
@Override
public void onTaskFinished()
{
// Hooray. A toast to our success.
Toast.makeText(this, "Task finished!", Toast.LENGTH_LONG).show();
// NB: I'm going to blow your mind again: the "int duration" parameter of makeText *isn't*
// the duration in milliseconds. ANDROID Y U NO ENUM?
}
@Override
public void OnFragmentAttached() {
} }
okcancel dialogfragment:
public class OkCancelDialogFragment<T> extends DialogFragment {
public final static String TITLE="title";
private OkCancelDialogEvents<T> buttonEvents;
private T[] params;
public OkCancelDialogFragment(String title, OkCancelDialogEvents<T> buttonEvents, T... params) {
this.buttonEvents=buttonEvents;
Bundle args = new Bundle();
args.putString(TITLE, title);
this.setArguments(args);
this.params=params;
}
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
this.setRetainInstance(true);
}
@Override
public void onDestroyView() {
if (getDialog() != null && getRetainInstance())
getDialog().setDismissMessage(null);
super.onDestroyView();
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
String title = getArguments().getString(TITLE);
return new AlertDialog.Builder(getActivity())
//.setIcon(R.drawable.alert_dialog_icon)
.setTitle(title)
.setPositiveButton("ok",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
buttonEvents.onPositiveClick(params);
}
}
)
.setNegativeButton("cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
buttonEvents.onNegativeClick();
}
}
)
.create(); }}
postęp fragment dialogowe:
public class TaskFragment extends DialogFragment{
// The task we are running.
GenericTask<?,?> mTask;
ProgressDialog mProgressDialog;
String title, message;
public void setTask(MyTask task)
{
mTask = task;
// TellsetFragment the AsyncTask to call updateProgress() and taskFinished() on this fragment.
mTask.setFragment(this);
}
public void setTitleMessage(String title, String message){
this.title=title;
this.message=message;
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Retain this instance so it isn't destroyed when MainActivity and
// MainFragment change configuration.
setRetainInstance(true);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
mProgressDialog= new ProgressDialog(getActivity());
mProgressDialog.setTitle(title);
mProgressDialog.setMessage(message);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCanceledOnTouchOutside(false);
return mProgressDialog;
}
// This is to work around what is apparently a bug. If you don't have it
// here the dialog will be dismissed on rotation, so tell it not to dismiss.
@Override
public void onDestroyView()
{
if (getDialog() != null && getRetainInstance())
getDialog().setDismissMessage(null);
super.onDestroyView();
}
// Also when we are dismissed we need to cancel the task.
@Override
public void onDismiss(DialogInterface dialog)
{
super.onDismiss(dialog);
// If true, the thread is interrupted immediately, which may do bad things.
// If false, it guarantees a result is never returned (onPostExecute() isn't called)
// but you have to repeatedly call isCancelled() in your doInBackground()
// function to check if it should exit. For some tasks that might not be feasible.
if (mTask != null)
mTask.cancel(false);
// You don't really need this if you don't want.
if (getTargetFragment() != null)
getTargetFragment().onActivityResult(MainFragment.TASK_FRAGMENT, Activity.RESULT_CANCELED, null);
}
@Override
public void onResume()
{
super.onResume();
// This is a little hacky, but we will see if the task has finished while we weren't
// in this activity, and then we can dismiss ourselves.
if (mTask == null)
dismiss();
}
// This is called by the AsyncTask.
public void updateProgress(int percent)
{
mProgressDialog.setProgress(percent);
}
// This is also called by the AsyncTask.
public void taskFinished()
{
// Make sure we check if it is resumed because we will crash if trying to dismiss the dialog
// after the user has switched to another app.
if (isResumed())
dismiss();
// If we aren't resumed, setting the task to null will allow us to dimiss ourselves in
// onResume().
mTask = null;
// Tell the fragment that we are done.
if (getTargetFragment() != null)
getTargetFragment().onActivityResult(MainFragment.TASK_FRAGMENT, Activity.RESULT_OK, null);
}
}
mainFragment:
public class MainFragment extends Fragment implements OkCancelDialogEvents<Void>, OnClickListener{
// This code up to onDetach() is all to get easy callbacks to the Activity.
private Callbacks mCallbacks = sDummyCallbacks;
private static Callbacks sDummyCallbacks = new Callbacks()
{
public void onTaskFinished() { }
};
@Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
if (!(activity instanceof Callbacks))
{
throw new IllegalStateException("Activity must implement fragment's callbacks.");
}
mCallbacks = (Callbacks) activity;
}
@Override
public void onDetach()
{
super.onDetach();
mCallbacks = sDummyCallbacks;
}
// Save a reference to the fragment manager. This is initialised in onCreate().
private FragmentManager mFM;
// Code to identify the fragment that is calling onActivityResult(). We don't really need
// this since we only have one fragment to deal with.
static final int TASK_FRAGMENT = 0;
// Tag so we can find the task fragment again, in another instance of this fragment after rotation.
static final String TASK_FRAGMENT_TAG = "task";
@Override
public void onCreate(Bundle savedInstanceState)
{
//this.setRetainInstance(true);
super.onCreate(savedInstanceState);
// At this point the fragment may have been recreated due to a rotation,
// and there may be a TaskFragment lying around. So see if we can find it.
mFM = getFragmentManager();
// Check to see if we have retained the worker fragment.
TaskFragment taskFragment = (TaskFragment)mFM.findFragmentByTag(TASK_FRAGMENT_TAG);
if (taskFragment != null)
{
// Update the target fragment so it goes to this fragment instead of the old one.
// This will also allow the GC to reclaim the old MainFragment, which the TaskFragment
// keeps a reference to. Note that I looked in the code and setTargetFragment() doesn't
// use weak references. To be sure you aren't leaking, you may wish to make your own
// setTargetFragment() which does.
taskFragment.setTargetFragment(this, TASK_FRAGMENT);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
return inflater.inflate(R.layout.fragment_main, container, false);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(savedInstanceState!=null)
Toast.makeText(getActivity(), savedInstanceState.getString("documents"), Toast.LENGTH_SHORT).show();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("documents", "teste");
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
// Callback for the "start task" button. I originally used the XML onClick()
// but it goes to the Activity instead.
view.findViewById(R.id.taskButton).setOnClickListener(this);
}
@Override
public void onClick(View v)
{
OkCancelDialogFragment<Void> dialog = new OkCancelDialogFragment<Void>("Teste", this);
dialog.setTargetFragment(this, 2);
dialog.show(getFragmentManager(), "basic_dialog");
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
if (requestCode == TASK_FRAGMENT && resultCode == Activity.RESULT_OK)
{
// Inform the activity.
mCallbacks.onTaskFinished();
}
}
@Override
public void onPositiveClick(Void... params) {
// We only have one click listener so we know it is the "Start Task" button.
// We will create a new TaskFragment.
TaskFragment taskFragment = new TaskFragment();
// And create a task for it to monitor. In this implementation the taskFragment
// executes the task, but you could change it so that it is started here.
MyTask task=new MyTask();
task.execute("one","two");
taskFragment.setTask(task);
taskFragment.setTitleMessage("File Download", "Downloading...");
// And tell it to call onActivityResult() on this fragment.
taskFragment.setTargetFragment(this, TASK_FRAGMENT);
// Show the fragment.
// I'm not sure which of the following two lines is best to use but this one works well.
taskFragment.show(mFM, TASK_FRAGMENT_TAG);
//mFM.beginTransaction().add(taskFragment, TASK_FRAGMENT_TAG).commit();
}
@Override
public void onNegativeClick() {
}}
Oto błędy:
12-12 11:24:52.144: E/AndroidRuntime(2451): FATAL EXCEPTION: main
12-12 11:24:52.144: E/AndroidRuntime(2451): java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
12-12 11:24:52.144: E/AndroidRuntime(2451): at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1327)
12-12 11:24:52.144: E/AndroidRuntime(2451): at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1338)
12-12 11:24:52.144: E/AndroidRuntime(2451): at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
12-12 11:24:52.144: E/AndroidRuntime(2451): at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
12-12 11:24:52.144: E/AndroidRuntime(2451): at android.support.v4.app.DialogFragment.show(DialogFragment.java:127)
12-12 11:24:52.144: E/AndroidRuntime(2451): at com.example.progressdialog.MainFragment.onPositiveClick(MainFragment.java:149)
12-12 11:24:52.144: E/AndroidRuntime(2451): at com.example.progressdialog.MainFragment.onPositiveClick(MainFragment.java:1)
12-12 11:24:52.144: E/AndroidRuntime(2451): at com.example.progressdialog.OkCancelDialogFragment$1.onClick(OkCancelDialogFragment.java:56)
12-12 11:24:52.144: E/AndroidRuntime(2451): at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:196)
12-12 11:24:52.144: E/AndroidRuntime(2451): at android.os.Handler.dispatchMessage(Handler.java:99)
12-12 11:24:52.144: E/AndroidRuntime(2451): at android.os.Looper.loop(Looper.java:123)
12-12 11:24:52.144: E/AndroidRuntime(2451): at android.app.ActivityThread.main(ActivityThread.java:4627)
12-12 11:24:52.144: E/AndroidRuntime(2451): at java.lang.reflect.Method.invokeNative(Native Method)
12-12 11:24:52.144: E/AndroidRuntime(2451): at java.lang.reflect.Method.invoke(Method.java:521)
12-12 11:24:52.144: E/AndroidRuntime(2451): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
12-12 11:24:52.144: E/AndroidRuntime(2451): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
12-12 11:24:52.144: E/AndroidRuntime(2451): at dalvik.system.NativeStart.main(Native Method)
Czy istnieje jakiś powód użycia 'setRetainInstance' w twoich fragmentach? – Luksprog
Cóż, używam tylko setReatainInstance w oknie dialogowymFragmenty. Czy nie znikają z rotacji, jeśli są ustawione na fałszywe? – Maxrunner
Możesz spróbować samemu przekonać się :). SetRetainInstance oznacza, że instancja fragmentu zostanie zachowana po zmianie konfiguracji, aby fragment nie został ponownie utworzony ponownie. – Luksprog