| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.widget; |
| |
| import android.content.Context; |
| import android.content.res.TypedArray; |
| import android.util.AttributeSet; |
| import android.util.SparseBooleanArray; |
| import android.view.View; |
| import android.view.ViewGroup; |
| |
| import com.android.internal.R; |
| |
| import java.util.regex.Pattern; |
| |
| /** |
| * <p>A layout that arranges its children into rows and columns. |
| * A TableLayout consists of a number of {@link android.widget.TableRow} objects, |
| * each defining a row (actually, you can have other children, which will be |
| * explained below). TableLayout containers do not display border lines for |
| * their rows, columns, or cells. Each row has zero or more cells; each cell can |
| * hold one {@link android.view.View View} object. The table has as many columns |
| * as the row with the most cells. A table can leave cells empty. Cells can span |
| * columns, as they can in HTML.</p> |
| * |
| * <p>The width of a column is defined by the row with the widest cell in that |
| * column. However, a TableLayout can specify certain columns as shrinkable or |
| * stretchable by calling |
| * {@link #setColumnShrinkable(int, boolean) setColumnShrinkable()} |
| * or {@link #setColumnStretchable(int, boolean) setColumnStretchable()}. If |
| * marked as shrinkable, the column width can be shrunk to fit the table into |
| * its parent object. If marked as stretchable, it can expand in width to fit |
| * any extra space. The total width of the table is defined by its parent |
| * container. It is important to remember that a column can be both shrinkable |
| * and stretchable. In such a situation, the column will change its size to |
| * always use up the available space, but never more. Finally, you can hide a |
| * column by calling |
| * {@link #setColumnCollapsed(int,boolean) setColumnCollapsed()}.</p> |
| * |
| * <p>The children of a TableLayout cannot specify the <code>layout_width</code> |
| * attribute. Width is always <code>MATCH_PARENT</code>. However, the |
| * <code>layout_height</code> attribute can be defined by a child; default value |
| * is {@link android.widget.TableLayout.LayoutParams#WRAP_CONTENT}. If the child |
| * is a {@link android.widget.TableRow}, then the height is always |
| * {@link android.widget.TableLayout.LayoutParams#WRAP_CONTENT}.</p> |
| * |
| * <p> Cells must be added to a row in increasing column order, both in code and |
| * XML. Column numbers are zero-based. If you don't specify a column number for |
| * a child cell, it will autoincrement to the next available column. If you skip |
| * a column number, it will be considered an empty cell in that row. See the |
| * TableLayout examples in ApiDemos for examples of creating tables in XML.</p> |
| * |
| * <p>Although the typical child of a TableLayout is a TableRow, you can |
| * actually use any View subclass as a direct child of TableLayout. The View |
| * will be displayed as a single row that spans all the table columns.</p> |
| * |
| */ |
| public class TableLayout extends LinearLayout { |
| private int[] mMaxWidths; |
| private SparseBooleanArray mStretchableColumns; |
| private SparseBooleanArray mShrinkableColumns; |
| private SparseBooleanArray mCollapsedColumns; |
| |
| private boolean mShrinkAllColumns; |
| private boolean mStretchAllColumns; |
| |
| private TableLayout.PassThroughHierarchyChangeListener mPassThroughListener; |
| |
| private boolean mInitialized; |
| |
| /** |
| * <p>Creates a new TableLayout for the given context.</p> |
| * |
| * @param context the application environment |
| */ |
| public TableLayout(Context context) { |
| super(context); |
| initTableLayout(); |
| } |
| |
| /** |
| * <p>Creates a new TableLayout for the given context and with the |
| * specified set attributes.</p> |
| * |
| * @param context the application environment |
| * @param attrs a collection of attributes |
| */ |
| public TableLayout(Context context, AttributeSet attrs) { |
| super(context, attrs); |
| |
| TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TableLayout); |
| |
| String stretchedColumns = a.getString(R.styleable.TableLayout_stretchColumns); |
| if (stretchedColumns != null) { |
| if (stretchedColumns.charAt(0) == '*') { |
| mStretchAllColumns = true; |
| } else { |
| mStretchableColumns = parseColumns(stretchedColumns); |
| } |
| } |
| |
| String shrinkedColumns = a.getString(R.styleable.TableLayout_shrinkColumns); |
| if (shrinkedColumns != null) { |
| if (shrinkedColumns.charAt(0) == '*') { |
| mShrinkAllColumns = true; |
| } else { |
| mShrinkableColumns = parseColumns(shrinkedColumns); |
| } |
| } |
| |
| String collapsedColumns = a.getString(R.styleable.TableLayout_collapseColumns); |
| if (collapsedColumns != null) { |
| mCollapsedColumns = parseColumns(collapsedColumns); |
| } |
| |
| a.recycle(); |
| initTableLayout(); |
| } |
| |
| /** |
| * <p>Parses a sequence of columns ids defined in a CharSequence with the |
| * following pattern (regex): \d+(\s*,\s*\d+)*</p> |
| * |
| * <p>Examples: "1" or "13, 7, 6" or "".</p> |
| * |
| * <p>The result of the parsing is stored in a sparse boolean array. The |
| * parsed column ids are used as the keys of the sparse array. The values |
| * are always true.</p> |
| * |
| * @param sequence a sequence of column ids, can be empty but not null |
| * @return a sparse array of boolean mapping column indexes to the columns |
| * collapse state |
| */ |
| private static SparseBooleanArray parseColumns(String sequence) { |
| SparseBooleanArray columns = new SparseBooleanArray(); |
| Pattern pattern = Pattern.compile("\\s*,\\s*"); |
| String[] columnDefs = pattern.split(sequence); |
| |
| for (String columnIdentifier : columnDefs) { |
| try { |
| int columnIndex = Integer.parseInt(columnIdentifier); |
| // only valid, i.e. positive, columns indexes are handled |
| if (columnIndex >= 0) { |
| // putting true in this sparse array indicates that the |
| // column index was defined in the XML file |
| columns.put(columnIndex, true); |
| } |
| } catch (NumberFormatException e) { |
| // we just ignore columns that don't exist |
| } |
| } |
| |
| return columns; |
| } |
| |
| /** |
| * <p>Performs initialization common to prorgrammatic use and XML use of |
| * this widget.</p> |
| */ |
| private void initTableLayout() { |
| if (mCollapsedColumns == null) { |
| mCollapsedColumns = new SparseBooleanArray(); |
| } |
| if (mStretchableColumns == null) { |
| mStretchableColumns = new SparseBooleanArray(); |
| } |
| if (mShrinkableColumns == null) { |
| mShrinkableColumns = new SparseBooleanArray(); |
| } |
| |
| // TableLayouts are always in vertical orientation; keep this tracked |
| // for shared LinearLayout code. |
| setOrientation(VERTICAL); |
| |
| mPassThroughListener = new PassThroughHierarchyChangeListener(); |
| // make sure to call the parent class method to avoid potential |
| // infinite loops |
| super.setOnHierarchyChangeListener(mPassThroughListener); |
| |
| mInitialized = true; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void setOnHierarchyChangeListener( |
| OnHierarchyChangeListener listener) { |
| // the user listener is delegated to our pass-through listener |
| mPassThroughListener.mOnHierarchyChangeListener = listener; |
| } |
| |
| private void requestRowsLayout() { |
| if (mInitialized) { |
| final int count = getChildCount(); |
| for (int i = 0; i < count; i++) { |
| getChildAt(i).requestLayout(); |
| } |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void requestLayout() { |
| if (mInitialized) { |
| int count = getChildCount(); |
| for (int i = 0; i < count; i++) { |
| getChildAt(i).forceLayout(); |
| } |
| } |
| |
| super.requestLayout(); |
| } |
| |
| /** |
| * <p>Indicates whether all columns are shrinkable or not.</p> |
| * |
| * @return true if all columns are shrinkable, false otherwise |
| * |
| * @attr ref android.R.styleable#TableLayout_shrinkColumns |
| */ |
| public boolean isShrinkAllColumns() { |
| return mShrinkAllColumns; |
| } |
| |
| /** |
| * <p>Convenience method to mark all columns as shrinkable.</p> |
| * |
| * @param shrinkAllColumns true to mark all columns shrinkable |
| * |
| * @attr ref android.R.styleable#TableLayout_shrinkColumns |
| */ |
| public void setShrinkAllColumns(boolean shrinkAllColumns) { |
| mShrinkAllColumns = shrinkAllColumns; |
| } |
| |
| /** |
| * <p>Indicates whether all columns are stretchable or not.</p> |
| * |
| * @return true if all columns are stretchable, false otherwise |
| * |
| * @attr ref android.R.styleable#TableLayout_stretchColumns |
| */ |
| public boolean isStretchAllColumns() { |
| return mStretchAllColumns; |
| } |
| |
| /** |
| * <p>Convenience method to mark all columns as stretchable.</p> |
| * |
| * @param stretchAllColumns true to mark all columns stretchable |
| * |
| * @attr ref android.R.styleable#TableLayout_stretchColumns |
| */ |
| public void setStretchAllColumns(boolean stretchAllColumns) { |
| mStretchAllColumns = stretchAllColumns; |
| } |
| |
| /** |
| * <p>Collapses or restores a given column. When collapsed, a column |
| * does not appear on screen and the extra space is reclaimed by the |
| * other columns. A column is collapsed/restored only when it belongs to |
| * a {@link android.widget.TableRow}.</p> |
| * |
| * <p>Calling this method requests a layout operation.</p> |
| * |
| * @param columnIndex the index of the column |
| * @param isCollapsed true if the column must be collapsed, false otherwise |
| * |
| * @attr ref android.R.styleable#TableLayout_collapseColumns |
| */ |
| public void setColumnCollapsed(int columnIndex, boolean isCollapsed) { |
| // update the collapse status of the column |
| mCollapsedColumns.put(columnIndex, isCollapsed); |
| |
| int count = getChildCount(); |
| for (int i = 0; i < count; i++) { |
| final View view = getChildAt(i); |
| if (view instanceof TableRow) { |
| ((TableRow) view).setColumnCollapsed(columnIndex, isCollapsed); |
| } |
| } |
| |
| requestRowsLayout(); |
| } |
| |
| /** |
| * <p>Returns the collapsed state of the specified column.</p> |
| * |
| * @param columnIndex the index of the column |
| * @return true if the column is collapsed, false otherwise |
| */ |
| public boolean isColumnCollapsed(int columnIndex) { |
| return mCollapsedColumns.get(columnIndex); |
| } |
| |
| /** |
| * <p>Makes the given column stretchable or not. When stretchable, a column |
| * takes up as much as available space as possible in its row.</p> |
| * |
| * <p>Calling this method requests a layout operation.</p> |
| * |
| * @param columnIndex the index of the column |
| * @param isStretchable true if the column must be stretchable, |
| * false otherwise. Default is false. |
| * |
| * @attr ref android.R.styleable#TableLayout_stretchColumns |
| */ |
| public void setColumnStretchable(int columnIndex, boolean isStretchable) { |
| mStretchableColumns.put(columnIndex, isStretchable); |
| requestRowsLayout(); |
| } |
| |
| /** |
| * <p>Returns whether the specified column is stretchable or not.</p> |
| * |
| * @param columnIndex the index of the column |
| * @return true if the column is stretchable, false otherwise |
| */ |
| public boolean isColumnStretchable(int columnIndex) { |
| return mStretchAllColumns || mStretchableColumns.get(columnIndex); |
| } |
| |
| /** |
| * <p>Makes the given column shrinkable or not. When a row is too wide, the |
| * table can reclaim extra space from shrinkable columns.</p> |
| * |
| * <p>Calling this method requests a layout operation.</p> |
| * |
| * @param columnIndex the index of the column |
| * @param isShrinkable true if the column must be shrinkable, |
| * false otherwise. Default is false. |
| * |
| * @attr ref android.R.styleable#TableLayout_shrinkColumns |
| */ |
| public void setColumnShrinkable(int columnIndex, boolean isShrinkable) { |
| mShrinkableColumns.put(columnIndex, isShrinkable); |
| requestRowsLayout(); |
| } |
| |
| /** |
| * <p>Returns whether the specified column is shrinkable or not.</p> |
| * |
| * @param columnIndex the index of the column |
| * @return true if the column is shrinkable, false otherwise. Default is false. |
| */ |
| public boolean isColumnShrinkable(int columnIndex) { |
| return mShrinkAllColumns || mShrinkableColumns.get(columnIndex); |
| } |
| |
| /** |
| * <p>Applies the columns collapse status to a new row added to this |
| * table. This method is invoked by PassThroughHierarchyChangeListener |
| * upon child insertion.</p> |
| * |
| * <p>This method only applies to {@link android.widget.TableRow} |
| * instances.</p> |
| * |
| * @param child the newly added child |
| */ |
| private void trackCollapsedColumns(View child) { |
| if (child instanceof TableRow) { |
| final TableRow row = (TableRow) child; |
| final SparseBooleanArray collapsedColumns = mCollapsedColumns; |
| final int count = collapsedColumns.size(); |
| for (int i = 0; i < count; i++) { |
| int columnIndex = collapsedColumns.keyAt(i); |
| boolean isCollapsed = collapsedColumns.valueAt(i); |
| // the collapse status is set only when the column should be |
| // collapsed; otherwise, this might affect the default |
| // visibility of the row's children |
| if (isCollapsed) { |
| row.setColumnCollapsed(columnIndex, isCollapsed); |
| } |
| } |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void addView(View child) { |
| super.addView(child); |
| requestRowsLayout(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void addView(View child, int index) { |
| super.addView(child, index); |
| requestRowsLayout(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void addView(View child, ViewGroup.LayoutParams params) { |
| super.addView(child, params); |
| requestRowsLayout(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void addView(View child, int index, ViewGroup.LayoutParams params) { |
| super.addView(child, index, params); |
| requestRowsLayout(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
| // enforce vertical layout |
| measureVertical(widthMeasureSpec, heightMeasureSpec); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected void onLayout(boolean changed, int l, int t, int r, int b) { |
| // enforce vertical layout |
| layoutVertical(l, t, r, b); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void measureChildBeforeLayout(View child, int childIndex, |
| int widthMeasureSpec, int totalWidth, |
| int heightMeasureSpec, int totalHeight) { |
| // when the measured child is a table row, we force the width of its |
| // children with the widths computed in findLargestCells() |
| if (child instanceof TableRow) { |
| ((TableRow) child).setColumnsWidthConstraints(mMaxWidths); |
| } |
| |
| super.measureChildBeforeLayout(child, childIndex, |
| widthMeasureSpec, totalWidth, heightMeasureSpec, totalHeight); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { |
| findLargestCells(widthMeasureSpec, heightMeasureSpec); |
| shrinkAndStretchColumns(widthMeasureSpec); |
| |
| super.measureVertical(widthMeasureSpec, heightMeasureSpec); |
| } |
| |
| /** |
| * <p>Finds the largest cell in each column. For each column, the width of |
| * the largest cell is applied to all the other cells.</p> |
| * |
| * @param widthMeasureSpec the measure constraint imposed by our parent |
| */ |
| private void findLargestCells(int widthMeasureSpec, int heightMeasureSpec) { |
| boolean firstRow = true; |
| |
| // find the maximum width for each column |
| // the total number of columns is dynamically changed if we find |
| // wider rows as we go through the children |
| // the array is reused for each layout operation; the array can grow |
| // but never shrinks. Unused extra cells in the array are just ignored |
| // this behavior avoids to unnecessary grow the array after the first |
| // layout operation |
| final int count = getChildCount(); |
| for (int i = 0; i < count; i++) { |
| final View child = getChildAt(i); |
| if (child.getVisibility() == GONE) { |
| continue; |
| } |
| |
| if (child instanceof TableRow) { |
| final TableRow row = (TableRow) child; |
| // forces the row's height |
| final ViewGroup.LayoutParams layoutParams = row.getLayoutParams(); |
| layoutParams.height = LayoutParams.WRAP_CONTENT; |
| |
| final int[] widths = row.getColumnsWidths(widthMeasureSpec, heightMeasureSpec); |
| final int newLength = widths.length; |
| // this is the first row, we just need to copy the values |
| if (firstRow) { |
| if (mMaxWidths == null || mMaxWidths.length != newLength) { |
| mMaxWidths = new int[newLength]; |
| } |
| System.arraycopy(widths, 0, mMaxWidths, 0, newLength); |
| firstRow = false; |
| } else { |
| int length = mMaxWidths.length; |
| final int difference = newLength - length; |
| // the current row is wider than the previous rows, so |
| // we just grow the array and copy the values |
| if (difference > 0) { |
| final int[] oldMaxWidths = mMaxWidths; |
| mMaxWidths = new int[newLength]; |
| System.arraycopy(oldMaxWidths, 0, mMaxWidths, 0, |
| oldMaxWidths.length); |
| System.arraycopy(widths, oldMaxWidths.length, |
| mMaxWidths, oldMaxWidths.length, difference); |
| } |
| |
| // the row is narrower or of the same width as the previous |
| // rows, so we find the maximum width for each column |
| // if the row is narrower than the previous ones, |
| // difference will be negative |
| final int[] maxWidths = mMaxWidths; |
| length = Math.min(length, newLength); |
| for (int j = 0; j < length; j++) { |
| maxWidths[j] = Math.max(maxWidths[j], widths[j]); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * <p>Shrinks the columns if their total width is greater than the |
| * width allocated by widthMeasureSpec. When the total width is less |
| * than the allocated width, this method attempts to stretch columns |
| * to fill the remaining space.</p> |
| * |
| * @param widthMeasureSpec the width measure specification as indicated |
| * by this widget's parent |
| */ |
| private void shrinkAndStretchColumns(int widthMeasureSpec) { |
| // when we have no row, mMaxWidths is not initialized and the loop |
| // below could cause a NPE |
| if (mMaxWidths == null) { |
| return; |
| } |
| |
| // should we honor AT_MOST, EXACTLY and UNSPECIFIED? |
| int totalWidth = 0; |
| for (int width : mMaxWidths) { |
| totalWidth += width; |
| } |
| |
| int size = MeasureSpec.getSize(widthMeasureSpec) - mPaddingLeft - mPaddingRight; |
| |
| if ((totalWidth > size) && (mShrinkAllColumns || mShrinkableColumns.size() > 0)) { |
| // oops, the largest columns are wider than the row itself |
| // fairly redistribute the row's width among the columns |
| mutateColumnsWidth(mShrinkableColumns, mShrinkAllColumns, size, totalWidth); |
| } else if ((totalWidth < size) && (mStretchAllColumns || mStretchableColumns.size() > 0)) { |
| // if we have some space left, we distribute it among the |
| // expandable columns |
| mutateColumnsWidth(mStretchableColumns, mStretchAllColumns, size, totalWidth); |
| } |
| } |
| |
| private void mutateColumnsWidth(SparseBooleanArray columns, |
| boolean allColumns, int size, int totalWidth) { |
| int skipped = 0; |
| final int[] maxWidths = mMaxWidths; |
| final int length = maxWidths.length; |
| final int count = allColumns ? length : columns.size(); |
| final int totalExtraSpace = size - totalWidth; |
| int extraSpace = totalExtraSpace / count; |
| |
| // Column's widths are changed: force child table rows to re-measure. |
| // (done by super.measureVertical after shrinkAndStretchColumns.) |
| final int nbChildren = getChildCount(); |
| for (int i = 0; i < nbChildren; i++) { |
| View child = getChildAt(i); |
| if (child instanceof TableRow) { |
| child.forceLayout(); |
| } |
| } |
| |
| if (!allColumns) { |
| for (int i = 0; i < count; i++) { |
| int column = columns.keyAt(i); |
| if (columns.valueAt(i)) { |
| if (column < length) { |
| maxWidths[column] += extraSpace; |
| } else { |
| skipped++; |
| } |
| } |
| } |
| } else { |
| for (int i = 0; i < count; i++) { |
| maxWidths[i] += extraSpace; |
| } |
| |
| // we don't skip any column so we can return right away |
| return; |
| } |
| |
| if (skipped > 0 && skipped < count) { |
| // reclaim any extra space we left to columns that don't exist |
| extraSpace = skipped * extraSpace / (count - skipped); |
| for (int i = 0; i < count; i++) { |
| int column = columns.keyAt(i); |
| if (columns.valueAt(i) && column < length) { |
| if (extraSpace > maxWidths[column]) { |
| maxWidths[column] = 0; |
| } else { |
| maxWidths[column] += extraSpace; |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public LayoutParams generateLayoutParams(AttributeSet attrs) { |
| return new TableLayout.LayoutParams(getContext(), attrs); |
| } |
| |
| /** |
| * Returns a set of layout parameters with a width of |
| * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}, |
| * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}. |
| */ |
| @Override |
| protected LinearLayout.LayoutParams generateDefaultLayoutParams() { |
| return new LayoutParams(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { |
| return p instanceof TableLayout.LayoutParams; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected LinearLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { |
| return new LayoutParams(p); |
| } |
| |
| @Override |
| public CharSequence getAccessibilityClassName() { |
| return TableLayout.class.getName(); |
| } |
| |
| /** |
| * <p>This set of layout parameters enforces the width of each child to be |
| * {@link #MATCH_PARENT} and the height of each child to be |
| * {@link #WRAP_CONTENT}, but only if the height is not specified.</p> |
| */ |
| @SuppressWarnings({"UnusedDeclaration"}) |
| public static class LayoutParams extends LinearLayout.LayoutParams { |
| /** |
| * {@inheritDoc} |
| */ |
| public LayoutParams(Context c, AttributeSet attrs) { |
| super(c, attrs); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public LayoutParams(int w, int h) { |
| super(MATCH_PARENT, h); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public LayoutParams(int w, int h, float initWeight) { |
| super(MATCH_PARENT, h, initWeight); |
| } |
| |
| /** |
| * <p>Sets the child width to |
| * {@link android.view.ViewGroup.LayoutParams} and the child height to |
| * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}.</p> |
| */ |
| public LayoutParams() { |
| super(MATCH_PARENT, WRAP_CONTENT); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public LayoutParams(ViewGroup.LayoutParams p) { |
| super(p); |
| width = MATCH_PARENT; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public LayoutParams(MarginLayoutParams source) { |
| super(source); |
| width = MATCH_PARENT; |
| if (source instanceof TableLayout.LayoutParams) { |
| weight = ((TableLayout.LayoutParams) source).weight; |
| } |
| } |
| |
| /** |
| * <p>Fixes the row's width to |
| * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}; the row's |
| * height is fixed to |
| * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} if no layout |
| * height is specified.</p> |
| * |
| * @param a the styled attributes set |
| * @param widthAttr the width attribute to fetch |
| * @param heightAttr the height attribute to fetch |
| */ |
| @Override |
| protected void setBaseAttributes(TypedArray a, |
| int widthAttr, int heightAttr) { |
| this.width = MATCH_PARENT; |
| if (a.hasValue(heightAttr)) { |
| this.height = a.getLayoutDimension(heightAttr, "layout_height"); |
| } else { |
| this.height = WRAP_CONTENT; |
| } |
| } |
| } |
| |
| /** |
| * <p>A pass-through listener acts upon the events and dispatches them |
| * to another listener. This allows the table layout to set its own internal |
| * hierarchy change listener without preventing the user to setup his.</p> |
| */ |
| private class PassThroughHierarchyChangeListener implements |
| OnHierarchyChangeListener { |
| private OnHierarchyChangeListener mOnHierarchyChangeListener; |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void onChildViewAdded(View parent, View child) { |
| trackCollapsedColumns(child); |
| |
| if (mOnHierarchyChangeListener != null) { |
| mOnHierarchyChangeListener.onChildViewAdded(parent, child); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void onChildViewRemoved(View parent, View child) { |
| if (mOnHierarchyChangeListener != null) { |
| mOnHierarchyChangeListener.onChildViewRemoved(parent, child); |
| } |
| } |
| } |
| } |