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