ActionMenuItemView.java revision 75d022af1f24cf2d8a7551183ea5bbe943d25d21
1/*
2 * Copyright (C) 2010 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 android.content.Context;
20import android.content.res.Resources;
21import android.content.res.TypedArray;
22import android.graphics.Rect;
23import android.graphics.drawable.Drawable;
24import android.text.TextUtils;
25import android.util.AttributeSet;
26import android.view.Gravity;
27import android.view.MotionEvent;
28import android.view.View;
29import android.view.accessibility.AccessibilityEvent;
30import android.widget.TextView;
31import android.widget.Toast;
32
33/**
34 * @hide
35 */
36public class ActionMenuItemView extends TextView
37        implements MenuView.ItemView, View.OnClickListener, View.OnLongClickListener,
38        ActionMenuView.ActionMenuChildView {
39    private static final String TAG = "ActionMenuItemView";
40
41    private MenuItemImpl mItemData;
42    private CharSequence mTitle;
43    private Drawable mIcon;
44    private MenuBuilder.ItemInvoker mItemInvoker;
45
46    private boolean mAllowTextWithIcon;
47    private boolean mExpandedFormat;
48    private int mMinWidth;
49    private int mSavedPaddingLeft;
50
51    public ActionMenuItemView(Context context) {
52        this(context, null);
53    }
54
55    public ActionMenuItemView(Context context, AttributeSet attrs) {
56        this(context, attrs, 0);
57    }
58
59    public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) {
60        super(context, attrs, defStyle);
61        final Resources res = context.getResources();
62        mAllowTextWithIcon = res.getBoolean(
63                com.android.internal.R.bool.config_allowActionMenuItemTextWithIcon);
64        TypedArray a = context.obtainStyledAttributes(attrs,
65                com.android.internal.R.styleable.ActionMenuItemView, 0, 0);
66        mMinWidth = a.getDimensionPixelSize(
67                com.android.internal.R.styleable.ActionMenuItemView_minWidth, 0);
68        a.recycle();
69
70        setOnClickListener(this);
71        setOnLongClickListener(this);
72
73        // Save the inflated padding for later, we'll need it.
74        mSavedPaddingLeft = getPaddingLeft();
75    }
76
77    public MenuItemImpl getItemData() {
78        return mItemData;
79    }
80
81    public void initialize(MenuItemImpl itemData, int menuType) {
82        mItemData = itemData;
83
84        setIcon(itemData.getIcon());
85        setTitle(itemData.getTitleForItemView(this)); // Title only takes effect if there is no icon
86        setId(itemData.getItemId());
87
88        setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
89        setEnabled(itemData.isEnabled());
90    }
91
92    public void onClick(View v) {
93        if (mItemInvoker != null) {
94            mItemInvoker.invokeItem(mItemData);
95        }
96    }
97
98    public void setItemInvoker(MenuBuilder.ItemInvoker invoker) {
99        mItemInvoker = invoker;
100    }
101
102    public boolean prefersCondensedTitle() {
103        return true;
104    }
105
106    public void setCheckable(boolean checkable) {
107        // TODO Support checkable action items
108    }
109
110    public void setChecked(boolean checked) {
111        // TODO Support checkable action items
112    }
113
114    public void setExpandedFormat(boolean expandedFormat) {
115        if (mExpandedFormat != expandedFormat) {
116            mExpandedFormat = expandedFormat;
117            if (mItemData != null) {
118                mItemData.actionFormatChanged();
119            }
120        }
121    }
122
123    private void updateTextButtonVisibility() {
124        boolean visible = !TextUtils.isEmpty(mTitle);
125        visible &= mIcon == null ||
126                (mItemData.showsTextAsAction() && (mAllowTextWithIcon || mExpandedFormat));
127
128        setText(visible ? mTitle : null);
129    }
130
131    public void setIcon(Drawable icon) {
132        mIcon = icon;
133        setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
134
135        updateTextButtonVisibility();
136    }
137
138    public boolean hasText() {
139        return !TextUtils.isEmpty(getText());
140    }
141
142    public void setShortcut(boolean showShortcut, char shortcutKey) {
143        // Action buttons don't show text for shortcut keys.
144    }
145
146    public void setTitle(CharSequence title) {
147        mTitle = title;
148
149        setContentDescription(mTitle);
150        updateTextButtonVisibility();
151    }
152
153    @Override
154    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
155        onPopulateAccessibilityEvent(event);
156        return true;
157    }
158
159    @Override
160    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
161        super.onPopulateAccessibilityEvent(event);
162        final CharSequence cdesc = getContentDescription();
163        if (!TextUtils.isEmpty(cdesc)) {
164            event.getText().add(cdesc);
165        }
166    }
167
168    @Override
169    public boolean dispatchHoverEvent(MotionEvent event) {
170        // Don't allow children to hover; we want this to be treated as a single component.
171        return onHoverEvent(event);
172    }
173
174    public boolean showsIcon() {
175        return true;
176    }
177
178    public boolean needsDividerBefore() {
179        return hasText() && mItemData.getIcon() == null;
180    }
181
182    public boolean needsDividerAfter() {
183        return hasText();
184    }
185
186    @Override
187    public boolean onLongClick(View v) {
188        if (hasText()) {
189            // Don't show the cheat sheet for items that already show text.
190            return false;
191        }
192
193        final int[] screenPos = new int[2];
194        final Rect displayFrame = new Rect();
195        getLocationOnScreen(screenPos);
196        getWindowVisibleDisplayFrame(displayFrame);
197
198        final Context context = getContext();
199        final int width = getWidth();
200        final int height = getHeight();
201        final int midy = screenPos[1] + height / 2;
202        final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
203
204        Toast cheatSheet = Toast.makeText(context, mItemData.getTitle(), Toast.LENGTH_SHORT);
205        if (midy < displayFrame.height()) {
206            // Show along the top; follow action buttons
207            cheatSheet.setGravity(Gravity.TOP | Gravity.RIGHT,
208                    screenWidth - screenPos[0] - width / 2, height);
209        } else {
210            // Show along the bottom center
211            cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
212        }
213        cheatSheet.show();
214        return true;
215    }
216
217    @Override
218    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
219        final boolean textVisible = hasText();
220        if (textVisible) {
221            setPadding(mSavedPaddingLeft, getPaddingTop(), getPaddingRight(), getPaddingBottom());
222        }
223
224        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
225
226        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
227        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
228        final int oldMeasuredWidth = getMeasuredWidth();
229        final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(widthSize, mMinWidth)
230                : mMinWidth;
231
232        if (widthMode != MeasureSpec.EXACTLY && mMinWidth > 0 && oldMeasuredWidth < targetWidth) {
233            // Remeasure at exactly the minimum width.
234            super.onMeasure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
235                    heightMeasureSpec);
236        }
237
238        if (!textVisible && mIcon != null) {
239            // TextView won't center compound drawables in both dimensions without
240            // a little coercion. Pad in to center the icon after we've measured.
241            final int w = getMeasuredWidth();
242            final int dw = mIcon.getIntrinsicWidth();
243            setPadding((w - dw) / 2, getPaddingTop(), getPaddingRight(), getPaddingBottom());
244        }
245    }
246}
247