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