web-dev-qa-db-fra.com

Fragment doit être une classe statique publique pour être correctement recréé à partir d'un état d'instance.

Après la mise à jour vers le dernier référentiel de support, 

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'

Je reçois une exception bizarre.

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)

Dans mon BaseActivity class, j'ai créé un fragment réutilisable pouvant être utilisé dans une classe d'activité qui étend le 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");
    }

Retour à la MainActivty J'ai utilisé le fragment comme ça,

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

Le DialogNew class est ci-dessous,

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: Le même code fonctionne avec un référentiel de support plus ancien. 

14

L'erreur n'est pas particulièrement étrange. Si vous n'aviez pas eu cette erreur auparavant, ça était bizarre.

Android détruit et recrée des fragments dans le cadre d'un changement de configuration (par exemple, la rotation d'écran) et dans le cadre de la reconstruction d'une tâche si nécessaire (par exemple, l'utilisateur bascule vers une autre application. tente de revenir à votre application, le tout dans un délai de 30 minutes environ). Android n'a aucun moyen de recréer une sous-classe anonyme de DialogNew.

Créez donc une classe Java public régulière (ou une classe publicstatic imbriquée) qui étend DialogNew et contient votre logique métier, en remplacement de la sous-classe anonyme DialogNew que vous utilisez actuellement.

12
CommonsWare

Edit: Vous ne voulez probablement pas faire ça ... Voir les commentaires.

L'exemple de code ressemble à ce que j'avais suggéré sur ici et j'ai récemment découvert que la solution que j'avais là-bas ne fonctionnait plus. J'ai mis à jour ma réponse à Java7, mais si vous avez Java8, la solution est très simple:

(Je n'ai pas encore testé cela)

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

Puis dans l'activité principale:

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