ActionBarContextView.java revision be4d68e7b238b8ee879de0481e39c40d3f1683b6
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 */
16package com.android.internal.widget;
17
18import com.android.internal.R;
19import com.android.internal.view.menu.ActionMenuView;
20import com.android.internal.view.menu.MenuBuilder;
21
22import android.content.Context;
23import android.content.res.TypedArray;
24import android.graphics.drawable.Drawable;
25import android.util.AttributeSet;
26import android.view.ActionMode;
27import android.view.LayoutInflater;
28import android.view.View;
29import android.view.ViewGroup;
30import android.view.View.MeasureSpec;
31import android.widget.ImageButton;
32import android.widget.LinearLayout;
33import android.widget.TextView;
34
35/**
36 * @hide
37 */
38public class ActionBarContextView extends ViewGroup {
39    private int mContentHeight;
40
41    private CharSequence mTitle;
42    private CharSequence mSubtitle;
43
44    private ImageButton mCloseButton;
45    private View mCustomView;
46    private LinearLayout mTitleLayout;
47    private TextView mTitleView;
48    private TextView mSubtitleView;
49    private int mTitleStyleRes;
50    private int mSubtitleStyleRes;
51    private ActionMenuView mMenuView;
52
53    public ActionBarContextView(Context context) {
54        this(context, null);
55    }
56
57    public ActionBarContextView(Context context, AttributeSet attrs) {
58        this(context, attrs, com.android.internal.R.attr.actionModeStyle);
59    }
60
61    public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) {
62        super(context, attrs, defStyle);
63
64        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionMode, defStyle, 0);
65        setBackgroundDrawable(a.getDrawable(
66                com.android.internal.R.styleable.ActionMode_background));
67        mTitleStyleRes = a.getResourceId(
68                com.android.internal.R.styleable.ActionMode_titleTextStyle, 0);
69        mSubtitleStyleRes = a.getResourceId(
70                com.android.internal.R.styleable.ActionMode_subtitleTextStyle, 0);
71
72        mContentHeight = a.getLayoutDimension(
73                com.android.internal.R.styleable.ActionMode_height, 0);
74        a.recycle();
75    }
76
77    @Override
78    public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
79        // No starting an action mode for an existing action mode UI child! (Where would it go?)
80        return null;
81    }
82
83    public void setHeight(int height) {
84        mContentHeight = height;
85    }
86
87    public void setCustomView(View view) {
88        if (mCustomView != null) {
89            removeView(mCustomView);
90        }
91        mCustomView = view;
92        if (mTitleLayout != null) {
93            removeView(mTitleLayout);
94            mTitleLayout = null;
95        }
96        if (view != null) {
97            addView(view);
98        }
99        requestLayout();
100    }
101
102    public void setTitle(CharSequence title) {
103        mTitle = title;
104        initTitle();
105    }
106
107    public void setSubtitle(CharSequence subtitle) {
108        mSubtitle = subtitle;
109        initTitle();
110    }
111
112    public CharSequence getTitle() {
113        return mTitle;
114    }
115
116    public CharSequence getSubtitle() {
117        return mSubtitle;
118    }
119
120    private void initTitle() {
121        if (mTitleLayout == null) {
122            LayoutInflater inflater = LayoutInflater.from(getContext());
123            mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item, null);
124            mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
125            mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
126            if (mTitle != null) {
127                mTitleView.setText(mTitle);
128                if (mTitleStyleRes != 0) {
129                    mTitleView.setTextAppearance(mContext, mTitleStyleRes);
130                }
131            }
132            if (mSubtitle != null) {
133                mSubtitleView.setText(mSubtitle);
134                if (mSubtitleStyleRes != 0) {
135                    mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
136                }
137                mSubtitleView.setVisibility(VISIBLE);
138            }
139            addView(mTitleLayout);
140        } else {
141            mTitleView.setText(mTitle);
142            mSubtitleView.setText(mSubtitle);
143            mSubtitleView.setVisibility(mSubtitle != null ? VISIBLE : GONE);
144            if (mTitleLayout.getParent() == null) {
145                addView(mTitleLayout);
146            }
147        }
148    }
149
150    public void initForMode(final ActionMode mode) {
151        if (mCloseButton == null) {
152            mCloseButton = new ImageButton(getContext(), null,
153                    com.android.internal.R.attr.actionModeCloseButtonStyle);
154        }
155        mCloseButton.setOnClickListener(new OnClickListener() {
156            public void onClick(View v) {
157                mode.finish();
158            }
159        });
160        addView(mCloseButton);
161
162        final MenuBuilder menu = (MenuBuilder) mode.getMenu();
163        mMenuView = (ActionMenuView) menu.getMenuView(MenuBuilder.TYPE_ACTION_BUTTON, this);
164        mMenuView.setOverflowReserved(true);
165        mMenuView.updateChildren(false);
166        addView(mMenuView);
167    }
168
169    public void closeMode() {
170        removeAllViews();
171        mCustomView = null;
172        mMenuView = null;
173    }
174
175    public boolean showOverflowMenu() {
176        if (mMenuView != null) {
177            return mMenuView.showOverflowMenu();
178        }
179        return false;
180    }
181
182    public boolean hideOverflowMenu() {
183        if (mMenuView != null) {
184            return mMenuView.hideOverflowMenu();
185        }
186        return false;
187    }
188
189    public boolean isOverflowMenuShowing() {
190        if (mMenuView != null) {
191            return mMenuView.isOverflowMenuShowing();
192        }
193        return false;
194    }
195
196    @Override
197    protected LayoutParams generateDefaultLayoutParams() {
198        // Used by custom views if they don't supply layout params. Everything else
199        // added to an ActionBarContextView should have them already.
200        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
201    }
202
203    @Override
204    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
205        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
206        if (widthMode != MeasureSpec.EXACTLY) {
207            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
208                    "with android:layout_width=\"match_parent\" (or fill_parent)");
209        }
210
211        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
212        if (heightMode != MeasureSpec.AT_MOST) {
213            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
214                    "with android:layout_height=\"wrap_content\"");
215        }
216
217        final int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
218
219        int maxHeight = mContentHeight > 0 ?
220                mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
221
222        final int verticalPadding = getPaddingTop() + getPaddingBottom();
223        int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
224        final int height = maxHeight - verticalPadding;
225        final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
226
227        if (mCloseButton != null) {
228            availableWidth = measureChildView(mCloseButton, availableWidth, childSpecHeight, 0);
229        }
230
231        if (mTitleLayout != null && mCustomView == null) {
232            availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0);
233        }
234
235        final int childCount = getChildCount();
236        for (int i = 0; i < childCount; i++) {
237            final View child = getChildAt(i);
238            if (child == mCloseButton || child == mTitleLayout || child == mCustomView) {
239                continue;
240            }
241
242            availableWidth = measureChildView(child, availableWidth, childSpecHeight, 0);
243        }
244
245        if (mCustomView != null) {
246            LayoutParams lp = mCustomView.getLayoutParams();
247            final int customWidthMode = lp.width != LayoutParams.WRAP_CONTENT ?
248                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
249            final int customWidth = lp.width >= 0 ?
250                    Math.min(lp.width, availableWidth) : availableWidth;
251            final int customHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
252                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
253            final int customHeight = lp.height >= 0 ?
254                    Math.min(lp.height, height) : height;
255            mCustomView.measure(MeasureSpec.makeMeasureSpec(customWidth, customWidthMode),
256                    MeasureSpec.makeMeasureSpec(customHeight, customHeightMode));
257        }
258
259        if (mContentHeight <= 0) {
260            int measuredHeight = 0;
261            final int count = getChildCount();
262            for (int i = 0; i < count; i++) {
263                View v = getChildAt(i);
264                int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
265                if (paddedViewHeight > measuredHeight) {
266                    measuredHeight = paddedViewHeight;
267                }
268            }
269            setMeasuredDimension(contentWidth, measuredHeight);
270        } else {
271            setMeasuredDimension(contentWidth, maxHeight);
272        }
273    }
274
275    @Override
276    protected void onLayout(boolean changed, int l, int t, int r, int b) {
277        int x = getPaddingLeft();
278        final int y = getPaddingTop();
279        final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
280
281        if (mCloseButton != null && mCloseButton.getVisibility() != GONE) {
282            x += positionChild(mCloseButton, x, y, contentHeight);
283        }
284
285        if (mTitleLayout != null && mCustomView == null) {
286            x += positionChild(mTitleLayout, x, y, contentHeight);
287        }
288
289        if (mCustomView != null) {
290            x += positionChild(mCustomView, x, y, contentHeight);
291        }
292
293        x = r - l - getPaddingRight();
294
295        if (mMenuView != null) {
296            x -= positionChildInverse(mMenuView, x, y, contentHeight);
297        }
298    }
299
300    private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) {
301        child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
302                childSpecHeight);
303
304        availableWidth -= child.getMeasuredWidth();
305        availableWidth -= spacing;
306
307        return availableWidth;
308    }
309
310    private int positionChild(View child, int x, int y, int contentHeight) {
311        int childWidth = child.getMeasuredWidth();
312        int childHeight = child.getMeasuredHeight();
313        int childTop = y + (contentHeight - childHeight) / 2;
314
315        child.layout(x, childTop, x + childWidth, childTop + childHeight);
316
317        return childWidth;
318    }
319
320    private int positionChildInverse(View child, int x, int y, int contentHeight) {
321        int childWidth = child.getMeasuredWidth();
322        int childHeight = child.getMeasuredHeight();
323        int childTop = y + (contentHeight - childHeight) / 2;
324
325        child.layout(x - childWidth, childTop, x, childTop + childHeight);
326
327        return childWidth;
328    }
329}
330