2017-01-08 2 views
4

Présentation

Huh, c'est un difficile un. Au moins, je pense donc ... Pour éclaircir les choses:onClickListener ne fonctionne pas (correctement) après ConfigurationChange en fragments

Je n'ai trouvé aucune réponse à ma question après une recherche Internet (via Google).

Tout ce que j'ai trouvé des gens portant création des onClickListener 's pour leur View est faux. Je suppose que c'est le même problème dans mon cas, mais aucun des autres problèmes ne correspondait au mien. Semble être le même problème ... Il n'a pas de réponses.

Configuration

J'ai trois Fragment « s mis en place avec un ViewPager dans mon AppCompatActivity.

viewPager = (ViewPager) findViewById(R.id.main_view_pager); 
viewPager.setAdapter(new SectionsPagerAdapter(getSupportFragmentManager())); 

Traitée par le SectionsPagerAdapter.

public class SectionsPagerAdapter extends FragmentPagerAdapter { 

    SectionsPagerAdapter(FragmentManager fm) { 
     super(fm); 
    } 

    @Override 
    public Fragment getItem(int position) { 
     switch (position) { 
      case 0: 
       return MainTextHolder.newInstance(tabIndicatorOnClick); 
      case 1: 
       return PostWriter.newInstance(tabIndicatorOnClick); 
      case 2: 
       return TopPage.newInstance(tabIndicatorOnClick); 
      default: 
       return Error.newInstance(); 
     } 
    } 

    @Override 
    public int getCount() { 
     return 3; 
    } 

    // ... more methods 
} 

Dans chacun des Fragment « s que j'ai un contenu plus une coutume TabIndicator.

(le fichier xml Voici mes View 's pour l'indicateur)

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" > 

    <View 
     android:id="@+id/fragment_divider_one" 
     android:layout_width="0dp" 
     android:layout_height="match_parent" 
     android:layout_weight="1" 
     android:tag="one" /> 

    <View 
     android:id="@+id/fragment_divider_two" 
     android:layout_width="0dp" 
     android:layout_height="match_parent" 
     android:layout_weight="1" 
     android:tag="two" /> 

    <View 
     android:id="@+id/fragment_divider_three" 
     android:layout_width="0dp" 
     android:layout_height="match_parent" 
     android:layout_weight="1" 
     android:tag="three" /> 

</LinearLayout> 

Et puis je mis un OnClickListener pour chacun de ces View' s (séparateurs) dans la méthode de onCreateView(LayoutInflater inflater, ViewGroup Bundle savedInstanceState)Fragment. Le OnClickListener est déjà préparé dans mon Activity où j'instancie aussi mon ViewPager afin que je puisse changer le courant Item (Fragment/tab).

private final View.OnClickListener tabIndicatorOnClick = new View.OnClickListener() { 
    @Override 
    public void onClick(View view) { 
     if (view.getTag().toString().equals("one")) 
      viewPager.setCurrentItem(0, true); // second argument for smooth transition 
     else if (view.getTag().toString().equals("two")) 
      viewPager.setCurrentItem(1, true); 
     else if (view.getTag().toString().equals("three")) 
      viewPager.setCurrentItem(2, true); 
    } 
}; 

Je passe que OnClickListener au « s par Fragment mettre dans ma méthode newInstance(View.OnClickListener tabIndicatorOnClick).

Ceci est pour l'un des fragments. C'est identique pour les autres!

public static MainTextHolder newInstance(View.OnClickListener tabIndicatorOnClick) { 
    MainTextHolder fragment = new MainTextHolder(); 
    Bundle args = new Bundle(); 
    fragment.putIndicatorTabIndicatorOnClick(tabIndicatorOnClick); 
    fragment.setArguments(args); 
    fragment.setRetainInstance(true); 
    return fragment; 
} 

Ma méthode putIndicatorTabIndicatorOnClick(View.OnClickListener tabIndicatorOnClick) est un Void dans un Interface. Il applique simplement le OnClickListener au Class (Fragment).

@Override 
public void putIndicatorTabIndicatorOnClick(View.OnClickListener tabIndicatorOnClick) { 
    this.tabIndicatorOnClick = tabIndicatorOnClick; 
} 

fonctionne-t-il

Oui, ce fait. Cela fonctionne parfaitement bien ... jusqu'à ce qu'un ConfigurationChange arrive.Dans mon cas, je l'ai testé avec en changeant l'orientation.

Le problème

Qu'est-ce qui se passe après ConfigurationChange est que tout se passe normalement. La OnClickListener soit appliquée à toutes les View 's dans toutes les Fragment' s, mais la tabIndicatorOnClickOnClickListener est une référence d'objet null dans les deuxième et troisième Fragment.

Ainsi, dans PostWriter et TopPage il n'y a même pas vraiment un OnClickListener sur les View « s pour une raison quelconque. Mais ça va mieux: En MainTextHolderFragment le tabIndicatorOnClick n'est plus un null object reference, mais il ne change plus le ViewPagerItem. Il exécute le code, mais il ne fait pas défiler le Tab.

Lors de la mise « Ne pas garder les activités » ind Options du développeur de l'appareil et quitter l'application, tous les « s sont OnClickListenernull object references. Même ceux au MainTextHolder.

Résumé

Après l'ConfigurationChange le passé dans OnClickListener obtient mon premier Fragment, mais il n'y a pas fonctionner correctement et dans les l » Fragment reste il est même pas passé/a null object reference.

Le numéro peut être répliqué en détruisant le Activity puis en le rechargeant.

