TableLayout.java revision c8d5f7d607123966651b2bc521ba9c4470952b34
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 */ 74public 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 mPassThroughListener = new PassThroughHierarchyChangeListener(); 188 // make sure to call the parent class method to avoid potential 189 // infinite loops 190 super.setOnHierarchyChangeListener(mPassThroughListener); 191 192 mInitialized = true; 193 } 194 195 /** 196 * {@inheritDoc} 197 */ 198 @Override 199 public void setOnHierarchyChangeListener( 200 OnHierarchyChangeListener listener) { 201 // the user listener is delegated to our pass-through listener 202 mPassThroughListener.mOnHierarchyChangeListener = listener; 203 } 204 205 private void requestRowsLayout() { 206 if (mInitialized) { 207 final int count = getChildCount(); 208 for (int i = 0; i < count; i++) { 209 getChildAt(i).requestLayout(); 210 } 211 } 212 } 213 214 /** 215 * {@inheritDoc} 216 */ 217 @Override 218 public void requestLayout() { 219 if (mInitialized) { 220 int count = getChildCount(); 221 for (int i = 0; i < count; i++) { 222 getChildAt(i).forceLayout(); 223 } 224 } 225 226 super.requestLayout(); 227 } 228 229 /** 230 * <p>Indicates whether all columns are shrinkable or not.</p> 231 * 232 * @return true if all columns are shrinkable, false otherwise 233 * 234 * @attr ref android.R.styleable#TableLayout_shrinkColumns 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 * @attr ref android.R.styleable#TableLayout_stretchColumns 257 */ 258 public boolean isStretchAllColumns() { 259 return mStretchAllColumns; 260 } 261 262 /** 263 * <p>Convenience method to mark all columns as stretchable.</p> 264 * 265 * @param stretchAllColumns true to mark all columns stretchable 266 * 267 * @attr ref android.R.styleable#TableLayout_stretchColumns 268 */ 269 public void setStretchAllColumns(boolean stretchAllColumns) { 270 mStretchAllColumns = stretchAllColumns; 271 } 272 273 /** 274 * <p>Collapses or restores a given column. When collapsed, a column 275 * does not appear on screen and the extra space is reclaimed by the 276 * other columns. A column is collapsed/restored only when it belongs to 277 * a {@link android.widget.TableRow}.</p> 278 * 279 * <p>Calling this method requests a layout operation.</p> 280 * 281 * @param columnIndex the index of the column 282 * @param isCollapsed true if the column must be collapsed, false otherwise 283 * 284 * @attr ref android.R.styleable#TableLayout_collapseColumns 285 */ 286 public void setColumnCollapsed(int columnIndex, boolean isCollapsed) { 287 // update the collapse status of the column 288 mCollapsedColumns.put(columnIndex, isCollapsed); 289 290 int count = getChildCount(); 291 for (int i = 0; i < count; i++) { 292 final View view = getChildAt(i); 293 if (view instanceof TableRow) { 294 ((TableRow) view).setColumnCollapsed(columnIndex, isCollapsed); 295 } 296 } 297 298 requestRowsLayout(); 299 } 300 301 /** 302 * <p>Returns the collapsed state of the specified column.</p> 303 * 304 * @param columnIndex the index of the column 305 * @return true if the column is collapsed, false otherwise 306 */ 307 public boolean isColumnCollapsed(int columnIndex) { 308 return mCollapsedColumns.get(columnIndex); 309 } 310 311 /** 312 * <p>Makes the given column stretchable or not. When stretchable, a column 313 * takes up as much as available space as possible in its row.</p> 314 * 315 * <p>Calling this method requests a layout operation.</p> 316 * 317 * @param columnIndex the index of the column 318 * @param isStretchable true if the column must be stretchable, 319 * false otherwise. Default is false. 320 * 321 * @attr ref android.R.styleable#TableLayout_stretchColumns 322 */ 323 public void setColumnStretchable(int columnIndex, boolean isStretchable) { 324 mStretchableColumns.put(columnIndex, isStretchable); 325 requestRowsLayout(); 326 } 327 328 /** 329 * <p>Returns whether the specified column is stretchable or not.</p> 330 * 331 * @param columnIndex the index of the column 332 * @return true if the column is stretchable, false otherwise 333 */ 334 public boolean isColumnStretchable(int columnIndex) { 335 return mStretchAllColumns || mStretchableColumns.get(columnIndex); 336 } 337 338 /** 339 * <p>Makes the given column shrinkable or not. When a row is too wide, the 340 * table can reclaim extra space from shrinkable columns.</p> 341 * 342 * <p>Calling this method requests a layout operation.</p> 343 * 344 * @param columnIndex the index of the column 345 * @param isShrinkable true if the column must be shrinkable, 346 * false otherwise. Default is false. 347 * 348 * @attr ref android.R.styleable#TableLayout_shrinkColumns 349 */ 350 public void setColumnShrinkable(int columnIndex, boolean isShrinkable) { 351 mShrinkableColumns.put(columnIndex, isShrinkable); 352 requestRowsLayout(); 353 } 354 355 /** 356 * <p>Returns whether the specified column is shrinkable or not.</p> 357 * 358 * @param columnIndex the index of the column 359 * @return true if the column is shrinkable, false otherwise. Default is false. 360 */ 361 public boolean isColumnShrinkable(int columnIndex) { 362 return mShrinkAllColumns || mShrinkableColumns.get(columnIndex); 363 } 364 365 /** 366 * <p>Applies the columns collapse status to a new row added to this 367 * table. This method is invoked by PassThroughHierarchyChangeListener 368 * upon child insertion.</p> 369 * 370 * <p>This method only applies to {@link android.widget.TableRow} 371 * instances.</p> 372 * 373 * @param child the newly added child 374 */ 375 private void trackCollapsedColumns(View child) { 376 if (child instanceof TableRow) { 377 final TableRow row = (TableRow) child; 378 final SparseBooleanArray collapsedColumns = mCollapsedColumns; 379 final int count = collapsedColumns.size(); 380 for (int i = 0; i < count; i++) { 381 int columnIndex = collapsedColumns.keyAt(i); 382 boolean isCollapsed = collapsedColumns.valueAt(i); 383 // the collapse status is set only when the column should be 384 // collapsed; otherwise, this might affect the default 385 // visibility of the row's children 386 if (isCollapsed) { 387 row.setColumnCollapsed(columnIndex, isCollapsed); 388 } 389 } 390 } 391 } 392 393 /** 394 * {@inheritDoc} 395 */ 396 @Override 397 public void addView(View child) { 398 super.addView(child); 399 requestRowsLayout(); 400 } 401 402 /** 403 * {@inheritDoc} 404 */ 405 @Override 406 public void addView(View child, int index) { 407 super.addView(child, index); 408 requestRowsLayout(); 409 } 410 411 /** 412 * {@inheritDoc} 413 */ 414 @Override 415 public void addView(View child, ViewGroup.LayoutParams params) { 416 super.addView(child, params); 417 requestRowsLayout(); 418 } 419 420 /** 421 * {@inheritDoc} 422 */ 423 @Override 424 public void addView(View child, int index, ViewGroup.LayoutParams params) { 425 super.addView(child, index, params); 426 requestRowsLayout(); 427 } 428 429 /** 430 * {@inheritDoc} 431 */ 432 @Override 433 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 434 // enforce vertical layout 435 measureVertical(widthMeasureSpec, heightMeasureSpec); 436 } 437 438 /** 439 * {@inheritDoc} 440 */ 441 @Override 442 protected void onLayout(boolean changed, int l, int t, int r, int b) { 443 // enforce vertical layout 444 layoutVertical(); 445 } 446 447 /** 448 * {@inheritDoc} 449 */ 450 @Override 451 void measureChildBeforeLayout(View child, int childIndex, 452 int widthMeasureSpec, int totalWidth, 453 int heightMeasureSpec, int totalHeight) { 454 // when the measured child is a table row, we force the width of its 455 // children with the widths computed in findLargestCells() 456 if (child instanceof TableRow) { 457 ((TableRow) child).setColumnsWidthConstraints(mMaxWidths); 458 } 459 460 super.measureChildBeforeLayout(child, childIndex, 461 widthMeasureSpec, totalWidth, heightMeasureSpec, totalHeight); 462 } 463 464 /** 465 * {@inheritDoc} 466 */ 467 @Override 468 void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { 469 findLargestCells(widthMeasureSpec); 470 shrinkAndStretchColumns(widthMeasureSpec); 471 472 super.measureVertical(widthMeasureSpec, heightMeasureSpec); 473 } 474 475 /** 476 * <p>Finds the largest cell in each column. For each column, the width of 477 * the largest cell is applied to all the other cells.</p> 478 * 479 * @param widthMeasureSpec the measure constraint imposed by our parent 480 */ 481 private void findLargestCells(int widthMeasureSpec) { 482 boolean firstRow = true; 483 484 // find the maximum width for each column 485 // the total number of columns is dynamically changed if we find 486 // wider rows as we go through the children 487 // the array is reused for each layout operation; the array can grow 488 // but never shrinks. Unused extra cells in the array are just ignored 489 // this behavior avoids to unnecessary grow the array after the first 490 // layout operation 491 final int count = getChildCount(); 492 for (int i = 0; i < count; i++) { 493 final View child = getChildAt(i); 494 if (child.getVisibility() == GONE) { 495 continue; 496 } 497 498 if (child instanceof TableRow) { 499 final TableRow row = (TableRow) child; 500 // forces the row's height 501 final ViewGroup.LayoutParams layoutParams = row.getLayoutParams(); 502 layoutParams.height = LayoutParams.WRAP_CONTENT; 503 504 final int[] widths = row.getColumnsWidths(widthMeasureSpec); 505 final int newLength = widths.length; 506 // this is the first row, we just need to copy the values 507 if (firstRow) { 508 if (mMaxWidths == null || mMaxWidths.length != newLength) { 509 mMaxWidths = new int[newLength]; 510 } 511 System.arraycopy(widths, 0, mMaxWidths, 0, newLength); 512 firstRow = false; 513 } else { 514 int length = mMaxWidths.length; 515 final int difference = newLength - length; 516 // the current row is wider than the previous rows, so 517 // we just grow the array and copy the values 518 if (difference > 0) { 519 final int[] oldMaxWidths = mMaxWidths; 520 mMaxWidths = new int[newLength]; 521 System.arraycopy(oldMaxWidths, 0, mMaxWidths, 0, 522 oldMaxWidths.length); 523 System.arraycopy(widths, oldMaxWidths.length, 524 mMaxWidths, oldMaxWidths.length, difference); 525 } 526 527 // the row is narrower or of the same width as the previous 528 // rows, so we find the maximum width for each column 529 // if the row is narrower than the previous ones, 530 // difference will be negative 531 final int[] maxWidths = mMaxWidths; 532 length = Math.min(length, newLength); 533 for (int j = 0; j < length; j++) { 534 maxWidths[j] = Math.max(maxWidths[j], widths[j]); 535 } 536 } 537 } 538 } 539 } 540 541 /** 542 * <p>Shrinks the columns if their total width is greater than the 543 * width allocated by widthMeasureSpec. When the total width is less 544 * than the allocated width, this method attempts to stretch columns 545 * to fill the remaining space.</p> 546 * 547 * @param widthMeasureSpec the width measure specification as indicated 548 * by this widget's parent 549 */ 550 private void shrinkAndStretchColumns(int widthMeasureSpec) { 551 // when we have no row, mMaxWidths is not initialized and the loop 552 // below could cause a NPE 553 if (mMaxWidths == null) { 554 return; 555 } 556 557 // should we honor AT_MOST, EXACTLY and UNSPECIFIED? 558 int totalWidth = 0; 559 for (int width : mMaxWidths) { 560 totalWidth += width; 561 } 562 563 int size = MeasureSpec.getSize(widthMeasureSpec) - mPaddingLeft - mPaddingRight; 564 565 if ((totalWidth > size) && (mShrinkAllColumns || mShrinkableColumns.size() > 0)) { 566 // oops, the largest columns are wider than the row itself 567 // fairly redistribute the row's width among the columns 568 mutateColumnsWidth(mShrinkableColumns, mShrinkAllColumns, size, totalWidth); 569 } else if ((totalWidth < size) && (mStretchAllColumns || mStretchableColumns.size() > 0)) { 570 // if we have some space left, we distribute it among the 571 // expandable columns 572 mutateColumnsWidth(mStretchableColumns, mStretchAllColumns, size, totalWidth); 573 } 574 } 575 576 private void mutateColumnsWidth(SparseBooleanArray columns, 577 boolean allColumns, int size, int totalWidth) { 578 int skipped = 0; 579 final int[] maxWidths = mMaxWidths; 580 final int length = maxWidths.length; 581 final int count = allColumns ? length : columns.size(); 582 final int totalExtraSpace = size - totalWidth; 583 int extraSpace = totalExtraSpace / count; 584 585 // Column's widths are changed: force child table rows to re-measure. 586 // (done by super.measureVertical after shrinkAndStretchColumns.) 587 final int nbChildren = getChildCount(); 588 for (int i = 0; i < nbChildren; i++) { 589 View child = getChildAt(i); 590 if (child instanceof TableRow) { 591 child.forceLayout(); 592 } 593 } 594 595 if (!allColumns) { 596 for (int i = 0; i < count; i++) { 597 int column = columns.keyAt(i); 598 if (columns.valueAt(i)) { 599 if (column < length) { 600 maxWidths[column] += extraSpace; 601 } else { 602 skipped++; 603 } 604 } 605 } 606 } else { 607 for (int i = 0; i < count; i++) { 608 maxWidths[i] += extraSpace; 609 } 610 611 // we don't skip any column so we can return right away 612 return; 613 } 614 615 if (skipped > 0 && skipped < count) { 616 // reclaim any extra space we left to columns that don't exist 617 extraSpace = skipped * extraSpace / (count - skipped); 618 for (int i = 0; i < count; i++) { 619 int column = columns.keyAt(i); 620 if (columns.valueAt(i) && column < length) { 621 if (extraSpace > maxWidths[column]) { 622 maxWidths[column] = 0; 623 } else { 624 maxWidths[column] += extraSpace; 625 } 626 } 627 } 628 } 629 } 630 631 /** 632 * {@inheritDoc} 633 */ 634 @Override 635 public LayoutParams generateLayoutParams(AttributeSet attrs) { 636 return new TableLayout.LayoutParams(getContext(), attrs); 637 } 638 639 /** 640 * Returns a set of layout parameters with a width of 641 * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}, 642 * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}. 643 */ 644 @Override 645 protected LinearLayout.LayoutParams generateDefaultLayoutParams() { 646 return new LayoutParams(); 647 } 648 649 /** 650 * {@inheritDoc} 651 */ 652 @Override 653 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 654 return p instanceof TableLayout.LayoutParams; 655 } 656 657 /** 658 * {@inheritDoc} 659 */ 660 @Override 661 protected LinearLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 662 return new LayoutParams(p); 663 } 664 665 @Override 666 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 667 super.onInitializeAccessibilityEvent(event); 668 event.setClassName(TableLayout.class.getName()); 669 } 670 671 @Override 672 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 673 super.onInitializeAccessibilityNodeInfo(info); 674 info.setClassName(TableLayout.class.getName()); 675 } 676 677 /** 678 * <p>This set of layout parameters enforces the width of each child to be 679 * {@link #MATCH_PARENT} and the height of each child to be 680 * {@link #WRAP_CONTENT}, but only if the height is not specified.</p> 681 */ 682 @SuppressWarnings({"UnusedDeclaration"}) 683 public static class LayoutParams extends LinearLayout.LayoutParams { 684 /** 685 * {@inheritDoc} 686 */ 687 public LayoutParams(Context c, AttributeSet attrs) { 688 super(c, attrs); 689 } 690 691 /** 692 * {@inheritDoc} 693 */ 694 public LayoutParams(int w, int h) { 695 super(MATCH_PARENT, h); 696 } 697 698 /** 699 * {@inheritDoc} 700 */ 701 public LayoutParams(int w, int h, float initWeight) { 702 super(MATCH_PARENT, h, initWeight); 703 } 704 705 /** 706 * <p>Sets the child width to 707 * {@link android.view.ViewGroup.LayoutParams} and the child height to 708 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}.</p> 709 */ 710 public LayoutParams() { 711 super(MATCH_PARENT, WRAP_CONTENT); 712 } 713 714 /** 715 * {@inheritDoc} 716 */ 717 public LayoutParams(ViewGroup.LayoutParams p) { 718 super(p); 719 } 720 721 /** 722 * {@inheritDoc} 723 */ 724 public LayoutParams(MarginLayoutParams source) { 725 super(source); 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 740 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { 741 this.width = MATCH_PARENT; 742 this.height = a.getLayoutDimension(heightAttr, WRAP_CONTENT); 743 } 744 } 745 746 /** 747 * <p>A pass-through listener acts upon the events and dispatches them 748 * to another listener. This allows the table layout to set its own internal 749 * hierarchy change listener without preventing the user to setup his.</p> 750 */ 751 private class PassThroughHierarchyChangeListener implements 752 OnHierarchyChangeListener { 753 private OnHierarchyChangeListener mOnHierarchyChangeListener; 754 755 /** 756 * {@inheritDoc} 757 */ 758 public void onChildViewAdded(View parent, View child) { 759 trackCollapsedColumns(child); 760 761 if (mOnHierarchyChangeListener != null) { 762 mOnHierarchyChangeListener.onChildViewAdded(parent, child); 763 } 764 } 765 766 /** 767 * {@inheritDoc} 768 */ 769 public void onChildViewRemoved(View parent, View child) { 770 if (mOnHierarchyChangeListener != null) { 771 mOnHierarchyChangeListener.onChildViewRemoved(parent, child); 772 } 773 } 774 } 775} 776