Pages

dimanche 18 novembre 2012

TableLayout sous Android ou comment créer des tableaux

Nous allons voir comment mettre en place un tableau dans une application Android. Le point de départ est le layout TableLayout. Le principe est un peu le même que pour le HTML. Le TableLayout défini un tableau, dans lequel nous allons rajoutés des lignes (TableRow) qui contiendront les composants.

Par défaut les composants vont s’auto organiser pour occuper tout l’espace laissé par le parent. La largeur d’une ligne sera d’ailleurs toujours définie en fonction du TableLayout (MATCH_PARENT) et la hauteur s’ajustera au contenu (WRAP_CONTENT). Pour les composants la largeur est également ajustée sur le parent mais la hauteur elle, peut se caler sur le conteneur

Les composants doivent être ajoutés dans l’ordre d’affichage à un TableRow. Il est possible de fusionner (span) plusieurs colonnes mais la fusion de ligne n'est pas disponible.

Plusieurs paramètres permettent de personnaliser l’affichage. Les colonnes peuvent être définies comme
  • extensibles (setColumnStretchable()). Ceci permet par exemple de gérer la largeur des colonnes tout en laissant une occuper l’espace vide pour arriver à la largeur du conteneur 
  • rétractables (setColumnShrinkable()). Qui aura l’effet inverse. 
Le Table Layout ne gère pas l’affichage des bordures, des lignes, des colonnes ou des cellules. Nous allons donc voir comment le faire grâce à plusieurs exemples.

Comment créer un tableau dans un layout
Nous allons nous cantonner dans un premier temps à la structure du tableau

<TableLayout style="@style/frag1TableLayout" > 
    <TableRow style="@style/frag1HeaderTableRow"> 
        <TextView style="@style/frag1HeaderCol" android:text="Credit"/> 
        <TextView style="@style/frag1HeaderCol" android:text="Debit"/> 
    </TableRow> 

    <TableRow style="@style/frag1TableRow"> 
        <TextView style="@style/frag1Col" android:text="124"/> 
        <TextView style="@style/frag1Col" android:text="-125"/> 
    </TableRow> 

    <TableRow style="@style/frag1TableRow"> 
        <TextView style="@style/frag1Col" android:text="345"/> 
        <TextView style="@style/frag1Col" android:text="-120"/> 
    </TableRow> 

    <TableRow style="@style/frag1TableRow"> 
        <TextView style="@style/frag1Col" android:text="245"/> 
        <TextView style="@style/frag1Col" android:text="-120"/> 
    </TableRow>
</TableLyout>

Comme vous pouvez le constater j’ai utilisé un style pour chacune des colonnes afin de ne pas surcharger la déclaration du tableau et ne pas dupliquer la configuration de chaque élément du tableau. Ces feuilles de style fonctionnent de la même manière que dans le monde HTML. Dans une application Android leur intérêt est à mon sens encore plus grand car vous pouvez par exemple utiliser des styles différent selon les tailles des écrans par exemple.

<resources xmlns:android="http://schemas.android.com/apk/res/android"> 
    <style name="AppTheme" parent="@android:style/Theme.Holo.Light" /> 
    <style name="defaultTextView" parent="@android:style/TextAppearance.Medium"> 
        <item name="android:layout_width">match_parent</item> 
        <item name="android:layout_height">wrap_content</item> 
    </style> 
    <style name="frag1TableLayout"> 
        <item name="android:layout_width">match_parent</item> 
        <item name="android:layout_height">wrap_content</item> 
    </style> 
    <style name="frag1HeaderTableRow" parent="frag1TableLayout"> 
        <item name="android:layout_marginBottom">3dp</item> 
    </style> 
    <style name="frag1TableRow" parent="frag1TableLayout"> 
    </style> 
    <style name="frag1Col" parent="defaultTextView"> 
        <item name="android:layout_marginBottom">1dp</item> 
    </style> 
    <style name="frag1HeaderCol" parent="frag1Col"> 
        <item name="android:textStyle">bold</item> 
    </style> 
</resources>

Vous pouvez constater qu’un style peut hériter d’un autre style.

Gérer une bordure grâce à un objet drawable
Ajouter une bordure à votre tableau n’est pas une chose intuitive. Ne cherchez pas la propriété border, elle n’existe pas. Nous allons dans un premier temps utilisé un objet drawable en forme de rectangle que nous allons définir comme arrière plan de que notre tableau

La première étape consiste à créer cet objet drawable dans le répertoire /res/drawable (par exemple fichier tableborder.xml)

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <solid android:color="#FFFFFF"/>
    <stroke android:width="1dp" android:color="#777777"/>
    <corners android:radius="3dp" />
    <padding android:left="100dp" android:top="5dp"
        android:right="10dp" android:bottom="5dp" />
</shape>

Il faut ensuite modifier le style associé à chaque TextView définissant les colonnes de notre tableau.

<style name="frag1Col" parent="defaultTextView">
    <item name="android:layout_marginBottom">1dp</item>
    <item name="android:background">@drawable/tableborder</item>
</style>

Au niveau rendu vous obtiendrez



Gérer une bordure en jouant sur les marges
La méthode précédente est intéressante mais si vous ne voulez pas de marge entre les cellules le rendu n’est pas très propre car les bordures se surposent. Une autre méthode consiste à jouer sur les marges et les couleurs de fond

