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