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