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