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