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 */ TableLayout(Context context)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 */ TableLayout(Context context, AttributeSet attrs)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 */ parseColumns(String sequence)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 */ initTableLayout()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 setOnHierarchyChangeListener( OnHierarchyChangeListener listener)200 public void setOnHierarchyChangeListener( 201 OnHierarchyChangeListener listener) { 202 // the user listener is delegated to our pass-through listener 203 mPassThroughListener.mOnHierarchyChangeListener = listener; 204 } 205 requestRowsLayout()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 requestLayout()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 */ isShrinkAllColumns()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 */ setShrinkAllColumns(boolean shrinkAllColumns)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 */ isStretchAllColumns()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 */ setStretchAllColumns(boolean stretchAllColumns)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 */ setColumnCollapsed(int columnIndex, boolean isCollapsed)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 */ isColumnCollapsed(int columnIndex)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 */ setColumnStretchable(int columnIndex, boolean isStretchable)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 */ isColumnStretchable(int columnIndex)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 */ setColumnShrinkable(int columnIndex, boolean isShrinkable)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 */ isColumnShrinkable(int columnIndex)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 */ trackCollapsedColumns(View child)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 addView(View child)398 public void addView(View child) { 399 super.addView(child); 400 requestRowsLayout(); 401 } 402 403 /** 404 * {@inheritDoc} 405 */ 406 @Override addView(View child, int index)407 public void addView(View child, int index) { 408 super.addView(child, index); 409 requestRowsLayout(); 410 } 411 412 /** 413 * {@inheritDoc} 414 */ 415 @Override addView(View child, ViewGroup.LayoutParams params)416 public void addView(View child, ViewGroup.LayoutParams params) { 417 super.addView(child, params); 418 requestRowsLayout(); 419 } 420 421 /** 422 * {@inheritDoc} 423 */ 424 @Override addView(View child, int index, ViewGroup.LayoutParams params)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 onMeasure(int widthMeasureSpec, int heightMeasureSpec)434 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 435 // enforce vertical layout 436 measureVertical(widthMeasureSpec, heightMeasureSpec); 437 } 438 439 /** 440 * {@inheritDoc} 441 */ 442 @Override onLayout(boolean changed, int l, int t, int r, int b)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 measureChildBeforeLayout(View child, int childIndex, int widthMeasureSpec, int totalWidth, int heightMeasureSpec, int totalHeight)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 measureVertical(int widthMeasureSpec, int heightMeasureSpec)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 */ findLargestCells(int widthMeasureSpec, int heightMeasureSpec)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 */ shrinkAndStretchColumns(int widthMeasureSpec)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 mutateColumnsWidth(SparseBooleanArray columns, boolean allColumns, int size, int totalWidth)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 generateLayoutParams(AttributeSet attrs)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 generateDefaultLayoutParams()646 protected LinearLayout.LayoutParams generateDefaultLayoutParams() { 647 return new LayoutParams(); 648 } 649 650 /** 651 * {@inheritDoc} 652 */ 653 @Override checkLayoutParams(ViewGroup.LayoutParams p)654 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 655 return p instanceof TableLayout.LayoutParams; 656 } 657 658 /** 659 * {@inheritDoc} 660 */ 661 @Override generateLayoutParams(ViewGroup.LayoutParams p)662 protected LinearLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 663 return new LayoutParams(p); 664 } 665 666 @Override getAccessibilityClassName()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 */ LayoutParams(Context c, AttributeSet attrs)681 public LayoutParams(Context c, AttributeSet attrs) { 682 super(c, attrs); 683 } 684 685 /** 686 * {@inheritDoc} 687 */ LayoutParams(int w, int h)688 public LayoutParams(int w, int h) { 689 super(MATCH_PARENT, h); 690 } 691 692 /** 693 * {@inheritDoc} 694 */ LayoutParams(int w, int h, float initWeight)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 */ LayoutParams()704 public LayoutParams() { 705 super(MATCH_PARENT, WRAP_CONTENT); 706 } 707 708 /** 709 * {@inheritDoc} 710 */ LayoutParams(ViewGroup.LayoutParams p)711 public LayoutParams(ViewGroup.LayoutParams p) { 712 super(p); 713 } 714 715 /** 716 * {@inheritDoc} 717 */ LayoutParams(MarginLayoutParams source)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 setBaseAttributes(TypedArray a, int widthAttr, int heightAttr)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 */ onChildViewAdded(View parent, View child)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 */ onChildViewRemoved(View parent, View child)768 public void onChildViewRemoved(View parent, View child) { 769 if (mOnHierarchyChangeListener != null) { 770 mOnHierarchyChangeListener.onChildViewRemoved(parent, child); 771 } 772 } 773 } 774 } 775