-2

Mon application est structurée avec un tiroir de navigation et 4 fragments. L'ancienne version de cette application utilise des activités, j'ai donc besoin de transformer les activités en fragments.Android force Fragment redessiner lorsque l'écran change d'orientation

Pour l'instant tout fonctionne bien mais dans un Fragment j'ai 80 Buttons que l'utilisateur peut définir la couleur de texte et le fond et la méthode qui appellent DialogActivity est dans le seul MainActivity qui gèrent l'ensemble Fragments et onActivityResults appelé par Dialogs à l'intérieur Fragments pour gérer changement d'utilisateur.

Le problème se produit lorsque l'orientation de l'écran passe en mode paysage. Si je presse Buttons et définir le texte et la couleur fonctionne avec le portrait mais si un changement d'orientation de l'écran avec le paysage je reçois quelque chose comme "ombre" comme fond et les boutons que j'ai ne change pas leurs propriétés quand je clique eux, mais si je fais tourner à nouveau l'écran, les boutons modifiés sont devenus visibles. Les choses étranges est que dans le fond Fragment je vois les boutons avec les bonnes mises à jour, mais pas dans le haut ... (je poste une photo, c'est difficile à expliquer)

Les anciens boutons que je change restent modifiés car je l'enregistrer à la DB, mais avec le paysage, je ne peux pas mettre à jour les autres boutons che ...

enter image description here

CODE:

MainActivity.java

public class MainActivity extends AppCompatActivity 
     implements NavigationView.OnNavigationItemSelectedListener { 


    static String clickedButtonViewId; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     setContentView(R.layout.activity_main); 

     Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 
     setSupportActionBar(toolbar); 


     DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); 
     ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
       this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); 
     drawer.setDrawerListener(toggle); 
     toggle.syncState(); 

     NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); 
     navigationView.setNavigationItemSelectedListener(this); 

     if (findViewById(R.id.content_frame) != null){ 

      getSupportFragmentManager().beginTransaction() 
        .add(R.id.content_frame, new OrarioFragment()).commit(); 
     } 
    } 

    @Override 
    public void onBackPressed() { 
     DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); 
     if (drawer.isDrawerOpen(GravityCompat.START)) { 
      drawer.closeDrawer(GravityCompat.START); 
     } else { 
      super.onBackPressed(); 
     } 
    } 

    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     // Inflate the menu; this adds items to the action bar if it is present. 
     getMenuInflater().inflate(R.menu.main, menu); 
     return true; 
    } 

    @Override 
    public boolean onOptionsItemSelected(MenuItem item) { 
     // Handle action bar item clicks here. The action bar will 
     // automatically handle clicks on the Home/Up button, so long 
     // as you specify a parent activity in AndroidManifest.xml. 
     int id = item.getItemId(); 

     //noinspection SimplifiableIfStatement 
     if (id == R.id.action_settings) { 
      return true; 
     } 

     return super.onOptionsItemSelected(item); 
    } 

    @SuppressWarnings("StatementWithEmptyBody") 
    @Override 
    public boolean onNavigationItemSelected(MenuItem item) { 
     // Handle navigation view item clicks here. 
     int id = item.getItemId(); 

     FragmentManager fragmentManager = getSupportFragmentManager(); 

     if (id == R.id.nav_orario) { 

      fragmentManager.beginTransaction() 
        .replace(R.id.content_frame, new OrarioFragment()) 
        .commit(); 

     } else if (id == R.id.nav_calendario) { 

      fragmentManager.beginTransaction() 
        .replace(R.id.content_frame, new CalendarioFragment()) 
        .commit(); 

     } else if (id == R.id.nav_voti) { 

      fragmentManager.beginTransaction() 
        .replace(R.id.content_frame, new VotiFragment()) 
        .commit(); 

     } else if (id == R.id.nav_registrazioni) { 

      fragmentManager.beginTransaction() 
        .replace(R.id.content_frame, new RegistrazioniFragment()) 
        .commit(); 

     } else if (id == R.id.nav_share) { 

     } else if (id == R.id.nav_send) { 

     } 

     DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); 
     drawer.closeDrawer(GravityCompat.START); 
     return true; 
    } 


    public void addMateria(View v){ 

     /* Prendo il nome della risorsa cosi nel ricompilare il progetto non perdo * 
     * tutti i riferimenti ai bottoni salvati nel database      */ 

     clickedButtonViewId = getResources().getResourceEntryName(v.getId()); 

     //StartActivityForResult perche mi aspetto la materia inserita dall'altra activity 
     Intent myIntent = new Intent(MainActivity.this, ActivityAddMateria.class); 
     startActivityForResult(myIntent, 1); 
     //onStop(); 
    } 

    //Take back data from ActivityAddMateria 
    public void onActivityResult(int requestCode, int resultCode, Intent data) { 
     if(requestCode == 1) { 
      if (resultCode == RESULT_OK) { 

       MySQLiteHelper db = new MySQLiteHelper(this); 

       //Cambio subito il Button 
       int resId = getResources().getIdentifier(clickedButtonViewId, "id", getPackageName()); 
       final Button clickedtextView = (Button) findViewById(resId); 

       String result = data.getStringExtra("result"); //Take the materia from Dialog 
       int color = data.getIntExtra("color", 1); //Take the color from Dialog 

       //Controllo se il Button è già presente nel db se presente aggiorno se non presente inserisco 
       boolean modifica = db.Exists(clickedButtonViewId); 

       //Se voglio ripristinare il bottone di default 
       if (color == getResources().getColor(R.color.blue_orario)) { 

        //Ripristino la grafica di Default 
        Drawable style = setButtonColor(color); 
        clickedtextView.setBackground(style); 
        clickedtextView.setText("New"); 

        //Se la materia è nel database la cancello 
        if (modifica) { 

         db.deleteSingleMateria(clickedButtonViewId); 

        } 

       } else { 
        //Quando inserisco un normale bottone colorato 
        if (!modifica) { 

         //Materia da inserire in un nuovo spazio 
         db.addMateriaToDb(new Materia(clickedButtonViewId, result, color)); 

        } else { 

         //Materia già presente nel Button quindi aggiorno la materia 
         db.updateMateria(new Materia(clickedButtonViewId, result, color)); 
         Toast.makeText(getApplicationContext(), "Materia modificata!", 
           Toast.LENGTH_LONG).show(); 
        } 

        //Inserisco la materia nel DB dei voti_media 
        db.addMateriaVotiFromOrario(new MaterieVoti(result, 0.0)); 

        clickedtextView.setText(result); 
        //clickedtextView.setBackgroundColor(color); 
        //clickedtextView.getBackground().setColorFilter(color, PorterDuff.Mode.MULTIPLY); 
        Drawable style = setButtonColor(color); 
        clickedtextView.setBackground(style); 
       } 
      } 

      if (resultCode == RESULT_CANCELED) { 
       //Nessuna materia inserita 
      } 

     } 
    }//onActivityResult 

