web-dev-qa-db-ja.com

フラグメントは、インスタンス状態から適切に再作成されるパブリック静的クラスである必要があります

最新のサポートリポジトリに更新した後、

compile 'com.Android.support:appcompat-v7:24.2.0'
compile 'com.Android.support:design:24.2.0'
compile 'com.Android.support:percent:24.2.0'
compile 'com.Android.support:recyclerview-v7:24.2.0'

奇妙な例外が発生しています。

Java.lang.IllegalStateException: Fragment null must be a public static class to be  properly recreated from instance state.
at Android.support.v4.app.BackStackRecord.doAddOp(BackStackRecord.Java:435)
at Android.support.v4.app.BackStackRecord.add(BackStackRecord.Java:414)
at Android.support.v4.app.DialogFragment.show(DialogFragment.Java:154)
at com.androidapp.base.BaseActivity.showDialogFragment(BaseActivity.Java:78)
at com.androidapp.MainActivity.showNewDialog(MainActivity.Java:304)
at com.androidapp.MainActivity$6.onClick(MainActivity.Java:228)

BaseActivityクラスで、BaseActivtyを拡張するアクティビティクラスで使用できる再利用可能なフラグメントを作成しました

public void showDialogFragment(DialogFragment newFragment) {
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        Fragment prev = getSupportFragmentManager().findFragmentByTag("dialog");
        if (prev != null) {
            ft.remove(prev);
        }
        ft.addToBackStack("dialog");
        newFragment.show(ft, "dialog");
    }

MainActivtyに戻り、このようなフラグメントを使用しました。

public class MainActivity extends BaseActivity {

    @SuppressLint("ValidFragment")
        public void showNewDialog(int type, String title, String message) {
            final DialogNew dialog = new DialogNew() {
                @Override
                public void success(boolean isLandscape) {
                    .......
                }

                @Override
                public void cancel() {

                }
            };
            dialog.setArgs(title, message);
            super.showDialogFragment(dialog);
        }
}

DialogNewクラスは次のとおりです。

public abstract class DialogNew extends DialogFragment {

    private View rootView;

    private String title;
    private String message;

    public void setArgs(String title, String message) {
        Bundle args = new Bundle();
        args.putString("title", title);
        args.putString("message", message);
        setArguments(args);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(STYLE_NO_TITLE, 0);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        rootView = inflater.inflate(R.layout.fragment_new_dialog, container, false);

        init();
        setListeners();

        return rootView;
    }

    public abstract void success(boolean isLandscape);

    public abstract void cancel();
}

PS:古いサポートリポジトリでも同じコードが機能します。

14

エラーは特に奇妙ではありません。以前にこのエラーが発生していなかった場合、thatは奇妙でした。

Androidは、構成の変更(画面の回転など)の一部として、必要に応じてタスクの再構築の一部として(たとえば、ユーザーが別のアプリに切り替え、アプリのプロセスがバックグラウンドにある間に終了し、その後ユーザーがすべて30分以内にアプリに戻ろうとします)。 Androidには、DialogNewの匿名サブクラスを再作成する手段がありません。

そのため、通常のpublic Javaクラス(またはpublicstaticネストされたクラス)を作成して、DialogNewを拡張し、現在使用しているDialogNewの匿名サブクラスを置き換えるビジネスロジック。

15
CommonsWare

Edit:おそらくこれをしたくないでしょう..コメントを参照してください。

コードサンプルは、私が here で提案したものに似ています。また、最近、私が持っていたソリューションがもう機能していないことも発見しました。 Java7の回答を更新しましたが、Java8を使用している場合、ソリューションは非常に簡単です。

(私はまだこれをテストしていません)

public class DialogNew extends DialogFragment {
    private View rootView;
    private String title;
    private String message;

    // Do nothing by default
    private Consumer mSuccess = (boolean b) -> {};
    private Runnable mCancel = () -> {};

    public void setArgs(String title, String message) {
        Bundle args = new Bundle();
        args.putString("title", title);
        args.putString("message", message);
        setArguments(args);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(STYLE_NO_TITLE, 0);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.fragment_new_dialog, container, false);
        // use mSuccess.accept(boolean) when needed
        init();
        setListeners();
        return rootView;
    }

    public void setSuccess(Consumer success) {
        mSuccess = success;
    }

    public void setCancel(Runnable cancel) {
        mCancel = cancel;
    }
}

次に、メインアクティビティで:

public class MainActivity extends BaseActivity {
        public void showNewDialog(int type, String title, String message) {
            final DialogNew dialog = new DialogNew();
            dialog.setArgs(title, message);
            dialog.setSuccess((boolean isLandscape) -> {
                //....
            });
            super.showDialogFragment(dialog);
        }
}
0
Tim Rae