/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.view.menu; import com.android.internal.view.menu.MenuBuilder.ItemInvoker; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.util.AttributeSet; import android.view.Gravity; import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewDebug; import android.widget.TextView; import android.text.Layout; /** * The item view for each item in the {@link IconMenuView}. */ public final class IconMenuItemView extends TextView implements MenuView.ItemView { private static final int NO_ALPHA = 0xFF; private IconMenuView mIconMenuView; private ItemInvoker mItemInvoker; private MenuItemImpl mItemData; private Drawable mIcon; private int mTextAppearance; private Context mTextAppearanceContext; private float mDisabledAlpha; private Rect mPositionIconAvailable = new Rect(); private Rect mPositionIconOutput = new Rect(); private boolean mShortcutCaptionMode; private String mShortcutCaption; private static String sPrependShortcutLabel; public IconMenuItemView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); if (sPrependShortcutLabel == null) { /* * Views should only be constructed from the UI thread, so no * synchronization needed */ sPrependShortcutLabel = getResources().getString( com.android.internal.R.string.prepend_shortcut_label); } final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.MenuView, defStyleAttr, defStyleRes); mDisabledAlpha = a.getFloat( com.android.internal.R.styleable.MenuView_itemIconDisabledAlpha, 0.8f); mTextAppearance = a.getResourceId(com.android.internal.R.styleable. MenuView_itemTextAppearance, -1); mTextAppearanceContext = context; a.recycle(); } public IconMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public IconMenuItemView(Context context, AttributeSet attrs) { this(context, attrs, 0); } /** * Initializes with the provided title and icon * @param title The title of this item * @param icon The icon of this item */ void initialize(CharSequence title, Drawable icon) { setClickable(true); setFocusable(true); if (mTextAppearance != -1) { setTextAppearance(mTextAppearanceContext, mTextAppearance); } setTitle(title); setIcon(icon); if (mItemData != null) { final CharSequence contentDescription = mItemData.getContentDescription(); if (TextUtils.isEmpty(contentDescription)) { setContentDescription(title); } else { setContentDescription(contentDescription); } setTooltipText(mItemData.getTooltipText()); } } public void initialize(MenuItemImpl itemData, int menuType) { mItemData = itemData; initialize(itemData.getTitleForItemView(this), itemData.getIcon()); setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE); setEnabled(itemData.isEnabled()); } public void setItemData(MenuItemImpl data) { mItemData = data; } @Override public boolean performClick() { // Let the view's click listener have top priority (the More button relies on this) if (super.performClick()) { return true; } if ((mItemInvoker != null) && (mItemInvoker.invokeItem(mItemData))) { playSoundEffect(SoundEffectConstants.CLICK); return true; } else { return false; } } public void setTitle(CharSequence title) { if (mShortcutCaptionMode) { /* * Don't set the title directly since it will replace the * shortcut+title being shown. Instead, re-set the shortcut caption * mode so the new title is shown. */ setCaptionMode(true); } else if (title != null) { setText(title); } } void setCaptionMode(boolean shortcut) { /* * If there is no item model, don't do any of the below (for example, * the 'More' item doesn't have a model) */ if (mItemData == null) { return; } mShortcutCaptionMode = shortcut && (mItemData.shouldShowShortcut()); CharSequence text = mItemData.getTitleForItemView(this); if (mShortcutCaptionMode) { if (mShortcutCaption == null) { mShortcutCaption = mItemData.getShortcutLabel(); } text = mShortcutCaption; } setText(text); } public void setIcon(Drawable icon) { mIcon = icon; if (icon != null) { /* Set the bounds of the icon since setCompoundDrawables needs it. */ icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); // Set the compound drawables setCompoundDrawables(null, icon, null, null); // When there is an icon, make sure the text is at the bottom setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL); /* * Request a layout to reposition the icon. The positioning of icon * depends on this TextView's line bounds, which is only available * after a layout. */ requestLayout(); } else { setCompoundDrawables(null, null, null, null); // When there is no icon, make sure the text is centered vertically setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL); } } public void setItemInvoker(ItemInvoker itemInvoker) { mItemInvoker = itemInvoker; } @ViewDebug.CapturedViewProperty(retrieveReturn = true) public MenuItemImpl getItemData() { return mItemData; } @Override public void setVisibility(int v) { super.setVisibility(v); if (mIconMenuView != null) { // On visibility change, mark the IconMenuView to refresh itself eventually mIconMenuView.markStaleChildren(); } } void setIconMenuView(IconMenuView iconMenuView) { mIconMenuView = iconMenuView; } @Override protected void drawableStateChanged() { super.drawableStateChanged(); if (mItemData != null && mIcon != null) { // When disabled, the not-focused state and the pressed state should // drop alpha on the icon final boolean isInAlphaState = !mItemData.isEnabled() && (isPressed() || !isFocused()); mIcon.setAlpha(isInAlphaState ? (int) (mDisabledAlpha * NO_ALPHA) : NO_ALPHA); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); positionIcon(); } @Override protected void onTextChanged(CharSequence text, int start, int before, int after) { super.onTextChanged(text, start, before, after); // our layout params depend on the length of the text setLayoutParams(getTextAppropriateLayoutParams()); } /** * @return layout params appropriate for this view. If layout params already exist, it will * augment them to be appropriate to the current text size. */ IconMenuView.LayoutParams getTextAppropriateLayoutParams() { IconMenuView.LayoutParams lp = (IconMenuView.LayoutParams) getLayoutParams(); if (lp == null) { // Default layout parameters lp = new IconMenuView.LayoutParams( IconMenuView.LayoutParams.MATCH_PARENT, IconMenuView.LayoutParams.MATCH_PARENT); } // Set the desired width of item lp.desiredWidth = (int) Layout.getDesiredWidth(getText(), 0, getText().length(), getPaint(), getTextDirectionHeuristic()); return lp; } /** * Positions the icon vertically (horizontal centering is taken care of by * the TextView's gravity). */ private void positionIcon() { if (mIcon == null) { return; } // We reuse the output rectangle as a temp rect Rect tmpRect = mPositionIconOutput; getLineBounds(0, tmpRect); mPositionIconAvailable.set(0, 0, getWidth(), tmpRect.top); final int layoutDirection = getLayoutDirection(); Gravity.apply(Gravity.CENTER_VERTICAL | Gravity.START, mIcon.getIntrinsicWidth(), mIcon .getIntrinsicHeight(), mPositionIconAvailable, mPositionIconOutput, layoutDirection); mIcon.setBounds(mPositionIconOutput); } public void setCheckable(boolean checkable) { } public void setChecked(boolean checked) { } public void setShortcut(boolean showShortcut, char shortcutKey) { if (mShortcutCaptionMode) { /* * Shortcut has changed and we're showing it right now, need to * update (clear the old one first). */ mShortcutCaption = null; setCaptionMode(true); } } public boolean prefersCondensedTitle() { return true; } public boolean showsIcon() { return true; } }