5

所以我一直试图了解如何解决这个问题,但我无法真正解决它。现在我有一个带有一个按钮的片段。当您按下此按钮时,它将启动DialogFragment带有确定/取消按钮的自定义。

如果我按下 ok 按钮,它将启动另一个 custom DialogFragment,这次它是一个ProgressDialog片段。问题是,当我旋转时出现 ok/cancel 对话框,然后按下 ok 按钮然后调用ProgressDialog片段,我得到这个错误。如果我只在显示进度对话框片段时旋转,则根本没有问题。我正在使用支持包 v4。这是课程:

主要活动:

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 对话框片段:

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(); }} 

进度对话框片段:

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);
}

}

主要片段:

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() {


}}

这是错误:

   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)
4

1 回答 1

1

这似乎是兼容性包的一个错误(尚未解决)。无论如何,您可以通过修改代码以更好地处理片段之间的通信来避免该错误。我已经修改了您的示例项目(可以在此处找到)。关于这一点,我不知道您的示例有多简单,但是如果所有回调都指向活动,那么您应该让活动类处理片段(例如启动对话框)以及它们之间的通信,因为它“ sees”并知道其中所有片段的状态。

于 2012-12-14T14:37:41.477 回答