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     public void setItemData(MenuItemImpl data) {
    116         mItemData = data;
    117     }
    118 
    119     @Override
    120     public boolean performClick() {
    121         // Let the view's click listener have top priority (the More button relies on this)
    122         if (super.performClick()) {
    123             return true;
    124         }
    125 
    126         if ((mItemInvoker != null) && (mItemInvoker.invokeItem(mItemData))) {
    127             playSoundEffect(SoundEffectConstants.CLICK);
    128             return true;
    129         } else {
    130             return false;
    131         }
    132     }
    133 
    134     public void setTitle(CharSequence title) {
    135 
    136         if (mShortcutCaptionMode) {
    137             /*
    138              * Don't set the title directly since it will replace the
    139              * shortcut+title being shown. Instead, re-set the shortcut caption
    140              * mode so the new title is shown.
    141              */
    142             setCaptionMode(true);
    143 
    144         } else if (title != null) {
    145             setText(title);
    146         }
    147     }
    148 
    149     void setCaptionMode(boolean shortcut) {
    150         /*
    151          * If there is no item model, don't do any of the below (for example,
    152          * the 'More' item doesn't have a model)
    153          */
    154         if (mItemData == null) {
    155             return;
    156         }
    157 
    158         mShortcutCaptionMode = shortcut && (mItemData.shouldShowShortcut());
    159 
    160         CharSequence text = mItemData.getTitleForItemView(this);
    161 
    162         if (mShortcutCaptionMode) {
    163 
    164             if (mShortcutCaption == null) {
    165                 mShortcutCaption = mItemData.getShortcutLabel();
    166             }
    167 
    168             text = mShortcutCaption;
    169         }
    170 
    171         setText(text);
    172     }
    173 
    174     public void setIcon(Drawable icon) {
    175         mIcon = icon;
    176 
    177         if (icon != null) {
    178 
    179             /* Set the bounds of the icon since setCompoundDrawables needs it. */
    180             icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
    181 
    182             // Set the compound drawables
    183             setCompoundDrawables(null, icon, null, null);
    184 
    185             // When there is an icon, make sure the text is at the bottom
    186             setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
    187 
    188             /*
    189              * Request a layout to reposition the icon. The positioning of icon
    190              * depends on this TextView's line bounds, which is only available
    191              * after a layout.
    192              */
    193             requestLayout();
    194         } else {
    195             setCompoundDrawables(null, null, null, null);
    196 
    197             // When there is no icon, make sure the text is centered vertically
    198             setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL);
    199         }
    200     }
    201 
    202     public void setItemInvoker(ItemInvoker itemInvoker) {
    203         mItemInvoker = itemInvoker;
    204     }
    205 
    206     @ViewDebug.CapturedViewProperty(retrieveReturn = true)
    207     public MenuItemImpl getItemData() {
    208         return mItemData;
    209     }
    210 
    211     @Override
    212     public void setVisibility(int v) {
    213         super.setVisibility(v);
    214 
    215         if (mIconMenuView != null) {
    216             // On visibility change, mark the IconMenuView to refresh itself eventually
    217             mIconMenuView.markStaleChildren();
    218         }
    219     }
    220 
    221     void setIconMenuView(IconMenuView iconMenuView) {
    222         mIconMenuView = iconMenuView;
    223     }
    224 
    225     @Override
    226     protected void drawableStateChanged() {
    227         super.drawableStateChanged();
    228 
    229         if (mItemData != null && mIcon != null) {
    230             // When disabled, the not-focused state and the pressed state should
    231             // drop alpha on the icon
    232             final boolean isInAlphaState = !mItemData.isEnabled() && (isPressed() || !isFocused());
    233             mIcon.setAlpha(isInAlphaState ? (int) (mDisabledAlpha * NO_ALPHA) : NO_ALPHA);
    234         }
    235     }
    236 
    237     @Override
    238     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    239         super.onLayout(changed, left, top, right, bottom);
    240 
    241         positionIcon();
    242     }
    243 
    244     @Override
    245     protected void onTextChanged(CharSequence text, int start, int before, int after) {
    246         super.onTextChanged(text, start, before, after);
    247 
    248         // our layout params depend on the length of the text
    249         setLayoutParams(getTextAppropriateLayoutParams());
    250     }
    251 
    252     /**
    253      * @return layout params appropriate for this view.  If layout params already exist, it will
    254      *         augment them to be appropriate to the current text size.
    255      */
    256     IconMenuView.LayoutParams getTextAppropriateLayoutParams() {
    257         IconMenuView.LayoutParams lp = (IconMenuView.LayoutParams) getLayoutParams();
    258         if (lp == null) {
    259             // Default layout parameters
    260             lp = new IconMenuView.LayoutParams(
    261                     IconMenuView.LayoutParams.MATCH_PARENT, IconMenuView.LayoutParams.MATCH_PARENT);
    262         }
    263 
    264         // Set the desired width of item
    265         lp.desiredWidth = (int) Layout.getDesiredWidth(getText(), getPaint());
    266 
    267         return lp;
    268     }
    269 
    270     /**
    271      * Positions the icon vertically (horizontal centering is taken care of by
    272      * the TextView's gravity).
    273      */
    274     private void positionIcon() {
    275 
    276         if (mIcon == null) {
    277             return;
    278         }
    279 
    280         // We reuse the output rectangle as a temp rect
    281         Rect tmpRect = mPositionIconOutput;
    282         getLineBounds(0, tmpRect);
    283         mPositionIconAvailable.set(0, 0, getWidth(), tmpRect.top);
    284         final int layoutDirection = getResolvedLayoutDirection();
    285         Gravity.apply(Gravity.CENTER_VERTICAL | Gravity.LEFT, mIcon.getIntrinsicWidth(), mIcon
    286                 .getIntrinsicHeight(), mPositionIconAvailable, mPositionIconOutput,
    287                 layoutDirection);
    288         mIcon.setBounds(mPositionIconOutput);
    289     }
    290 
    291     public void setCheckable(boolean checkable) {
    292     }
    293 
    294     public void setChecked(boolean checked) {
    295     }
    296 
    297     public void setShortcut(boolean showShortcut, char shortcutKey) {
    298 
    299         if (mShortcutCaptionMode) {
    300             /*
    301              * Shortcut has changed and we're showing it right now, need to
    302              * update (clear the old one first).
    303              */
    304             mShortcutCaption = null;
    305             setCaptionMode(true);
    306         }
    307     }
    308 
    309     public boolean prefersCondensedTitle() {
    310         return true;
    311     }
    312 
    313     public boolean showsIcon() {
    314         return true;
    315     }
    316 
    317 }
    318