/** * Notifies the menu that the contents of the menu item specified by {@code menuRowId} have * changed. This should be called if icons, titles, etc. are changing for a particular menu item * while the menu is open. * * @param menuRowId The id of the menu item to change. This must be a row id and not a child id. */ public void menuItemContentChanged(int menuRowId) { // Make sure we have all the valid state objects we need. if (mAdapter == null || mMenu == null || mPopup == null || mPopup.getListView() == null) { return; } // Calculate the item index. int index = -1; int menuSize = mMenu.size(); for (int i = 0; i < menuSize; i++) { if (mMenu.getItem(i).getItemId() == menuRowId) { index = i; break; } } if (index == -1) return; // Check if the item is visible. ListView list = mPopup.getListView(); int startIndex = list.getFirstVisiblePosition(); int endIndex = list.getLastVisiblePosition(); if (index < startIndex || index > endIndex) return; // Grab the correct View. View view = list.getChildAt(index - startIndex); if (view == null) return; // Cause the Adapter to re-populate the View. list.getAdapter().getView(index, view, list); }
@Override public void onClick(View v) { if (UiClosables.closeQuietly(mPopup)) { return; } mAdapter = new GroupMembershipAdapter<GroupSelectionItem>( getContext(), R.layout.group_membership_list_item); mGroupMetaData.moveToPosition(-1); while (mGroupMetaData.moveToNext()) { String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME); String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE); String dataSet = mGroupMetaData.getString(GroupMetaDataLoader.DATA_SET); if (accountName.equals(mAccountName) && accountType.equals(mAccountType) && Objects.equal(dataSet, mDataSet)) { long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID); if (groupId != mFavoritesGroupId && (groupId != mDefaultGroupId || mDefaultGroupVisible)) { String title = mGroupMetaData.getString(GroupMetaDataLoader.TITLE); boolean checked = hasMembership(groupId); mAdapter.add(new GroupSelectionItem(groupId, title, checked)); } } } mAdapter.add( new GroupSelectionItem( CREATE_NEW_GROUP_GROUP_ID, getContext().getString(R.string.create_group_item_label), false)); mPopup = new ListPopupWindow(getContext(), null); mPopup.setAnchorView(mGroupList); mPopup.setAdapter(mAdapter); mPopup.setModal(true); mPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED); mPopup.show(); ListView listView = mPopup.getListView(); listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); listView.setOverScrollMode(OVER_SCROLL_ALWAYS); int count = mAdapter.getCount(); for (int i = 0; i < count; i++) { listView.setItemChecked(i, mAdapter.getItem(i).isChecked()); } listView.setOnItemClickListener(this); }
@Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (mPopup == null || mPopup.getListView() == null) return false; if (event.getKeyCode() == KeyEvent.KEYCODE_MENU) { if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { event.startTracking(); v.getKeyDispatcherState().startTracking(event, this); return true; } else if (event.getAction() == KeyEvent.ACTION_UP) { v.getKeyDispatcherState().handleUpEvent(event); if (event.isTracking() && !event.isCanceled()) { dismiss(); return true; } } } return false; }
public void setGroupMetaData(Cursor groupMetaData) { this.mGroupMetaData = groupMetaData; updateView(); // Open up the list of groups if a new group was just created. if (mCreatedNewGroup) { mCreatedNewGroup = false; onClick(this); // This causes the popup to open. if (mPopup != null) { // Ensure that the newly created group is checked. int position = mAdapter.getCount() - 2; ListView listView = mPopup.getListView(); if (!listView.isItemChecked(position)) { // Newly created group is not checked, so check it. listView.setItemChecked(position, true); onItemClick(listView, null, position, listView.getItemIdAtPosition(position)); } } } }
private void runMenuItemEnterAnimations() { mMenuItemEnterAnimator = new AnimatorSet(); AnimatorSet.Builder builder = null; ViewGroup list = mPopup.getListView(); for (int i = 0; i < list.getChildCount(); i++) { View view = list.getChildAt(i); Object animatorObject = view.getTag(R.id.menu_item_enter_anim_id); if (animatorObject != null) { if (builder == null) { builder = mMenuItemEnterAnimator.play((Animator) animatorObject); } else { builder.with((Animator) animatorObject); } } } mMenuItemEnterAnimator.addListener(mAnimationHistogramRecorder); mMenuItemEnterAnimator.start(); }
/** * Creates and shows the app menu anchored to the specified view. * * @param context The context of the AppMenu (ensure the proper theme is set on this context). * @param anchorView The anchor {@link View} of the {@link ListPopupWindow}. * @param isByPermanentButton Whether or not permanent hardware button triggered it. (oppose to * software button or keyboard). * @param screenRotation Current device screen rotation. * @param visibleDisplayFrame The display area rect in which AppMenu is supposed to fit in. * @param screenHeight Current device screen height. * @param footerResourceId The resource id for a view to add to the end of the menu list. Can be 0 * if no such view is required. */ void show( Context context, View anchorView, boolean isByPermanentButton, int screenRotation, Rect visibleDisplayFrame, int screenHeight, int footerResourceId) { mPopup = new ListPopupWindow(context, null, android.R.attr.popupMenuStyle); mPopup.setModal(true); mPopup.setAnchorView(anchorView); mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); int footerHeight = 0; if (footerResourceId != 0) { mPopup.setPromptPosition(ListPopupWindow.POSITION_PROMPT_BELOW); View promptView = LayoutInflater.from(context).inflate(footerResourceId, null); mPopup.setPromptView(promptView); int measureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); promptView.measure(measureSpec, measureSpec); footerHeight = promptView.getMeasuredHeight(); } mPopup.setOnDismissListener( new OnDismissListener() { @Override public void onDismiss() { if (mPopup.getAnchorView() instanceof ImageButton) { ((ImageButton) mPopup.getAnchorView()).setSelected(false); } if (mMenuItemEnterAnimator != null) mMenuItemEnterAnimator.cancel(); mHandler.appMenuDismissed(); mHandler.onMenuVisibilityChanged(false); } }); // Some OEMs don't actually let us change the background... but they still return the // padding of the new background, which breaks the menu height. If we still have a // drawable here even though our style says @null we should use this padding instead... Drawable originalBgDrawable = mPopup.getBackground(); // Need to explicitly set the background here. Relying on it being set in the style caused // an incorrectly drawn background. if (isByPermanentButton) { mPopup.setBackgroundDrawable( ApiCompatibilityUtils.getDrawable(context.getResources(), R.drawable.menu_bg)); } else { mPopup.setBackgroundDrawable( ApiCompatibilityUtils.getDrawable(context.getResources(), R.drawable.edge_menu_bg)); mPopup.setAnimationStyle(R.style.OverflowMenuAnim); } // Turn off window animations for low end devices, and on Android M, which has built-in menu // animations. if (SysUtils.isLowEndDevice() || Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { mPopup.setAnimationStyle(0); } Rect bgPadding = new Rect(); mPopup.getBackground().getPadding(bgPadding); int popupWidth = context.getResources().getDimensionPixelSize(R.dimen.menu_width) + bgPadding.left + bgPadding.right; mPopup.setWidth(popupWidth); mCurrentScreenRotation = screenRotation; mIsByPermanentButton = isByPermanentButton; // Extract visible items from the Menu. int numItems = mMenu.size(); List<MenuItem> menuItems = new ArrayList<MenuItem>(); for (int i = 0; i < numItems; ++i) { MenuItem item = mMenu.getItem(i); if (item.isVisible()) { menuItems.add(item); } } Rect sizingPadding = new Rect(bgPadding); if (isByPermanentButton && originalBgDrawable != null) { Rect originalPadding = new Rect(); originalBgDrawable.getPadding(originalPadding); sizingPadding.top = originalPadding.top; sizingPadding.bottom = originalPadding.bottom; } // A List adapter for visible items in the Menu. The first row is added as a header to the // list view. mAdapter = new AppMenuAdapter(this, menuItems, LayoutInflater.from(context)); mPopup.setAdapter(mAdapter); setMenuHeight(menuItems.size(), visibleDisplayFrame, screenHeight, sizingPadding, footerHeight); setPopupOffset(mPopup, mCurrentScreenRotation, visibleDisplayFrame, sizingPadding); mPopup.setOnItemClickListener(this); mPopup.show(); mPopup.getListView().setItemsCanFocus(true); mPopup.getListView().setOnKeyListener(this); mHandler.onMenuVisibilityChanged(true); if (mVerticalFadeDistance > 0) { mPopup.getListView().setVerticalFadingEdgeEnabled(true); mPopup.getListView().setFadingEdgeLength(mVerticalFadeDistance); } // Don't animate the menu items for low end devices. if (!SysUtils.isLowEndDevice() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { mPopup .getListView() .addOnLayoutChangeListener( new View.OnLayoutChangeListener() { @Override public void onLayoutChange( View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { mPopup.getListView().removeOnLayoutChangeListener(this); runMenuItemEnterAnimations(); } }); } }