EDIT

Ok, j'ai trouvé le problème.

Dans le MainActivity j'ai cette ligne de code pour la force le premier fragment à afficher

if (findViewById(R.id.content_frame) != null){ 

      getSupportFragmentManager().beginTransaction() 
        .add(R.id.content_frame, new OrarioFragment()).commit(); 
     } 

Et lorsque l'orientation de l'écran changer le MainActivity est recréée et si la charge le même fragment sur le vieux fragment parce que J'utilise .add()

Alors, comment puis-je définir le fragment à afficher lorsque l'application commence à éviter ce problème?

Je me trompe dans la gestion du tiroir?

+0

Montrez votre code. – Bryan

+0

@Brian a ajouté le code, si besoin en plus juste dire :) – Dario

+0

Pouvez-vous partager votre mise en page aussi? – JRG

Répondre

1

Android Documentation expliquant comment sauvegarder les états et un très bon article sur la sauvegarde des états dans l'activité et le fragment.

Sauvegarde et restauration de l'état d'activité Il y a quelques scénarios dans lesquels votre activité est détruite à cause de comportement normal de l'application, par exemple lorsque l'utilisateur appuie sur le bouton Retour ou votre activité signale sa propre destruction en appelant la méthode finish().Le système peut également détruire le processus contenant votre activité pour récupérer la mémoire si l'activité est à l'état Arrêté et n'a pas été utilisée depuis longtemps ou si l'activité de premier plan nécessite plus de ressources. Lorsque votre activité est détruite parce que l'utilisateur appuie sur Retour ou que l'activité se termine, le concept du système de cette instance d'activité a disparu pour toujours car le comportement indique que l'activité n'est plus nécessaire. Toutefois, si le système détruit l'activité en raison des contraintes du système (plutôt que du comportement normal de l'application), le système se souvient que l'instance d'activité a disparu, de sorte que si l'utilisateur y retourne, le système crée une nouvelle instance de l'activité utilisant un ensemble de données enregistrées qui décrit l'état de l'activité lorsqu'elle a été détruite. Les données sauvegardées que le système utilise pour restaurer l'état précédent s'appellent l'état d'instance et constituent une collection de paires clé-valeur stockées dans un objet Bundle.

Par défaut, le système utilise l'état de l'instance de regroupement pour enregistrer des informations sur chaque objet View dans votre présentation d'activité (telle que la valeur de texte entrée dans un widget EditText). Ainsi, si votre instance d'activité est détruite et recréée, l'état de la mise en page est restauré à son état précédent sans que vous ayez besoin de code. Cependant, votre activité peut contenir plus d'informations d'état que vous souhaitez restaurer, telles que les variables de membre qui suivent la progression de l'activité dans l'activité.

Enregistrez votre état d'activité Comme votre activité commence à arrêter, le système appelle la méthode onSaveInstanceState() afin que votre activité peut enregistrer des informations d'état avec une collection de paires clé-valeur. L'implémentation par défaut de cette méthode enregistre des informations transitoires sur l'état de la hiérarchie des vues de l'activité, telles que le texte d'un widget EditText ou la position de défilement d'un widget ListView. Votre application doit implémenter le rappel onSaveInstanceState() après la méthode onPause(), et avant onStop(). N'implémentez pas ce rappel dans onPause().

Attention: Vous devez toujours appeler l'implémentation de la classe de onSaveInstanceState() pour que l'implémentation par défaut puisse enregistrer l'état de la hiérarchie de vue. Pour enregistrer des informations d'état supplémentaires pour votre activité, vous devez remplacer onSaveInstanceState() et ajouter des paires clé-valeur à l'objet Bundle qui est enregistré dans le cas où votre activité est détruite de manière inattendue. Par exemple:

static final String STATE_SCORE = "playerScore"; 
static final String STATE_LEVEL = "playerLevel"; 
... 


@Override 
public void onSaveInstanceState(Bundle savedInstanceState) { 
    // Save the user's current game state 
    savedInstanceState.putInt(STATE_SCORE, mCurrentScore); 
    savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel); 


    // Always call the superclass so it can save the view hierarchy state 
    super.onSaveInstanceState(savedInstanceState); 
} 

