Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.widget;
     18 
     19 import com.android.internal.R;
     20 
     21 import android.content.Context;
     22 import android.content.res.TypedArray;
     23 import android.util.AttributeSet;
     24 import android.util.SparseBooleanArray;
     25 import android.view.View;
     26 import android.view.ViewGroup;
     27 import android.view.accessibility.AccessibilityEvent;
     28 import android.view.accessibility.AccessibilityNodeInfo;
     29 
     30 import java.util.regex.Pattern;
     31 
     32 /**
     33  * <p>A layout that arranges its children into rows and columns.
     34  * A TableLayout consists of a number of {@link android.widget.TableRow} objects,
     35  * each defining a row (actually, you can have other children, which will be
     36  * explained below). TableLayout containers do not display border lines for
     37  * their rows, columns, or cells. Each row has zero or more cells; each cell can
     38  * hold one {@link android.view.View View} object. The table has as many columns
     39  * as the row with the most cells. A table can leave cells empty. Cells can span
     40  * columns, as they can in HTML.</p>
     41  *
     42  * <p>The width of a column is defined by the row with the widest cell in that
     43  * column. However, a TableLayout can specify certain columns as shrinkable or
     44  * stretchable by calling
     45  * {@link #setColumnShrinkable(int, boolean) setColumnShrinkable()}
     46  * or {@link #setColumnStretchable(int, boolean) setColumnStretchable()}. If
     47  * marked as shrinkable, the column width can be shrunk to fit the table into
     48  * its parent object. If marked as stretchable, it can expand in width to fit
     49  * any extra space. The total width of the table is defined by its parent
     50  * container. It is important to remember that a column can be both shrinkable
     51  * and stretchable. In such a situation, the column will change its size to
     52  * always use up the available space, but never more. Finally, you can hide a
     53  * column by calling
     54  * {@link #setColumnCollapsed(int,boolean) setColumnCollapsed()}.</p>
     55  *
     56  * <p>The children of a TableLayout cannot specify the <code>layout_width</code>
     57  * attribute. Width is always <code>MATCH_PARENT</code>. However, the
     58  * <code>layout_height</code> attribute can be defined by a child; default value
     59  * is {@link android.widget.TableLayout.LayoutParams#WRAP_CONTENT}. If the child
     60  * is a {@link android.widget.TableRow}, then the height is always
     61  * {@link android.widget.TableLayout.LayoutParams#WRAP_CONTENT}.</p>
     62  *
     63  * <p> Cells must be added to a row in increasing column order, both in code and
     64  * XML. Column numbers are zero-based. If you don't specify a column number for
     65  * a child cell, it will autoincrement to the next available column. If you skip
     66  * a column number, it will be considered an empty cell in that row. See the
     67  * TableLayout examples in ApiDemos for examples of creating tables in XML.</p>
     68  *
     69  * <p>Although the typical child of a TableLayout is a TableRow, you can
     70  * actually use any View subclass as a direct child of TableLayout. The View
     71  * will be displayed as a single row that spans all the table columns.</p>
     72  *
     73  */
     74 public class TableLayout extends LinearLayout {
     75     private int[] mMaxWidths;
     76     private SparseBooleanArray mStretchableColumns;
     77     private SparseBooleanArray mShrinkableColumns;
     78     private SparseBooleanArray mCollapsedColumns;
     79 
     80     private boolean mShrinkAllColumns;
     81     private boolean mStretchAllColumns;
     82 
     83     private TableLayout.PassThroughHierarchyChangeListener mPassThroughListener;
     84 
     85     private boolean mInitialized;
     86 
     87     /**
     88      * <p>Creates a new TableLayout for the given context.</p>
     89      *
     90      * @param context the application environment
     91      */
     92     public TableLayout(Context context) {
     93         super(context);
     94         initTableLayout();
     95     }
     96 
     97     /**
     98      * <p>Creates a new TableLayout for the given context and with the
     99      * specified set attributes.</p>
    100      *
    101      * @param context the application environment
    102      * @param attrs a collection of attributes
    103      */
    104     public TableLayout(Context context, AttributeSet attrs) {
    105         super(context, attrs);
    106 
    107         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TableLayout);
    108 
    109         String stretchedColumns = a.getString(R.styleable.TableLayout_stretchColumns);
    110         if (stretchedColumns != null) {
    111             if (stretchedColumns.charAt(0) == '*') {
    112                 mStretchAllColumns = true;
    113             } else {
    114                 mStretchableColumns = parseColumns(stretchedColumns);
    115             }
    116         }
    117 
    118         String shrinkedColumns = a.getString(R.styleable.TableLayout_shrinkColumns);
    119         if (shrinkedColumns != null) {
    120             if (shrinkedColumns.charAt(0) == '*') {
    121                 mShrinkAllColumns = true;
    122             } else {
    123                 mShrinkableColumns = parseColumns(shrinkedColumns);
    124             }
    125         }
    126 
    127         String collapsedColumns = a.getString(R.styleable.TableLayout_collapseColumns);
    128         if (collapsedColumns != null) {
    129             mCollapsedColumns = parseColumns(collapsedColumns);
    130         }
    131 
    132         a.recycle();
    133         initTableLayout();
    134     }
    135 
    136     /**
    137      * <p>Parses a sequence of columns ids defined in a CharSequence with the
    138      * following pattern (regex): \d+(\s*,\s*\d+)*</p>
    139      *
    140      * <p>Examples: "1" or "13, 7, 6" or "".</p>
    141      *
    142      * <p>The result of the parsing is stored in a sparse boolean array. The
    143      * parsed column ids are used as the keys of the sparse array. The values
    144      * are always true.</p>
    145      *
    146      * @param sequence a sequence of column ids, can be empty but not null
    147      * @return a sparse array of boolean mapping column indexes to the columns
    148      *         collapse state
    149      */
    150     private static SparseBooleanArray parseColumns(String sequence) {
    151         SparseBooleanArray columns = new SparseBooleanArray();
    152         Pattern pattern = Pattern.compile("\\s*,\\s*");
    153         String[] columnDefs = pattern.split(sequence);
    154 
    155         for (String columnIdentifier : columnDefs) {
    156             try {
    157                 int columnIndex = Integer.parseInt(columnIdentifier);
    158                 // only valid, i.e. positive, columns indexes are handled
    159                 if (columnIndex >= 0) {
    160                     // putting true in this sparse array indicates that the
    161                     // column index was defined in the XML file
    162                     columns.put(columnIndex, true);
    163                 }
    164             } catch (NumberFormatException e) {
    165                 // we just ignore columns that don't exist
    166             }
    167         }
    168 
    169         return columns;
    170     }
    171 
    172     /**
    173      * <p>Performs initialization common to prorgrammatic use and XML use of
    174      * this widget.</p>
    175      */
    176     private void initTableLayout() {
    177         if (mCollapsedColumns == null) {
    178             mCollapsedColumns = new SparseBooleanArray();
    179         }
    180         if (mStretchableColumns == null) {
    181             mStretchableColumns = new SparseBooleanArray();
    182         }
    183         if (mShrinkableColumns == null) {
    184             mShrinkableColumns = new SparseBooleanArray();
    185         }
    186 
    187         // TableLayouts are always in vertical orientation; keep this tracked
    188         // for shared LinearLayout code.
    189         setOrientation(VERTICAL);
    190 
    191         mPassThroughListener = new PassThroughHierarchyChangeListener();
    192         // make sure to call the parent class method to avoid potential
    193         // infinite loops
    194         super.setOnHierarchyChangeListener(mPassThroughListener);
    195 
    196         mInitialized = true;
    197     }
    198 
    199     /**
    200      * {@inheritDoc}
    201      */
    202     @Override
    203     public void setOnHierarchyChangeListener(
    204             OnHierarchyChangeListener listener) {
    205         // the user listener is delegated to our pass-through listener
    206         mPassThroughListener.mOnHierarchyChangeListener = listener;
    207     }
    208 
    209     private void requestRowsLayout() {
    210         if (mInitialized) {
    211             final int count = getChildCount();
    212             for (int i = 0; i < count; i++) {
    213                 getChildAt(i).requestLayout();
    214             }
    215         }
    216     }
    217 
    218     /**
    219      * {@inheritDoc}
    220      */
    221     @Override
    222     public void requestLayout() {
    223         if (mInitialized) {
    224             int count = getChildCount();
    225             for (int i = 0; i < count; i++) {
    226                 getChildAt(i).forceLayout();
    227             }
    228         }
    229 
    230         super.requestLayout();
    231     }
    232 
    233     /**
    234      * <p>Indicates whether all columns are shrinkable or not.</p>
    235      *
    236      * @return true if all columns are shrinkable, false otherwise
    237      *
    238      * @attr ref android.R.styleable#TableLayout_shrinkColumns
    239      */
    240     public boolean isShrinkAllColumns() {
    241         return mShrinkAllColumns;
    242     }
    243 
    244     /**
    245      * <p>Convenience method to mark all columns as shrinkable.</p>
    246      *
    247      * @param shrinkAllColumns true to mark all columns shrinkable
    248      *
    249      * @attr ref android.R.styleable#TableLayout_shrinkColumns
    250      */
    251     public void setShrinkAllColumns(boolean shrinkAllColumns) {
    252         mShrinkAllColumns = shrinkAllColumns;
    253     }
    254 
    255     /**
    256      * <p>Indicates whether all columns are stretchable or not.</p>
    257      *
    258      * @return true if all columns are stretchable, false otherwise
    259      *
    260      * @attr ref android.R.styleable#TableLayout_stretchColumns
    261      */
    262     public boolean isStretchAllColumns() {
    263         return mStretchAllColumns;
    264     }
    265 
    266     /**
    267      * <p>Convenience method to mark all columns as stretchable.</p>
    268      *
    269      * @param stretchAllColumns true to mark all columns stretchable
    270      *
    271      * @attr ref android.R.styleable#TableLayout_stretchColumns
    272      */
    273     public void setStretchAllColumns(boolean stretchAllColumns) {
    274         mStretchAllColumns = stretchAllColumns;
    275     }
    276 
    277     /**
    278      * <p>Collapses or restores a given column. When collapsed, a column
    279      * does not appear on screen and the extra space is reclaimed by the
    280      * other columns. A column is collapsed/restored only when it belongs to
    281      * a {@link android.widget.TableRow}.</p>
    282      *
    283      * <p>Calling this method requests a layout operation.</p>
    284      *
    285      * @param columnIndex the index of the column
    286      * @param isCollapsed true if the column must be collapsed, false otherwise
    287      *
    288      * @attr ref android.R.styleable#TableLayout_collapseColumns
    289      */
    290     public void setColumnCollapsed(int columnIndex, boolean isCollapsed) {
    291         // update the collapse status of the column
    292         mCollapsedColumns.put(columnIndex, isCollapsed);
    293 
    294         int count = getChildCount();
    295         for (int i = 0; i < count; i++) {
    296             final View view = getChildAt(i);
    297             if (view instanceof TableRow) {
    298                 ((TableRow) view).setColumnCollapsed(columnIndex, isCollapsed);
    299             }
    300         }
    301 
    302         requestRowsLayout();
    303     }
    304 
    305     /**
    306      * <p>Returns the collapsed state of the specified column.</p>
    307      *
    308      * @param columnIndex the index of the column
    309      * @return true if the column is collapsed, false otherwise
    310      */
    311     public boolean isColumnCollapsed(int columnIndex) {
    312         return mCollapsedColumns.get(columnIndex);
    313     }
    314 
    315     /**
    316      * <p>Makes the given column stretchable or not. When stretchable, a column
    317      * takes up as much as available space as possible in its row.</p>
    318      *
    319      * <p>Calling this method requests a layout operation.</p>
    320      *
    321      * @param columnIndex the index of the column
    322      * @param isStretchable true if the column must be stretchable,
    323      *                      false otherwise. Default is false.
    324      *
    325      * @attr ref android.R.styleable#TableLayout_stretchColumns
    326      */
    327     public void setColumnStretchable(int columnIndex, boolean isStretchable) {
    328         mStretchableColumns.put(columnIndex, isStretchable);
    329         requestRowsLayout();
    330     }
    331 
    332     /**
    333      * <p>Returns whether the specified column is stretchable or not.</p>
    334      *
    335      * @param columnIndex the index of the column
    336      * @return true if the column is stretchable, false otherwise
    337      */
    338     public boolean isColumnStretchable(int columnIndex) {
    339         return mStretchAllColumns || mStretchableColumns.get(columnIndex);
    340     }
    341 
    342     /**
    343      * <p>Makes the given column shrinkable or not. When a row is too wide, the
    344      * table can reclaim extra space from shrinkable columns.</p>
    345      *
    346      * <p>Calling this method requests a layout operation.</p>
    347      *
    348      * @param columnIndex the index of the column
    349      * @param isShrinkable true if the column must be shrinkable,
    350      *                     false otherwise. Default is false.
    351      *
    352      * @attr ref android.R.styleable#TableLayout_shrinkColumns
    353      */
    354     public void setColumnShrinkable(int columnIndex, boolean isShrinkable) {
    355         mShrinkableColumns.put(columnIndex, isShrinkable);
    356         requestRowsLayout();
    357     }
    358 
    359     /**
    360      * <p>Returns whether the specified column is shrinkable or not.</p>
    361      *
    362      * @param columnIndex the index of the column
    363      * @return true if the column is shrinkable, false otherwise. Default is false.
    364      */
    365     public boolean isColumnShrinkable(int columnIndex) {
    366         return mShrinkAllColumns || mShrinkableColumns.get(columnIndex);
    367     }
    368 
    369     /**
    370      * <p>Applies the columns collapse status to a new row added to this
    371      * table. This method is invoked by PassThroughHierarchyChangeListener
    372      * upon child insertion.</p>
    373      *
    374      * <p>This method only applies to {@link android.widget.TableRow}
    375      * instances.</p>
    376      *
    377      * @param child the newly added child
    378      */
    379     private void trackCollapsedColumns(View child) {
    380         if (child instanceof TableRow) {
    381             final TableRow row = (TableRow) child;
    382             final SparseBooleanArray collapsedColumns = mCollapsedColumns;
    383             final int count = collapsedColumns.size();
    384             for (int i = 0; i < count; i++) {
    385                 int columnIndex = collapsedColumns.keyAt(i);
    386                 boolean isCollapsed = collapsedColumns.valueAt(i);
    387                 // the collapse status is set only when the column should be
    388                 // collapsed; otherwise, this might affect the default
    389                 // visibility of the row's children
    390                 if (isCollapsed) {
    391                     row.setColumnCollapsed(columnIndex, isCollapsed);
    392                 }
    393             }
    394         }
    395     }
    396 
    397     /**
    398      * {@inheritDoc}
    399      */
    400     @Override
    401     public void addView(View child) {
    402         super.addView(child);
    403         requestRowsLayout();
    404     }
    405 
    406     /**
    407      * {@inheritDoc}
    408      */
    409     @Override
    410     public void addView(View child, int index) {
    411         super.addView(child, index);
    412         requestRowsLayout();
    413     }
    414 
    415     /**
    416      * {@inheritDoc}
    417      */
    418     @Override
    419     public void addView(View child, ViewGroup.LayoutParams params) {
    420         super.addView(child, params);
    421         requestRowsLayout();
    422     }
    423 
    424     /**
    425      * {@inheritDoc}
    426      */
    427     @Override
    428     public void addView(View child, int index, ViewGroup.LayoutParams params) {
    429         super.addView(child, index, params);
    430         requestRowsLayout();
    431     }
    432 
    433     /**
    434      * {@inheritDoc}
    435      */
    436     @Override
    437     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    438         // enforce vertical layout
    439         measureVertical(widthMeasureSpec, heightMeasureSpec);
    440     }
    441 
    442     /**
    443      * {@inheritDoc}
    444      */
    445     @Override
    446     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    447         // enforce vertical layout
    448         layoutVertical(l, t, r, b);
    449     }
    450 
    451     /**
    452      * {@inheritDoc}
    453      */
    454     @Override
    455     void measureChildBeforeLayout(View child, int childIndex,
    456             int widthMeasureSpec, int totalWidth,
    457             int heightMeasureSpec, int totalHeight) {
    458         // when the measured child is a table row, we force the width of its
    459         // children with the widths computed in findLargestCells()
    460         if (child instanceof TableRow) {
    461             ((TableRow) child).setColumnsWidthConstraints(mMaxWidths);
    462         }
    463 
    464         super.measureChildBeforeLayout(child, childIndex,
    465                 widthMeasureSpec, totalWidth, heightMeasureSpec, totalHeight);
    466     }
    467 
    468     /**
    469      * {@inheritDoc}
    470      */
    471     @Override
    472     void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
    473         findLargestCells(widthMeasureSpec);
    474         shrinkAndStretchColumns(widthMeasureSpec);
    475 
    476         super.measureVertical(widthMeasureSpec, heightMeasureSpec);
    477     }
    478 
    479     /**
    480      * <p>Finds the largest cell in each column. For each column, the width of
    481      * the largest cell is applied to all the other cells.</p>
    482      *
    483      * @param widthMeasureSpec the measure constraint imposed by our parent
    484      */
    485     private void findLargestCells(int widthMeasureSpec) {
    486         boolean firstRow = true;
    487 
    488         // find the maximum width for each column
    489         // the total number of columns is dynamically changed if we find
    490         // wider rows as we go through the children
    491         // the array is reused for each layout operation; the array can grow
    492         // but never shrinks. Unused extra cells in the array are just ignored
    493         // this behavior avoids to unnecessary grow the array after the first
    494         // layout operation
    495         final int count = getChildCount();
    496         for (int i = 0; i < count; i++) {
    497             final View child = getChildAt(i);
    498             if (child.getVisibility() == GONE) {
    499                 continue;
    500             }
    501 
    502             if (child instanceof TableRow) {
    503                 final TableRow row = (TableRow) child;
    504                 // forces the row's height
    505                 final ViewGroup.LayoutParams layoutParams = row.getLayoutParams();
    506                 layoutParams.height = LayoutParams.WRAP_CONTENT;
    507 
    508                 final int[] widths = row.getColumnsWidths(widthMeasureSpec);
    509                 final int newLength = widths.length;
    510                 // this is the first row, we just need to copy the values
    511                 if (firstRow) {
    512                     if (mMaxWidths == null || mMaxWidths.length != newLength) {
    513                         mMaxWidths = new int[newLength];
    514                     }
    515                     System.arraycopy(widths, 0, mMaxWidths, 0, newLength);
    516                     firstRow = false;
    517                 } else {
    518                     int length = mMaxWidths.length;
    519                     final int difference = newLength - length;
    520                     // the current row is wider than the previous rows, so
    521                     // we just grow the array and copy the values
    522                     if (difference > 0) {
    523                         final int[] oldMaxWidths = mMaxWidths;
    524                         mMaxWidths = new int[newLength];
    525                         System.arraycopy(oldMaxWidths, 0, mMaxWidths, 0,
    526                                 oldMaxWidths.length);
    527                         System.arraycopy(widths, oldMaxWidths.length,
    528                                 mMaxWidths, oldMaxWidths.length, difference);
    529                     }
    530 
    531                     // the row is narrower or of the same width as the previous
    532                     // rows, so we find the maximum width for each column
    533                     // if the row is narrower than the previous ones,
    534                     // difference will be negative
    535                     final int[] maxWidths = mMaxWidths;
    536                     length = Math.min(length, newLength);
    537                     for (int j = 0; j < length; j++) {
    538                         maxWidths[j] = Math.max(maxWidths[j], widths[j]);
    539                     }
    540                 }
    541             }
    542         }
    543     }
    544 
    545     /**
    546      * <p>Shrinks the columns if their total width is greater than the
    547      * width allocated by widthMeasureSpec. When the total width is less
    548      * than the allocated width, this method attempts to stretch columns
    549      * to fill the remaining space.</p>
    550      *
    551      * @param widthMeasureSpec the width measure specification as indicated
    552      *                         by this widget's parent
    553      */
    554     private void shrinkAndStretchColumns(int widthMeasureSpec) {
    555         // when we have no row, mMaxWidths is not initialized and the loop
    556         // below could cause a NPE
    557         if (mMaxWidths == null) {
    558             return;
    559         }
    560 
    561         // should we honor AT_MOST, EXACTLY and UNSPECIFIED?
    562         int totalWidth = 0;
    563         for (int width : mMaxWidths) {
    564             totalWidth += width;
    565         }
    566 
    567         int size = MeasureSpec.getSize(widthMeasureSpec) - mPaddingLeft - mPaddingRight;
    568 
    569         if ((totalWidth > size) && (mShrinkAllColumns || mShrinkableColumns.size() > 0)) {
    570             // oops, the largest columns are wider than the row itself
    571             // fairly redistribute the row's width among the columns
    572             mutateColumnsWidth(mShrinkableColumns, mShrinkAllColumns, size, totalWidth);
    573         } else if ((totalWidth < size) && (mStretchAllColumns || mStretchableColumns.size() > 0)) {
    574             // if we have some space left, we distribute it among the
    575             // expandable columns
    576             mutateColumnsWidth(mStretchableColumns, mStretchAllColumns, size, totalWidth);
    577         }
    578     }
    579 
    580     private void mutateColumnsWidth(SparseBooleanArray columns,
    581             boolean allColumns, int size, int totalWidth) {
    582         int skipped = 0;
    583         final int[] maxWidths = mMaxWidths;
    584         final int length = maxWidths.length;
    585         final int count = allColumns ? length : columns.size();
    586         final int totalExtraSpace = size - totalWidth;
    587         int extraSpace = totalExtraSpace / count;
    588 
    589         // Column's widths are changed: force child table rows to re-measure.
    590         // (done by super.measureVertical after shrinkAndStretchColumns.)
    591         final int nbChildren = getChildCount();
    592         for (int i = 0; i < nbChildren; i++) {
    593             View child = getChildAt(i);
    594             if (child instanceof TableRow) {
    595                 child.forceLayout();
    596             }
    597         }
    598 
    599         if (!allColumns) {
    600             for (int i = 0; i < count; i++) {
    601                 int column = columns.keyAt(i);
    602                 if (columns.valueAt(i)) {
    603                     if (column < length) {
    604                         maxWidths[column] += extraSpace;
    605                     } else {
    606                         skipped++;
    607                     }
    608                 }
    609             }
    610         } else {
    611             for (int i = 0; i < count; i++) {
    612                 maxWidths[i] += extraSpace;
    613             }
    614 
    615             // we don't skip any column so we can return right away
    616             return;
    617         }
    618 
    619         if (skipped > 0 && skipped < count) {
    620             // reclaim any extra space we left to columns that don't exist
    621             extraSpace = skipped * extraSpace / (count - skipped);
    622             for (int i = 0; i < count; i++) {
    623                 int column = columns.keyAt(i);
    624                 if (columns.valueAt(i) && column < length) {
    625                     if (extraSpace > maxWidths[column]) {
    626                         maxWidths[column] = 0;
    627                     } else {
    628                         maxWidths[column] += extraSpace;
    629                     }
    630                 }
    631             }
    632         }
    633     }
    634 
    635     /**
    636      * {@inheritDoc}
    637      */
    638     @Override
    639     public LayoutParams generateLayoutParams(AttributeSet attrs) {
    640         return new TableLayout.LayoutParams(getContext(), attrs);
    641     }
    642 
    643     /**
    644      * Returns a set of layout parameters with a width of
    645      * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT},
    646      * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}.
    647      */
    648     @Override
    649     protected LinearLayout.LayoutParams generateDefaultLayoutParams() {
    650         return new LayoutParams();
    651     }
    652 
    653     /**
    654      * {@inheritDoc}
    655      */
    656     @Override
    657     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
    658         return p instanceof TableLayout.LayoutParams;
    659     }
    660 
    661     /**
    662      * {@inheritDoc}
    663      */
    664     @Override
    665     protected LinearLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
    666         return new LayoutParams(p);
    667     }
    668 
    669     @Override
    670     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
    671         super.onInitializeAccessibilityEvent(event);
    672         event.setClassName(TableLayout.class.getName());
    673     }
    674 
    675     @Override
    676     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
    677         super.onInitializeAccessibilityNodeInfo(info);
    678         info.setClassName(TableLayout.class.getName());
    679     }
    680 
    681     /**
    682      * <p>This set of layout parameters enforces the width of each child to be
    683      * {@link #MATCH_PARENT} and the height of each child to be
    684      * {@link #WRAP_CONTENT}, but only if the height is not specified.</p>
    685      */
    686     @SuppressWarnings({"UnusedDeclaration"})
    687     public static class LayoutParams extends LinearLayout.LayoutParams {
    688         /**
    689          * {@inheritDoc}
    690          */
    691         public LayoutParams(Context c, AttributeSet attrs) {
    692             super(c, attrs);
    693         }
    694 
    695         /**
    696          * {@inheritDoc}
    697          */
    698         public LayoutParams(int w, int h) {
    699             super(MATCH_PARENT, h);
    700         }
    701 
    702         /**
    703          * {@inheritDoc}
    704          */
    705         public LayoutParams(int w, int h, float initWeight) {
    706             super(MATCH_PARENT, h, initWeight);
    707         }
    708 
    709         /**
    710          * <p>Sets the child width to
    711          * {@link android.view.ViewGroup.LayoutParams} and the child height to
    712          * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}.</p>
    713          */
    714         public LayoutParams() {
    715             super(MATCH_PARENT, WRAP_CONTENT);
    716         }
    717 
    718         /**
    719          * {@inheritDoc}
    720          */
    721         public LayoutParams(ViewGroup.LayoutParams p) {
    722             super(p);
    723         }
    724 
    725         /**
    726          * {@inheritDoc}
    727          */
    728         public LayoutParams(MarginLayoutParams source) {
    729             super(source);
    730         }
    731 
    732         /**
    733          * <p>Fixes the row's width to
    734          * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}; the row's
    735          * height is fixed to
    736          * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} if no layout
    737          * height is specified.</p>
    738          *
    739          * @param a the styled attributes set
    740          * @param widthAttr the width attribute to fetch
    741          * @param heightAttr the height attribute to fetch
    742          */
    743         @Override
    744         protected void setBaseAttributes(TypedArray a,
    745                 int widthAttr, int heightAttr) {
    746             this.width = MATCH_PARENT;
    747             if (a.hasValue(heightAttr)) {
    748                 this.height = a.getLayoutDimension(heightAttr, "layout_height");
    749             } else {
    750                 this.height = WRAP_CONTENT;
    751             }
    752         }
    753     }
    754 
    755     /**
    756      * <p>A pass-through listener acts upon the events and dispatches them
    757      * to another listener. This allows the table layout to set its own internal
    758      * hierarchy change listener without preventing the user to setup his.</p>
    759      */
    760     private class PassThroughHierarchyChangeListener implements
    761             OnHierarchyChangeListener {
    762         private OnHierarchyChangeListener mOnHierarchyChangeListener;
    763 
    764         /**
    765          * {@inheritDoc}
    766          */
    767         public void onChildViewAdded(View parent, View child) {
    768             trackCollapsedColumns(child);
    769 
    770             if (mOnHierarchyChangeListener != null) {
    771                 mOnHierarchyChangeListener.onChildViewAdded(parent, child);
    772             }
    773         }
    774 
    775         /**
    776          * {@inheritDoc}
    777          */
    778         public void onChildViewRemoved(View parent, View child) {
    779             if (mOnHierarchyChangeListener != null) {
    780                 mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
    781             }
    782         }
    783     }
    784 }
    785