1/* 2 * Copyright (C) 2006 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 com.android.internal.view.menu; 18 19import com.android.internal.view.menu.MenuBuilder.ItemInvoker; 20 21import android.content.Context; 22import android.content.res.Resources; 23import android.content.res.TypedArray; 24import android.graphics.Canvas; 25import android.graphics.Rect; 26import android.graphics.drawable.Drawable; 27import android.os.Parcel; 28import android.os.Parcelable; 29import android.util.AttributeSet; 30import android.view.KeyEvent; 31import android.view.View; 32import android.view.ViewConfiguration; 33import android.view.ViewGroup; 34import android.view.LayoutInflater; 35 36import java.util.ArrayList; 37 38/** 39 * The icon menu view is an icon-based menu usually with a subset of all the menu items. 40 * It is opened as the default menu, and shows either the first five or all six of the menu items 41 * with text and icon. In the situation of there being more than six items, the first five items 42 * will be accompanied with a 'More' button that opens an {@link ExpandedMenuView} which lists 43 * all the menu items. 44 * 45 * @attr ref android.R.styleable#IconMenuView_rowHeight 46 * @attr ref android.R.styleable#IconMenuView_maxRows 47 * @attr ref android.R.styleable#IconMenuView_maxItemsPerRow 48 * 49 * @hide 50 */ 51public final class IconMenuView extends ViewGroup implements ItemInvoker, MenuView, Runnable { 52 private static final int ITEM_CAPTION_CYCLE_DELAY = 1000; 53 54 private MenuBuilder mMenu; 55 56 /** Height of each row */ 57 private int mRowHeight; 58 /** Maximum number of rows to be shown */ 59 private int mMaxRows; 60 /** Maximum number of items to show in the icon menu. */ 61 private int mMaxItems; 62 /** Maximum number of items per row */ 63 private int mMaxItemsPerRow; 64 /** Actual number of items (the 'More' view does not count as an item) shown */ 65 private int mNumActualItemsShown; 66 67 /** Divider that is drawn between all rows */ 68 private Drawable mHorizontalDivider; 69 /** Height of the horizontal divider */ 70 private int mHorizontalDividerHeight; 71 /** Set of horizontal divider positions where the horizontal divider will be drawn */ 72 private ArrayList<Rect> mHorizontalDividerRects; 73 74 /** Divider that is drawn between all columns */ 75 private Drawable mVerticalDivider; 76 /** Width of the vertical divider */ 77 private int mVerticalDividerWidth; 78 /** Set of vertical divider positions where the vertical divider will be drawn */ 79 private ArrayList<Rect> mVerticalDividerRects; 80 81 /** Icon for the 'More' button */ 82 private Drawable mMoreIcon; 83 84 /** Background of each item (should contain the selected and focused states) */ 85 private Drawable mItemBackground; 86 87 /** Default animations for this menu */ 88 private int mAnimations; 89 90 /** 91 * Whether this IconMenuView has stale children and needs to update them. 92 * Set true by {@link #markStaleChildren()} and reset to false by 93 * {@link #onMeasure(int, int)} 94 */ 95 private boolean mHasStaleChildren; 96 97 /** 98 * Longpress on MENU (while this is shown) switches to shortcut caption 99 * mode. When the user releases the longpress, we do not want to pass the 100 * key-up event up since that will dismiss the menu. 101 */ 102 private boolean mMenuBeingLongpressed = false; 103 104 /** 105 * While {@link #mMenuBeingLongpressed}, we toggle the children's caption 106 * mode between each's title and its shortcut. This is the last caption mode 107 * we broadcasted to children. 108 */ 109 private boolean mLastChildrenCaptionMode; 110 111 /** 112 * The layout to use for menu items. Each index is the row number (0 is the 113 * top-most). Each value contains the number of items in that row. 114 * <p> 115 * The length of this array should not be used to get the number of rows in 116 * the current layout, instead use {@link #mLayoutNumRows}. 117 */ 118 private int[] mLayout; 119 120 /** 121 * The number of rows in the current layout. 122 */ 123 private int mLayoutNumRows; 124 125 /** 126 * Instantiates the IconMenuView that is linked with the provided MenuBuilder. 127 */ 128 public IconMenuView(Context context, AttributeSet attrs) { 129 super(context, attrs); 130 131 TypedArray a = 132 context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.IconMenuView, 0, 0); 133 mRowHeight = a.getDimensionPixelSize(com.android.internal.R.styleable.IconMenuView_rowHeight, 64); 134 mMaxRows = a.getInt(com.android.internal.R.styleable.IconMenuView_maxRows, 2); 135 mMaxItems = a.getInt(com.android.internal.R.styleable.IconMenuView_maxItems, 6); 136 mMaxItemsPerRow = a.getInt(com.android.internal.R.styleable.IconMenuView_maxItemsPerRow, 3); 137 mMoreIcon = a.getDrawable(com.android.internal.R.styleable.IconMenuView_moreIcon); 138 a.recycle(); 139 140 a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.MenuView, 0, 0); 141 mItemBackground = a.getDrawable(com.android.internal.R.styleable.MenuView_itemBackground); 142 mHorizontalDivider = a.getDrawable(com.android.internal.R.styleable.MenuView_horizontalDivider); 143 mHorizontalDividerRects = new ArrayList<Rect>(); 144 mVerticalDivider = a.getDrawable(com.android.internal.R.styleable.MenuView_verticalDivider); 145 mVerticalDividerRects = new ArrayList<Rect>(); 146 mAnimations = a.getResourceId(com.android.internal.R.styleable.MenuView_windowAnimationStyle, 0); 147 a.recycle(); 148 149 if (mHorizontalDivider != null) { 150 mHorizontalDividerHeight = mHorizontalDivider.getIntrinsicHeight(); 151 // Make sure to have some height for the divider 152 if (mHorizontalDividerHeight == -1) mHorizontalDividerHeight = 1; 153 } 154 155 if (mVerticalDivider != null) { 156 mVerticalDividerWidth = mVerticalDivider.getIntrinsicWidth(); 157 // Make sure to have some width for the divider 158 if (mVerticalDividerWidth == -1) mVerticalDividerWidth = 1; 159 } 160 161 mLayout = new int[mMaxRows]; 162 163 // This view will be drawing the dividers 164 setWillNotDraw(false); 165 166 // This is so we'll receive the MENU key in touch mode 167 setFocusableInTouchMode(true); 168 // This is so our children can still be arrow-key focused 169 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); 170 } 171 172 int getMaxItems() { 173 return mMaxItems; 174 } 175 176 /** 177 * Figures out the layout for the menu items. 178 * 179 * @param width The available width for the icon menu. 180 */ 181 private void layoutItems(int width) { 182 int numItems = getChildCount(); 183 if (numItems == 0) { 184 mLayoutNumRows = 0; 185 return; 186 } 187 188 // Start with the least possible number of rows 189 int curNumRows = 190 Math.min((int) Math.ceil(numItems / (float) mMaxItemsPerRow), mMaxRows); 191 192 /* 193 * Increase the number of rows until we find a configuration that fits 194 * all of the items' titles. Worst case, we use mMaxRows. 195 */ 196 for (; curNumRows <= mMaxRows; curNumRows++) { 197 layoutItemsUsingGravity(curNumRows, numItems); 198 199 if (curNumRows >= numItems) { 200 // Can't have more rows than items 201 break; 202 } 203 204 if (doItemsFit()) { 205 // All the items fit, so this is a good configuration 206 break; 207 } 208 } 209 } 210 211 /** 212 * Figures out the layout for the menu items by equally distributing, and 213 * adding any excess items equally to lower rows. 214 * 215 * @param numRows The total number of rows for the menu view 216 * @param numItems The total number of items (across all rows) contained in 217 * the menu view 218 * @return int[] Where the value of index i contains the number of items for row i 219 */ 220 private void layoutItemsUsingGravity(int numRows, int numItems) { 221 int numBaseItemsPerRow = numItems / numRows; 222 int numLeftoverItems = numItems % numRows; 223 /** 224 * The bottom rows will each get a leftover item. Rows (indexed at 0) 225 * that are >= this get a leftover item. Note: if there are 0 leftover 226 * items, no rows will get them since this value will be greater than 227 * the last row. 228 */ 229 int rowsThatGetALeftoverItem = numRows - numLeftoverItems; 230 231 int[] layout = mLayout; 232 for (int i = 0; i < numRows; i++) { 233 layout[i] = numBaseItemsPerRow; 234 235 // Fill the bottom rows with a leftover item each 236 if (i >= rowsThatGetALeftoverItem) { 237 layout[i]++; 238 } 239 } 240 241 mLayoutNumRows = numRows; 242 } 243 244 /** 245 * Checks whether each item's title is fully visible using the current 246 * layout. 247 * 248 * @return True if the items fit (each item's text is fully visible), false 249 * otherwise. 250 */ 251 private boolean doItemsFit() { 252 int itemPos = 0; 253 254 int[] layout = mLayout; 255 int numRows = mLayoutNumRows; 256 for (int row = 0; row < numRows; row++) { 257 int numItemsOnRow = layout[row]; 258 259 /* 260 * If there is only one item on this row, increasing the 261 * number of rows won't help. 262 */ 263 if (numItemsOnRow == 1) { 264 itemPos++; 265 continue; 266 } 267 268 for (int itemsOnRowCounter = numItemsOnRow; itemsOnRowCounter > 0; 269 itemsOnRowCounter--) { 270 View child = getChildAt(itemPos++); 271 LayoutParams lp = (LayoutParams) child.getLayoutParams(); 272 if (lp.maxNumItemsOnRow < numItemsOnRow) { 273 return false; 274 } 275 } 276 } 277 278 return true; 279 } 280 281 Drawable getItemBackgroundDrawable() { 282 return mItemBackground.getConstantState().newDrawable(getContext().getResources()); 283 } 284 285 /** 286 * Creates the item view for the 'More' button which is used to switch to 287 * the expanded menu view. This button is a special case since it does not 288 * have a MenuItemData backing it. 289 * @return The IconMenuItemView for the 'More' button 290 */ 291 IconMenuItemView createMoreItemView() { 292 Context context = getContext(); 293 LayoutInflater inflater = LayoutInflater.from(context); 294 295 final IconMenuItemView itemView = (IconMenuItemView) inflater.inflate( 296 com.android.internal.R.layout.icon_menu_item_layout, null); 297 298 Resources r = context.getResources(); 299 itemView.initialize(r.getText(com.android.internal.R.string.more_item_label), mMoreIcon); 300 301 // Set up a click listener on the view since there will be no invocation sequence 302 // due to the lack of a MenuItemData this view 303 itemView.setOnClickListener(new OnClickListener() { 304 public void onClick(View v) { 305 // Switches the menu to expanded mode. Requires support from 306 // the menu's active callback. 307 mMenu.changeMenuMode(); 308 } 309 }); 310 311 return itemView; 312 } 313 314 315 public void initialize(MenuBuilder menu) { 316 mMenu = menu; 317 } 318 319 /** 320 * The positioning algorithm that gets called from onMeasure. It 321 * just computes positions for each child, and then stores them in the child's layout params. 322 * @param menuWidth The width of this menu to assume for positioning 323 * @param menuHeight The height of this menu to assume for positioning 324 */ 325 private void positionChildren(int menuWidth, int menuHeight) { 326 // Clear the containers for the positions where the dividers should be drawn 327 if (mHorizontalDivider != null) mHorizontalDividerRects.clear(); 328 if (mVerticalDivider != null) mVerticalDividerRects.clear(); 329 330 // Get the minimum number of rows needed 331 final int numRows = mLayoutNumRows; 332 final int numRowsMinus1 = numRows - 1; 333 final int numItemsForRow[] = mLayout; 334 335 // The item position across all rows 336 int itemPos = 0; 337 View child; 338 IconMenuView.LayoutParams childLayoutParams = null; 339 340 // Use float for this to get precise positions (uniform item widths 341 // instead of last one taking any slack), and then convert to ints at last opportunity 342 float itemLeft; 343 float itemTop = 0; 344 // Since each row can have a different number of items, this will be computed per row 345 float itemWidth; 346 // Subtract the space needed for the horizontal dividers 347 final float itemHeight = (menuHeight - mHorizontalDividerHeight * (numRows - 1)) 348 / (float)numRows; 349 350 for (int row = 0; row < numRows; row++) { 351 // Start at the left 352 itemLeft = 0; 353 354 // Subtract the space needed for the vertical dividers, and divide by the number of items 355 itemWidth = (menuWidth - mVerticalDividerWidth * (numItemsForRow[row] - 1)) 356 / (float)numItemsForRow[row]; 357 358 for (int itemPosOnRow = 0; itemPosOnRow < numItemsForRow[row]; itemPosOnRow++) { 359 // Tell the child to be exactly this size 360 child = getChildAt(itemPos); 361 child.measure(MeasureSpec.makeMeasureSpec((int) itemWidth, MeasureSpec.EXACTLY), 362 MeasureSpec.makeMeasureSpec((int) itemHeight, MeasureSpec.EXACTLY)); 363 364 // Remember the child's position for layout 365 childLayoutParams = (IconMenuView.LayoutParams) child.getLayoutParams(); 366 childLayoutParams.left = (int) itemLeft; 367 childLayoutParams.right = (int) (itemLeft + itemWidth); 368 childLayoutParams.top = (int) itemTop; 369 childLayoutParams.bottom = (int) (itemTop + itemHeight); 370 371 // Increment by item width 372 itemLeft += itemWidth; 373 itemPos++; 374 375 // Add a vertical divider to draw 376 if (mVerticalDivider != null) { 377 mVerticalDividerRects.add(new Rect((int) itemLeft, 378 (int) itemTop, (int) (itemLeft + mVerticalDividerWidth), 379 (int) (itemTop + itemHeight))); 380 } 381 382 // Increment by divider width (even if we're not computing 383 // dividers, since we need to leave room for them when 384 // calculating item positions) 385 itemLeft += mVerticalDividerWidth; 386 } 387 388 // Last child on each row should extend to very right edge 389 if (childLayoutParams != null) { 390 childLayoutParams.right = menuWidth; 391 } 392 393 itemTop += itemHeight; 394 395 // Add a horizontal divider to draw 396 if ((mHorizontalDivider != null) && (row < numRowsMinus1)) { 397 mHorizontalDividerRects.add(new Rect(0, (int) itemTop, menuWidth, 398 (int) (itemTop + mHorizontalDividerHeight))); 399 400 itemTop += mHorizontalDividerHeight; 401 } 402 } 403 } 404 405 @Override 406 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 407 int measuredWidth = resolveSize(Integer.MAX_VALUE, widthMeasureSpec); 408 calculateItemFittingMetadata(measuredWidth); 409 layoutItems(measuredWidth); 410 411 // Get the desired height of the icon menu view (last row of items does 412 // not have a divider below) 413 final int layoutNumRows = mLayoutNumRows; 414 final int desiredHeight = (mRowHeight + mHorizontalDividerHeight) * 415 layoutNumRows - mHorizontalDividerHeight; 416 417 // Maximum possible width and desired height 418 setMeasuredDimension(measuredWidth, 419 resolveSize(desiredHeight, heightMeasureSpec)); 420 421 // Position the children 422 if (layoutNumRows > 0) { 423 positionChildren(getMeasuredWidth(), getMeasuredHeight()); 424 } 425 } 426 427 428 @Override 429 protected void onLayout(boolean changed, int l, int t, int r, int b) { 430 View child; 431 IconMenuView.LayoutParams childLayoutParams; 432 433 for (int i = getChildCount() - 1; i >= 0; i--) { 434 child = getChildAt(i); 435 childLayoutParams = (IconMenuView.LayoutParams)child 436 .getLayoutParams(); 437 438 // Layout children according to positions set during the measure 439 child.layout(childLayoutParams.left, childLayoutParams.top, childLayoutParams.right, 440 childLayoutParams.bottom); 441 } 442 } 443 444 @Override 445 protected void onDraw(Canvas canvas) { 446 Drawable drawable = mHorizontalDivider; 447 if (drawable != null) { 448 // If we have a horizontal divider to draw, draw it at the remembered positions 449 final ArrayList<Rect> rects = mHorizontalDividerRects; 450 for (int i = rects.size() - 1; i >= 0; i--) { 451 drawable.setBounds(rects.get(i)); 452 drawable.draw(canvas); 453 } 454 } 455 456 drawable = mVerticalDivider; 457 if (drawable != null) { 458 // If we have a vertical divider to draw, draw it at the remembered positions 459 final ArrayList<Rect> rects = mVerticalDividerRects; 460 for (int i = rects.size() - 1; i >= 0; i--) { 461 drawable.setBounds(rects.get(i)); 462 drawable.draw(canvas); 463 } 464 } 465 } 466 467 public boolean invokeItem(MenuItemImpl item) { 468 return mMenu.performItemAction(item, 0); 469 } 470 471 @Override 472 public LayoutParams generateLayoutParams(AttributeSet attrs) { 473 return new IconMenuView.LayoutParams(getContext(), attrs); 474 } 475 476 @Override 477 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 478 // Override to allow type-checking of LayoutParams. 479 return p instanceof IconMenuView.LayoutParams; 480 } 481 482 /** 483 * Marks as having stale children. 484 */ 485 void markStaleChildren() { 486 if (!mHasStaleChildren) { 487 mHasStaleChildren = true; 488 requestLayout(); 489 } 490 } 491 492 /** 493 * @return The number of actual items shown (those that are backed by an 494 * {@link MenuView.ItemView} implementation--eg: excludes More 495 * item). 496 */ 497 int getNumActualItemsShown() { 498 return mNumActualItemsShown; 499 } 500 501 void setNumActualItemsShown(int count) { 502 mNumActualItemsShown = count; 503 } 504 505 public int getWindowAnimations() { 506 return mAnimations; 507 } 508 509 /** 510 * Returns the number of items per row. 511 * <p> 512 * This should only be used for testing. 513 * 514 * @return The length of the array is the number of rows. A value at a 515 * position is the number of items in that row. 516 * @hide 517 */ 518 public int[] getLayout() { 519 return mLayout; 520 } 521 522 /** 523 * Returns the number of rows in the layout. 524 * <p> 525 * This should only be used for testing. 526 * 527 * @return The length of the array is the number of rows. A value at a 528 * position is the number of items in that row. 529 * @hide 530 */ 531 public int getLayoutNumRows() { 532 return mLayoutNumRows; 533 } 534 535 @Override 536 public boolean dispatchKeyEvent(KeyEvent event) { 537 538 if (event.getKeyCode() == KeyEvent.KEYCODE_MENU) { 539 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { 540 removeCallbacks(this); 541 postDelayed(this, ViewConfiguration.getLongPressTimeout()); 542 } else if (event.getAction() == KeyEvent.ACTION_UP) { 543 544 if (mMenuBeingLongpressed) { 545 // It was in cycle mode, so reset it (will also remove us 546 // from being called back) 547 setCycleShortcutCaptionMode(false); 548 return true; 549 550 } else { 551 // Just remove us from being called back 552 removeCallbacks(this); 553 // Fall through to normal processing too 554 } 555 } 556 } 557 558 return super.dispatchKeyEvent(event); 559 } 560 561 @Override 562 protected void onAttachedToWindow() { 563 super.onAttachedToWindow(); 564 565 requestFocus(); 566 } 567 568 @Override 569 protected void onDetachedFromWindow() { 570 setCycleShortcutCaptionMode(false); 571 super.onDetachedFromWindow(); 572 } 573 574 @Override 575 public void onWindowFocusChanged(boolean hasWindowFocus) { 576 577 if (!hasWindowFocus) { 578 setCycleShortcutCaptionMode(false); 579 } 580 581 super.onWindowFocusChanged(hasWindowFocus); 582 } 583 584 /** 585 * Sets the shortcut caption mode for IconMenuView. This mode will 586 * continuously cycle between a child's shortcut and its title. 587 * 588 * @param cycleShortcutAndNormal Whether to go into cycling shortcut mode, 589 * or to go back to normal. 590 */ 591 private void setCycleShortcutCaptionMode(boolean cycleShortcutAndNormal) { 592 593 if (!cycleShortcutAndNormal) { 594 /* 595 * We're setting back to title, so remove any callbacks for setting 596 * to shortcut 597 */ 598 removeCallbacks(this); 599 setChildrenCaptionMode(false); 600 mMenuBeingLongpressed = false; 601 602 } else { 603 604 // Set it the first time (the cycle will be started in run()). 605 setChildrenCaptionMode(true); 606 } 607 608 } 609 610 /** 611 * When this method is invoked if the menu is currently not being 612 * longpressed, it means that the longpress has just been reached (so we set 613 * longpress flag, and start cycling). If it is being longpressed, we cycle 614 * to the next mode. 615 */ 616 public void run() { 617 618 if (mMenuBeingLongpressed) { 619 620 // Cycle to other caption mode on the children 621 setChildrenCaptionMode(!mLastChildrenCaptionMode); 622 623 } else { 624 625 // Switch ourselves to continuously cycle the items captions 626 mMenuBeingLongpressed = true; 627 setCycleShortcutCaptionMode(true); 628 } 629 630 // We should run again soon to cycle to the other caption mode 631 postDelayed(this, ITEM_CAPTION_CYCLE_DELAY); 632 } 633 634 /** 635 * Iterates children and sets the desired shortcut mode. Only 636 * {@link #setCycleShortcutCaptionMode(boolean)} and {@link #run()} should call 637 * this. 638 * 639 * @param shortcut Whether to show shortcut or the title. 640 */ 641 private void setChildrenCaptionMode(boolean shortcut) { 642 643 // Set the last caption mode pushed to children 644 mLastChildrenCaptionMode = shortcut; 645 646 for (int i = getChildCount() - 1; i >= 0; i--) { 647 ((IconMenuItemView) getChildAt(i)).setCaptionMode(shortcut); 648 } 649 } 650 651 /** 652 * For each item, calculates the most dense row that fully shows the item's 653 * title. 654 * 655 * @param width The available width of the icon menu. 656 */ 657 private void calculateItemFittingMetadata(int width) { 658 int maxNumItemsPerRow = mMaxItemsPerRow; 659 int numItems = getChildCount(); 660 for (int i = 0; i < numItems; i++) { 661 LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams(); 662 // Start with 1, since that case does not get covered in the loop below 663 lp.maxNumItemsOnRow = 1; 664 for (int curNumItemsPerRow = maxNumItemsPerRow; curNumItemsPerRow > 0; 665 curNumItemsPerRow--) { 666 // Check whether this item can fit into a row containing curNumItemsPerRow 667 if (lp.desiredWidth < width / curNumItemsPerRow) { 668 // It can, mark this value as the most dense row it can fit into 669 lp.maxNumItemsOnRow = curNumItemsPerRow; 670 break; 671 } 672 } 673 } 674 } 675 676 @Override 677 protected Parcelable onSaveInstanceState() { 678 Parcelable superState = super.onSaveInstanceState(); 679 680 View focusedView = getFocusedChild(); 681 682 for (int i = getChildCount() - 1; i >= 0; i--) { 683 if (getChildAt(i) == focusedView) { 684 return new SavedState(superState, i); 685 } 686 } 687 688 return new SavedState(superState, -1); 689 } 690 691 @Override 692 protected void onRestoreInstanceState(Parcelable state) { 693 SavedState ss = (SavedState) state; 694 super.onRestoreInstanceState(ss.getSuperState()); 695 696 if (ss.focusedPosition >= getChildCount()) { 697 return; 698 } 699 700 View v = getChildAt(ss.focusedPosition); 701 if (v != null) { 702 v.requestFocus(); 703 } 704 } 705 706 private static class SavedState extends BaseSavedState { 707 int focusedPosition; 708 709 /** 710 * Constructor called from {@link IconMenuView#onSaveInstanceState()} 711 */ 712 public SavedState(Parcelable superState, int focusedPosition) { 713 super(superState); 714 this.focusedPosition = focusedPosition; 715 } 716 717 /** 718 * Constructor called from {@link #CREATOR} 719 */ 720 private SavedState(Parcel in) { 721 super(in); 722 focusedPosition = in.readInt(); 723 } 724 725 @Override 726 public void writeToParcel(Parcel dest, int flags) { 727 super.writeToParcel(dest, flags); 728 dest.writeInt(focusedPosition); 729 } 730 731 public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { 732 public SavedState createFromParcel(Parcel in) { 733 return new SavedState(in); 734 } 735 736 public SavedState[] newArray(int size) { 737 return new SavedState[size]; 738 } 739 }; 740 741 } 742 743 /** 744 * Layout parameters specific to IconMenuView (stores the left, top, right, bottom from the 745 * measure pass). 746 */ 747 public static class LayoutParams extends ViewGroup.MarginLayoutParams 748 { 749 int left, top, right, bottom; 750 int desiredWidth; 751 int maxNumItemsOnRow; 752 753 public LayoutParams(Context c, AttributeSet attrs) { 754 super(c, attrs); 755 } 756 757 public LayoutParams(int width, int height) { 758 super(width, height); 759 } 760 } 761} 762