IconMenuItemView.java revision 22f7dfd23490a3de2f21ff96949ba47003aac8f8
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
17package com.android.internal.view.menu;
18
19import com.android.internal.view.menu.MenuBuilder.ItemInvoker;
20
21import android.content.Context;
22import android.content.res.TypedArray;
23import android.graphics.Rect;
24import android.graphics.drawable.Drawable;
25import android.util.AttributeSet;
26import android.view.Gravity;
27import android.view.SoundEffectConstants;
28import android.view.View;
29import android.view.ViewDebug;
30import android.widget.TextView;
31
32/**
33 * The item view for each item in the {@link IconMenuView}.
34 */
35public final class IconMenuItemView extends TextView implements MenuView.ItemView {
36
37    private static final int NO_ALPHA = 0xFF;
38
39    private IconMenuView mIconMenuView;
40
41    private ItemInvoker mItemInvoker;
42    private MenuItemImpl mItemData;
43
44    private Drawable mIcon;
45
46    private int mTextAppearance;
47    private Context mTextAppearanceContext;
48
49    private float mDisabledAlpha;
50
51    private Rect mPositionIconAvailable = new Rect();
52    private Rect mPositionIconOutput = new Rect();
53
54    private boolean mShortcutCaptionMode;
55    private String mShortcutCaption;
56
57    private static String sPrependShortcutLabel;
58
59    public IconMenuItemView(Context context, AttributeSet attrs, int defStyle) {
60        super(context, attrs);
61
62        if (sPrependShortcutLabel == null) {
63            /*
64             * Views should only be constructed from the UI thread, so no
65             * synchronization needed
66             */
67            sPrependShortcutLabel = getResources().getString(
68                    com.android.internal.R.string.prepend_shortcut_label);
69        }
70
71        TypedArray a =
72            context.obtainStyledAttributes(
73                attrs, com.android.internal.R.styleable.MenuView, defStyle, 0);
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) {
85        this(context, attrs, 0);
86    }
87
88    /**
89     * Initializes with the provided title and icon
90     * @param title The title of this item
91     * @param icon The icon of this item
92     */
93    void initialize(CharSequence title, Drawable icon) {
94        setClickable(true);
95        setFocusable(true);
96
97        if (mTextAppearance != -1) {
98            setTextAppearance(mTextAppearanceContext, mTextAppearance);
99        }
100
101        setTitle(title);
102        setIcon(icon);
103    }
104
105    public void initialize(MenuItemImpl itemData, int menuType) {
106        mItemData = itemData;
107
108        initialize(itemData.getTitleForItemView(this), itemData.getIcon());
109
110        setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
111        setEnabled(itemData.isEnabled());
112    }
113
114    @Override
115    public boolean performClick() {
116        // Let the view's click listener have top priority (the More button relies on this)
117        if (super.performClick()) {
118            return true;
119        }
120
121        if ((mItemInvoker != null) && (mItemInvoker.invokeItem(mItemData))) {
122            playSoundEffect(SoundEffectConstants.CLICK);
123            return true;
124        } else {
125            return false;
126        }
127    }
128
129    public void setTitle(CharSequence title) {
130
131        if (mShortcutCaptionMode) {
132            /*
133             * Don't set the title directly since it will replace the
134             * shortcut+title being shown. Instead, re-set the shortcut caption
135             * mode so the new title is shown.
136             */
137            setCaptionMode(true);
138
139        } else if (title != null) {
140            setText(title);
141        }
142    }
143
144    void setCaptionMode(boolean shortcut) {
145
146        mShortcutCaptionMode = shortcut && (mItemData.shouldShowShortcut());
147
148        /*
149         * If there is no item model, don't do any of the below (for example,
150         * the 'More' item doesn't have a model)
151         */
152        if (mItemData == null) {
153            return;
154        }
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    /**
241     * Positions the icon vertically (horizontal centering is taken care of by
242     * the TextView's gravity).
243     */
244    private void positionIcon() {
245
246        if (mIcon == null) {
247            return;
248        }
249
250        // We reuse the output rectangle as a temp rect
251        Rect tmpRect = mPositionIconOutput;
252        getLineBounds(0, tmpRect);
253        mPositionIconAvailable.set(0, 0, getWidth(), tmpRect.top);
254        Gravity.apply(Gravity.CENTER_VERTICAL | Gravity.LEFT, mIcon.getIntrinsicWidth(), mIcon
255                .getIntrinsicHeight(), mPositionIconAvailable, mPositionIconOutput);
256        mIcon.setBounds(mPositionIconOutput);
257    }
258
259    public void setCheckable(boolean checkable) {
260    }
261
262    public void setChecked(boolean checked) {
263    }
264
265    public void setShortcut(boolean showShortcut, char shortcutKey) {
266
267        if (mShortcutCaptionMode) {
268            /*
269             * Shortcut has changed and we're showing it right now, need to
270             * update (clear the old one first).
271             */
272            mShortcutCaption = null;
273            setCaptionMode(true);
274        }
275    }
276
277    public boolean prefersCondensedTitle() {
278        return true;
279    }
280
281    public boolean showsIcon() {
282        return true;
283    }
284
285}
286