Frammento deve essere un pubblico statico classe per essere correttamente ricreato da istanza dello stato

Dopo l’aggiornamento all’ultimo supporto repository,

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'

Io sono sempre strano eccezione.

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)

Nel mio BaseActivity classe, ho creato un riutilizzabili frammento che può essere utilizzato in attività di classe che estende la 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");
    }

Indietro per il MainActivty ho usato il frammento come questo,

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

Il DialogNew classe è al di sotto,

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: Lo stesso codice funziona con il vecchio supporto repository.

  • Perché DialogNew astratto? Non è possibile creare un’istanza di una classe astratta.
  • va bene. Quando si fa questo tipo di cose sì, hai ragione sul punto che non è possibile creare un’istanza di un astratto invece è inizializzazione di una classe anonima che si estende in che classe Astratta. Insomma non c’è alcun problema con questo.
  • di fronte a stesso errore nella libreria di supporto versione 24.2.1
  • aggiungere poi ! , qual è la soluzione ! Ho avuto il vecchio codice , e si tenta di aggiornare il supporto per le librerie , e si blocca con la ragione , che cosa dobbiamo fare ?



2 Replies
  1. 15

    L’errore non è particolarmente strano. Se non è stato sempre questo errore prima, che è stato strano.

    Android distrugge e ricrea frammenti come parte di un cambiamento di configurazione (ad esempio, la rotazione dello schermo) e come parte della ricostruzione di un compito, se necessario (ad esempio, l’utente passa ad un’altra app, l’app del processo è terminato, mentre è in background, quindi l’utente tenta di tornare alla tua app, tutti entro 30 minuti o così). Android non ha la possibilità di ricreare un anonimo sottoclasse di DialogNew.

    Così, fare una regolare public classe Java (o un public static classe nidificata) che si estende DialogNew e ha la logica di business, in sostituzione di anonimo sottoclasse di DialogNew che si sta utilizzando attualmente.

    • Grazie per la risposta! Sono d’accordo con la tua logica. Adesso il mio DialogNew già si estende DialogFragment, quindi non posso ampliare obbiettivo principale l’allevamento causare l’obbiettivo principale l’allevamento stessa si estende l’Attività. Non so come, ma lo stesso codice funziona correttamente su con il vecchio supporto repo. Voglio solo tenere la finestra e classe di attività separate.
    • Che cosa se il vostro DialogFragment sottoclasse è parte di una libreria Android e non volete esporre in pubblico API?
    • Purtroppo, dato il modo in cui Java funziona, i vostri obiettivi sono reciprocamente incompatibili, se la “public API” è definito come “cose contrassegnato come public“. Nella documentazione, si può notare che questo frammento non è inteso per l’uso da parte dei consumatori della biblioteca. Si potrebbe anche mettere una custom Panno check-in libreria per avvertire gli sviluppatori in modo proattivo, anche se questo processo viene documentato e in uno stato di flusso a destra ora. Ma, da un punto di vista tecnico, dato il modo in cui hanno implementato frammenti, si ha bisogno di loro per essere instantiatable via un public zero argomento del costruttore.
  2. 0

    Edit: probabilmente non Si vuole fare questo… Vedere i commenti.

    L’esempio di codice simile a quello che mi aveva suggerito sopra qui, e ho anche scoperto di recente che la soluzione che ho avuto non c’era più. Ho aggiornato la mia risposta per Java7, ma se avete Java8 la soluzione è super facile:

    (Non ho testato ancora)

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

    Quindi nell’attività 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);
            }
    }
    • Questo non funziona per un paio di motivi. Primo, mSuccess e mCancel non sopravvivere il Frammento essere stoccate. Se avete fatto questo Frammento conservato, si ha un peggioramento del problema: Quando si chiama dialogo.setSuccess e passare un’espressione lambda, si in realtà ho creato una classe interna di obbiettivo principale l’allevamento. Quando l’attività è ricreato su config cambiare, avrai una perdita di memoria, e i metodi di callback tenterà di chiamare i metodi distrutto Attività.

Lascia un commento