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