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