Nous prendrons comme exemple une activité qui possède deux fragments : une liste et une zone détail.
Le but est de pouvoir ouvrir un écran de détail lorsque l’on clique sur un élément de la liste. En mode portrait nous avons un enchaînement de deux écrans alors qu’en mode paysage nous avons un seul écran présentant la liste et le détail.
Les layouts
Pour proposer une disposition différente en fonction de l’orientation de l’écran, nous nous appuyons sur le mécanisme proposé par le système Android pour personnaliser les fichiers de ressources en fonction de l’orientation ou de la densité de l’écran, de la langue…
Le premier layout nommé fragment_joueurs.xml, définit l’écran qui s’affichera en mode paysage. Il est placé dans le répertoire /res/layout-land et il définit un fragment ListeJoueurFragment et un FrameLayout dans lequel les détails seront placés.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment class="com.ehret.score.fragment.joueur.ListeJoueurFragment" android:id="@+id/joueurListe" android:layout_weight="1" android:layout_width="0px" android:layout_height="match_parent" /> <FrameLayout android:id="@+id/joueurDetail" android:layout_weight="1" android:layout_width="0px" android:layout_height="match_parent" android:background="?android:attr/detailsElementBackground" /> </LinearLayout>
Un layout portant le même nom est créé dans le répertoire res/layout pour définir l’écran qui sera affiché en mode paysage (notre liste)
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment class="com.ehret.score.fragment.joueur.ListeJoueurFragment" android:id="@+id/titles" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>
Une activité est définie pour charger le layout fragment_joueurs.xml défini dans la première partie. Comme cette activité utilise des fragments, elle hérite de la classe FragmentActivity
public class JoueursActivity extends FragmentActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_gestion_user); } }
Fragment affichant la liste
Ce nouveau fragment a plusieurs rôles
- il scrupte les éléments chargés par l’activité pour savoir si la liste et le détail sont présents ensemble ou non
- il sauvegarde le dernier élément sélectionné dans la liste pour que ce dernier ne soit pas perdu si l’activité est reconstruite (interruption par une autre application ou une rotation de l’écran…)
- en fonction de l’orientation de l’écran il affiche le détail correspondant à l’élément sélectionné sur le même écran (mode paysage) ou ouvre une nouvelle activité (mode portrait)
public class ListeJoueurFragment extends ListFragment { /** * un premier état pour indiquer si on est en mode ListeDetail ou Liste simple */ protected boolean mListeDetail = false; /** * Un second pour garder une référence sur l'élément sélectionné */ protected int mJoueurSelectionneIndex = 0; /** * Les elements affciches sont figés dans notre exemple */ public static String[] TEST = new String[]{"test", "test1"}; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); //Si on a un état sauvegardé on met a jour l'élément par défaut if(savedInstanceState != null) { mJoueurSelectionneIndex = savedInstanceState.getInt("joueurSelectionneIndex", 0); } //En fonction du layout affiché on connait le mode d'ouverture View joueurDetailFragment = getActivity().findViewById(R.id.joueurDetail); mListeDetail = joueurDetailFragment != null && joueurDetailFragment.getVisibility() == View.VISIBLE; //On specifie qu'el adapter sera rattaché à notre liste pour afficher les éléments setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_activated_1, TEST)); //Si on est en mode Liste/Detail on affiche le détail correspondant à l' if (mListeDetail) { getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); selectionJoueur(mJoueurSelectionneIndex); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); //a la suspension de l'écran on sauvegarde l'index courant outState.putInt("joueurSelectionneIndex", mJoueurSelectionneIndex); } @Override public void onListItemClick(ListView l, View v, int position, long id) { //un clic permet d'affiche le détail selectionJoueur(position); } /** * Fonction Helper qui va se charger d’afficher les données correspodant à l’item * sélectionné dans la liste */ void selectionJoueur(int index) { mJoueurSelectionneIndex = index; if (mListeDetail) { //Mise a jour de la liste getListView().setItemChecked(index, true); DetailJoueurFragment detailJoueur = (DetailJoueurFragment) getFragmentManager().findFragmentById(R.id.joueurDetail); if (detailJoueur == null || detailJoueur.getIdJoueurEnCours() != index) { detailJoueur = DetailJoueurFragment.newInstance(index); //On remplace le fragment existant avec le nouveau FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.replace(R.id.joueurDetail, detailJoueur); ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); ft.commit(); } } else { //En mode liste simple le detail s'affiche dans une nouvelle activité Intent intent = new Intent(); intent.setClass(getActivity(), DetailJoueurActivity.class); //on passe à l'actvité l'id du joeur sélectionné intent.putExtra("idJoueur", index); startActivity(intent); } } }
Activité affichant le détail en mode portrait
En mode portrait, l’affichage du détail est géré par une activité spécifique qui chargera le fragment correspondant.
public class DetailJoueurActivity extends FragmentActivity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState == null) { DetailJoueurFragment detailJoueur = DetailJoueurFragment.newInstance(getIntent().getIntExtra("idJoueur",0)); getSupportFragmentManager().beginTransaction().add(android.R.id.content, detailJoueur).commit(); } } }
Fragment affichant le détail
Comme la zone de détail est toujours dépendante de l’appelant, le fragment lié sera créé par la méthode statique newInstance(int index). Cette manière d’instancier le fragment simplifie le passage des paramètres d’initialisation (ici l’index sélectionné dans la liste parente)
public class DetailJoueurFragment extends Fragment { /** * Permet de créer une nouvelle instance de notre fragment */ public static DetailJoueurFragment newInstance(int index) { DetailJoueurFragment f = new DetailJoueurFragment(); //Recupération de l’index à afficher dans les arguments d’appel Bundle args = new Bundle(); args.putInt("idJoueur", index); f.setArguments(args); return f; } public int getIdJoueurEnCours() { return getArguments().getInt("idJoueur", 0); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (container == null) { // Si le ViewGroup n'est pas renseigné on ne cherche pas à en créer un // car sur cet écran l'appelant doit passer par newInstance... return null; } ScrollView scroller = new ScrollView(getActivity()); TextView text = new TextView(getActivity()); scroller.addView(text); text.setText(ListeJoueurFragment.TEST[getIdJoueurEnCours()]); return scroller; } }
Cet exemple montre un exemple simple d’un fragment liste /détail.
Good exemple
RépondreSupprimer