En fin de compte ...

... Je n'ai aucune idée de ce qui va mal. J'espère que j'ai bien structuré ma question.

peut être intéressant

minSdkVersion:

targetSdkVersion:

J'ai déjà un SpringIndicator sur mon ViewPager. Cela ne dispose pas d'un OnClickListener donc j'ai ajouté mes propres indicateurs "superposés" qui ont la fonctionnalité nécessaire OnClick. J'aime aussi le look de celui-ci, mais si vous pouvez me donner une meilleure solution pour mon TabIndicator, j'aimerais aussi avoir cela comme une réponse.

je suis tombé sur la solution pour mettre la méthode dans le xml:onClick attribut plusieurs fois, mais pour que je besoin de créer le même OnClickListener pour chaque Fragment qui est pas vraiment la bonne façon et il est pas non plus résolution mon problème, mais un moyen de contourner cela. - Je devrais passer le ViewPager dans mon Fragment et puis appeler cette méthode de xml qui est, comme je l'ai dit, juste une autre façon de le faire et non la façon dont je préfère le faire. Aussi je ne sais pas si ça marche. Je vais probablement le tester.

Autres OnClickListener fonctionnent toujours correctement dans les Fragment. Donc, comme je l'ai dit le problème réside dans le OnClickListener lui-même ne fonctionne pas bien/étant un null object reference.

+0

avez-vous essayé de changer dans votre manifeste android: configChanges = "orientation | screenSize"? –

+0

cela ressemble à un problème de cycle de vie Fragments. Pouvez-vous confirmer si vous pouvez répliquer l'erreur en activant Options développeur/Ne pas conserver les activités et en utilisant le bouton Accueil pour entrer et sortir de l'activité? –

+0

@MilosLulic oui – creativecreatorormaybenot

Répondre

2

Tout d'abord, bien fait pour écrire une description détaillée du problème. Beaucoup de questionneurs sur StackOverflow peuvent apprendre de la capacité de cette question à articuler le problème.

Cette erreur semble provenir de ne pas prendre en compte les modifications du cycle de vie des fragments. À l'intérieur d'un ViewPager, Fragments passera par différents états, y compris cachés et affichés quand ils sont temporairement hors écran, et mis en pause et repris s'ils sont hors écran et le système libère la mémoire, et finalement créé et détruit si le système Android décide d'enregistrer l'état d'instance de votre activité (par exemple à partir d'un changement de configuration). Dans ce dernier cas, Android tentera de restaurer l'état de vos fragments à partir de l'état d'instance enregistré dans votre ViewPager. Puisque vos fragments n'ont aucun moyen de sauvegarder les View.OnClickedListener, ils ont été passés dans les arguments, ils se retrouvent avec un pointeur null quand ils sont restaurés ce qui provoque votre erreur.

Pour corriger l'erreur, je suggère que vous ne transmettez pas le View.OnClickedListener comme paramètre aux fragments. Plutôt, exposer le OnClickListener à vos fragments via une méthode publique dans votre activité et que les fragments l'obtiennent eux-mêmes dans leur onResume(). De cette façon, vous pouvez garantir que les fragments auront une référence au View.OnClickedListener lorsqu'ils sont dans un état de reprise. Donc, votre onResume() pourrait ressembler à ceci:

@Override 
public void onResume() { 
    OnClickListener onClickListener = ((MyActivity) getActivity).getOnClickListener(); 
    putIndicatorTabIndicatorOnClick(onClickListener); 

}

Depuis que vous avez défini dans onResume(), vous devez supprimer dans onPause():

@Override 
public void onPause() { 
    //set the onClickListener to null to free up the resource 
} 

En utilisant onResume() et onPause() comme celui-ci libérer des ressources auprès des auditeurs est l'approche recommandée dans le guide du développeur.

Bien sûr, vous devrez vous assurer que votre activité gère également son propre cycle de vie en vérifiant l'état enregistré dans onCreate(Bundle savedInstanceState) et en le restaurant de manière appropriée.

S'il vous plaît noter que l'état de l'instance sauvegarde et la restauration peut être déclenchée dans plusieurs façons:

  1. Configuration changement d'état (comme la rotation du téléphone et de provoquer l'activité à afficher dans le paysage plutôt que portrait
  2. Activation des options du développeur/Ne gardez pas les activités et n'utilisez pas la maison pour placer votre application dans les applications récentes, puis revenez à votre application.

Vous pourrez utiliser l'une de ces méthodes pour déterminer si vous avez correctement géré les cycles de vie de vos activités et fragments.

+0

Tout d'abord, merci pour votre réponse. Beaucoup d'informations super. Bien que je ne l'ai pas réussi à travailler. J'ai compris que dans le cycle de vie la méthode 'onCreateView()' est appelée avant ma méthode 'onResume()', donc mon tabIndicator est toujours nul lorsqu'il est appliqué à 'View'. Je sais comment contourner cela, mais y a-t-il un meilleur moyen que de mettre onClick dans la méthode 'onResume()' que vous connaissez? – creativecreatorormaybenot

+0

le définir dans 'onResume()' est la pratique recommandée. Vous pouvez consulter le cycle de vie Gestion de l'activité et les sections pertinentes du Guide du développeur. Fragments/Activités ne sont pas comme POJO où vous pouvez toujours passer dans les dépendances en tant que paramètres du constructeur –

+0

Great! Merci de votre aide. Cela a eu de très bonnes informations supplémentaires. – creativecreatorormaybenot