Home | History | Annotate | Download | only in menu
      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 
     17 package com.android.internal.view.menu;
     18 
     19 import com.android.internal.view.menu.MenuBuilder.ItemInvoker;
     20 
     21 import android.content.Context;
     22 import android.content.res.TypedArray;
     23 import android.graphics.Rect;
     24 import android.graphics.drawable.Drawable;
     25 import android.util.AttributeSet;
     26 import android.view.Gravity;
     27 import android.view.SoundEffectConstants;
     28 import android.view.View;
     29 import android.view.ViewDebug;
     30 import android.widget.TextView;
     31 import android.text.Layout;
     32 
     33 /**
     34  * The item view for each item in the {@link IconMenuView}.
     35  */
     36 public final class IconMenuItemView extends TextView implements MenuView.ItemView {
     37 
     38     private static final int NO_ALPHA = 0xFF;
     39 
     40     private IconMenuView mIconMenuView;
     41 
     42     private ItemInvoker mItemInvoker;
     43     private MenuItemImpl mItemData;
     44 
     45     private Drawable mIcon;
     46 
     47     private int mTextAppearance;
     48     private Context mTextAppearanceContext;
     49 
     50     private float mDisabledAlpha;
     51 
     52     private Rect mPositionIconAvailable = new Rect();
     53     private Rect mPositionIconOutput = new Rect();
     54 
     55     private boolean mShortcutCaptionMode;
     56     private String mShortcutCaption;
     57 
     58     private static String sPrependShortcutLabel;
     59 
     60     public IconMenuItemView(Context context, AttributeSet attrs, int defStyle) {
     61         super(context, attrs);
     62 
     63         if (sPrependShortcutLabel == null) {
     64             /*
     65              * Views should only be constructed from the UI thread, so no
     66              * synchronization needed
     67              */
     68             sPrependShortcutLabel = getResources().getString(
     69                     com.android.internal.R.string.prepend_shortcut_label);
     70         }
     71 
     72         TypedArray a =
     73             context.obtainStyledAttributes(
     74                 attrs, com.android.internal.R.styleable.MenuView, defStyle, 0);
     75 
     76         mDisabledAlpha = a.getFloat(
     77                 com.android.internal.R.styleable.MenuView_itemIconDisabledAlpha, 0.8f);
     78         mTextAppearance = a.getResourceId(com.android.internal.R.styleable.
     79                                           MenuView_itemTextAppearance, -1);
     80         mTextAppearanceContext = context;
     81 
     82         a.recycle();
     83     }
     84 
     85     public IconMenuItemView(Context context, AttributeSet attrs) {
     86         this(context, attrs, 0);
     87     }
     88 
     89     /**
     90      * Initializes with the provided title and icon
     91      * @param title The title of this item
     92      * @param icon The icon of this item
     93      */
     94     void initialize(CharSequence title, Drawable icon) {
     95         setClickable(true);
     96         setFocusable(true);
     97 
     98         if (mTextAppearance != -1) {
     99             setTextAppearance(mTextAppearanceContext, mTextAppearance);
    100         }
    101 
    102         setTitle(title);
    103         setIcon(icon);
    104     }
    105 
    106     public void initialize(MenuItemImpl itemData, int menuType) {
    107         mItemData = itemData;
    108 
    109         initialize(itemData.getTitleForItemView(this), itemData.getIcon());
    110 
    111         setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
    112         setEnabled(itemData.isEnabled());
    113     }
    114 
    115     @Override
    116     public boolean performClick() {
    117         // Let the view's click listener have top priority (the More button relies on this)
    118         if (super.performClick()) {
    119             return true;
    120         }
    121 
    122         if ((mItemInvoker != null) && (mItemInvoker.invokeItem(mItemData))) {
    123             playSoundEffect(SoundEffectConstants.CLICK);
    124             return true;
    125         } else {
    126             return false;
    127         }
    128     }
    129 
    130     public void setTitle(CharSequence title) {
    131 
    132         if (mShortcutCaptionMode) {
    133             /*
    134              * Don't set the title directly since it will replace the
    135              * shortcut+title being shown. Instead, re-set the shortcut caption
    136              * mode so the new title is shown.
    137              */
    138             setCaptionMode(true);
    139 
    140         } else if (title != null) {
    141             setText(title);
    142         }
    143     }
    144 
    145     void setCaptionMode(boolean shortcut) {
    146         /*
    147          * If there is no item model, don't do any of the below (for example,
    148          * the 'More' item doesn't have a model)
    149          */
    150         if (mItemData == null) {
    151             return;
    152         }
    153 
    154         mShortcutCaptionMode = shortcut && (mItemData.shouldShowShortcut());
    155 
    156         CharSequence text = mItemData.getTitleForItemView(this);
    157 
    158         if (mShortcutCaptionMode) {
    159 
    160             if (mShortcutCaption == null) {
    161                 mShortcutCaption = mItemData.getShortcutLabel();
    162             }
    163 
    164             text = mShortcutCaption;
    165         }
    166 
    167         setText(text);
    168     }
    169 
    170     public void setIcon(Drawable icon) {
    171         mIcon = icon;
    172 
    173         if (icon != null) {
    174 
    175             /* Set the bounds of the icon since setCompoundDrawables needs it. */
    176             icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
    177 
    178             // Set the compound drawables
    179             setCompoundDrawables(null, icon, null, null);
    180 
    181             // When there is an icon, make sure the text is at the bottom
    182             setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
    183 
    184             /*
    185              * Request a layout to reposition the icon. The positioning of icon
    186              * depends on this TextView's line bounds, which is only available
    187              * after a layout.
    188              */
    189             requestLayout();
    190         } else {
    191             setCompoundDrawables(null, null, null, null);
    192 
    193             // When there is no icon, make sure the text is centered vertically
    194             setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL);
    195         }
    196     }
    197 
    198     public void setItemInvoker(ItemInvoker itemInvoker) {
    199         mItemInvoker = itemInvoker;
    200     }
    201 
    202     @ViewDebug.CapturedViewProperty(retrieveReturn = true)
    203     public MenuItemImpl getItemData() {
    204         return mItemData;
    205     }
    206 
    207     @Override
    208     public void setVisibility(int v) {
    209         super.setVisibility(v);
    210 
    211         if (mIconMenuView != null) {
    212             // On visibility change, mark the IconMenuView to refresh itself eventually
    213             mIconMenuView.markStaleChildren();
    214         }
    215     }
    216 
    217     void setIconMenuView(IconMenuView iconMenuView) {
    218         mIconMenuView = iconMenuView;
    219     }
    220 
    221     @Override
    222     protected void drawableStateChanged() {
    223         super.drawableStateChanged();
    224 
    225         if (mItemData != null && mIcon != null) {
    226             // When disabled, the not-focused state and the pressed state should
    227             // drop alpha on the icon
    228             final boolean isInAlphaState = !mItemData.isEnabled() && (isPressed() || !isFocused());
    229             mIcon.setAlpha(isInAlphaState ? (int) (mDisabledAlpha * NO_ALPHA) : NO_ALPHA);
    230         }
    231     }
    232 
    233     @Override
    234     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    235         super.onLayout(changed, left, top, right, bottom);
    236 
    237         positionIcon();
    238     }
    239 
    240     @Override
    241     protected void onTextChanged(CharSequence text, int start, int before, int after) {
    242         super.onTextChanged(text, start, before, after);
    243 
    244         // our layout params depend on the length of the text
    245         setLayoutParams(getTextAppropriateLayoutParams());
    246     }
    247 
    248     /**
    249      * @return layout params appropriate for this view.  If layout params already exist, it will
    250      *         augment them to be appropriate to the current text size.
    251      */
    252     IconMenuView.LayoutParams getTextAppropriateLayoutParams() {
    253         IconMenuView.LayoutParams lp = (IconMenuView.LayoutParams) getLayoutParams();
    254         if (lp == null) {
    255             // Default layout parameters
    256             lp = new IconMenuView.LayoutParams(
    257                     IconMenuView.LayoutParams.MATCH_PARENT, IconMenuView.LayoutParams.MATCH_PARENT);
    258         }
    259 
    260         // Set the desired width of item
    261         lp.desiredWidth = (int) Layout.getDesiredWidth(getText(), getPaint());
    262 
    263         return lp;
    264     }
    265 
    266     /**
    267      * Positions the icon vertically (horizontal centering is taken care of by
    268      * the TextView's gravity).
    269      */
    270     private void positionIcon() {
    271 
    272         if (mIcon == null) {
    273             return;
    274         }
    275 
    276         // We reuse the output rectangle as a temp rect
    277         Rect tmpRect = mPositionIconOutput;
    278         getLineBounds(0, tmpRect);
    279         mPositionIconAvailable.set(0, 0, getWidth(), tmpRect.top);
    280         Gravity.apply(Gravity.CENTER_VERTICAL | Gravity.LEFT, mIcon.getIntrinsicWidth(), mIcon
    281                 .getIntrinsicHeight(), mPositionIconAvailable, mPositionIconOutput);
    282         mIcon.setBounds(mPositionIconOutput);
    283     }
    284 
    285     public void setCheckable(boolean checkable) {
    286     }
    287 
    288     public void setChecked(boolean checked) {
    289     }
    290 
    291     public void setShortcut(boolean showShortcut, char shortcutKey) {
    292 
    293         if (mShortcutCaptionMode) {
    294             /*
    295              * Shortcut has changed and we're showing it right now, need to
    296              * update (clear the old one first).
    297              */
    298             mShortcutCaption = null;
    299             setCaptionMode(true);
    300         }
    301     }
    302 
    303     public boolean prefersCondensedTitle() {
    304         return true;
    305     }
    306 
    307     public boolean showsIcon() {
    308         return true;
    309     }
    310 
    311 }
    312