Vous définissez la couleur attendue de vos bordures comme couleur de fond de l’objet TableRow. Ensuite vous mettez un fond blanc à chaque cellule tout en leur ajoutant une marge correspondant à la taille de votre bordure.

La nouvelle feuille de style sera la suivante

    <style name="frag2TableLayout">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:stretchColumns">*</item>
        <item name="android:shrinkColumns">*</item>
        <item name="android:layout_margin">5dp</item>
    </style>
    <style name="frag2TableRow" parent="frag1TableLayout">
        <item name="android:background">#ccc</item>
    </style>
    <style name="frag2Col" parent="defaultTextView">
        <item name="android:layout_margin">1dp</item>
        <item name="android:background">#ffffff</item>
    </style>
    <style name="frag2HeaderCol" parent="frag2Col">
        <item name="android:textStyle">bold</item>
    </style>

Vous pouvez observer une particularité au niveau de la définition du style associé au TableLayout. En effet nous utilisons les propriétés strechColumns et shrinkColums pour que les colonnes du tableau utilisent tout l’espace

Au niveau du rendu nous obtenons


Gérer une bordure « fine et parfaite» 
Je ne suis pas encore satisfait car nous observons des chevauchements qui ne rendent pas le rendu parfait. Pour éviter le chevauchement nous allons encore jouer sur les styles mais en mettant des marges différentes entre le haut (layout_marginTop), le bas (layout_marginBottom), la droite (layout_marginRight) et la gauche (android:layout_marginLeft). C’est une logique d’esprit un peu particulière mais je n’ai encore rien trouvé de mieux pour l’instant.

Avec cette parade le tableau commence à ressembler à quelque chose


Pour les sources complètes vous pouvez les récupérer sur Github à l’adresse suivante https://github.com/javamind/annexe/tree/master/TableExamples

Comment créer un tableau directement dans votre code
Il reste une dernière étape dans notre voyage sur la gestion des tableaux dans le monde Android. Il s’agit d’un tableau totalement géré par votre code Java.

Au niveau de votre layout vous pouvez simplement déclarer un TableLayout sur lequel vous ajoutez un id

<TableLayout 
    android:id="@+id/containerTable" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:stretchColumns="*" 
    android:shrinkColumns="*" 
    android:layout_margin="5dp"> 
</TableLayout>

Au niveau du code Java nous allons voir comment remplir ce tableau à partir d’un Fragment (vous pouvez faire exactement la même chose à partir d’une Activity). Dans l’exemple ci-dessous nous récupérons une liste d’élements qui seront définies dans l’entête du tableau

public class TableJavaFragment extends Fragment { 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
        // Inflate the layout for this fragment 
        return inflater.inflate(R.layout.fragment3, container, false); 
    }

    @Override 
    public void onActivityCreated(Bundle savedInstanceState) { 
        super.onActivityCreated(savedInstanceState); 
        containerTable = (TableLayout) getActivity().findViewById(R.id.containerTable); 

        // Recuperation du table layout sur lequel nous allons agir 
        String[] players = getResources().getStringArray(R.array.locations); 

        // On va calculer la largeur des colonnes en fonction de la marge de 10 
        // On affiche l'enreg dans une ligne 
        TableRow tableRow = new TableRow(getActivity()); 
        containerTable.addView(tableRow, 
            new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); 
        containerTable.setBackgroundColor(getResources().getColor(R.color.grey)); 

        // On crée une ligne de x players colonnes 
        tableRow.setLayoutParams(new LayoutParams(players.length)); 

        // On va commencer par renseigner une ligne de titre par joueur 
        int i = 0; 
        for (String player : players) { 
            TextView text = createTextView(false , i == players.length - 1); 
            text.setText(player); 
            text.setGravity(Gravity.CENTER); 
            tableRow.addView(text, i++); 
        } 

        for (int j = 0; j < 10; j++) { 
            tableRow = new TableRow(getActivity()); 
            containerTable.addView(tableRow, 
                new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); 
            i = 0; 
            for (String player : players) { 
                TextView text = createTextView(j==9, i == players.length - 1); 
                text.setText("123"); 
                tableRow.addView(text, i++); 
                text.setGravity(Gravity.RIGHT); 
            } 
        } 
    } 

 private TextView createTextView(boolean endline, boolean endcolumn){ 
        TextView text = new TextView(getActivity(), null, R.style.frag3HeaderCol); 
        int bottom = endline ? 1 : 0; 
        int right = endcolumn ? 1 :0; 
        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 0.3f); 
        params.setMargins(1, 1, right, bottom); 
        text.setLayoutParams(params); 
        text.setPadding(4, 4, 10, 4); 
        text.setBackgroundColor(getResources().getColor(R.color.white)); 
        return text; 
    } 
}


Exemple en ligne
Vous pouvez accéder à l’application illustrant tous ces exemples en récupérant les sources sur github https://github.com/javamind/annexe/tree/master/TableExamples

GridLayout
Je vous conseille de lire mon article sur le GridLayout qui est un layout permettant également de créer des tableaux 

1 commentaire:

Remarque : Seul un membre de ce blog est autorisé à enregistrer un commentaire.