Note: Pour que le système Android pour restaurer l'état des vues de votre activité, chaque vue doit avoir un identifiant unique, fourni par l'androïde: attribut id.

Pour enregistrer des données persistantes, telles que les préférences utilisateur ou les données d'une base de données, vous devez saisir les opportunités appropriées lorsque votre activité est au premier plan. Si aucune opportunité ne se présente, vous devez enregistrer ces données lors de la méthode onStop().

Restaurez votre état d'activité Lorsque votre activité est recréée après avoir été précédemment détruite, vous pouvez récupérer votre état enregistré à partir du Bundle que le système passe à votre activité. Les méthodes de rappel onCreate() et onRestoreInstanceState() reçoivent toutes deux le même regroupement qui contient les informations sur l'état de l'instance. Étant donné que la méthode onCreate() est appelée, que le système crée une nouvelle instance de votre activité ou en recréer une précédente, vous devez vérifier si l'état Bundle est null avant de tenter de le lire.Si elle est nulle, le système crée une nouvelle instance de l'activité, au lieu de restaurer une précédente qui a été détruite.

Par exemple, l'extrait de code suivant montre comment vous pouvez restaurer des données d'état dans onCreate():

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); // Always call the superclass first 


    // Check whether we're recreating a previously destroyed instance 
    if (savedInstanceState != null) { 
     // Restore value of members from saved state 
     mCurrentScore = savedInstanceState.getInt(STATE_SCORE); 
     mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL); 
    } else { 
     // Probably initialize members with default values for a new instance 
    } 
    ... 
} 

Au lieu de restaurer l'état pendant onCreate(), vous pouvez choisir de mettre en œuvre onRestoreInstanceState(), que le système appelle après la méthode onStart(). Le système appelle onRestoreInstanceState() que s'il y a un état enregistré à restaurer, de sorte que vous n'avez pas besoin de vérifier si l'ensemble est nul:

public void onRestoreInstanceState(Bundle savedInstanceState) { 
    // Always call the superclass so it can restore the view hierarchy 
    super.onRestoreInstanceState(savedInstanceState); 


    // Restore state members from saved instance 
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE); 
    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL); 
} 

Attention: Il faut toujours appeler la mise en œuvre superclasse de onRestoreInstanceState () de sorte que l'implémentation par défaut peut restaurer l'état de la hiérarchie de vues.

+0

Oui, mais pourquoi j'ai une "ombre" comme la photo lorsque l'orientation de l'écran change et pourquoi je ne peux pas voir le changement de bouton avec le paysage de l'écran. Si je reviens à portrait tout fonctionne parfaitement, c'est le paysage le problème et ne pense pas que le problème est le InstanceState .... non? Avec l'instanceState je ne peux pas éviter l'effet "shadow" avec le paysage – Dario

+0

Avez-vous la même disposition pour l'orientation ou vous avez la disposition et la mise en page-dossier de terrain pour les orientations? – JRG

+0

même mise en page, je n'ai pas deux mise en page pour l'orientation de l'écran différent. Il est possible que je doive détruire des fragments lorsque l'orientation de l'écran change? Peut-être que les effets "d'observation" sont-ils un chevauchement d'une interface utilisateur à deux fragments? Parce que l'ombre en arrière-plan si je change un bouton avec paysage je vois la mise à jour dans le fond d'ombre, mais pas dans le haut (D) – Dario