ActionBarContextView.java revision d8222dedaacae30fa03bee346ebf42ee75b39928
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 android.support.v7.internal.widget;
17
18import android.content.Context;
19import android.content.res.TypedArray;
20import android.graphics.drawable.Drawable;
21import android.support.v7.appcompat.R;
22import android.support.v7.view.ActionMode;
23import android.support.v7.internal.view.menu.ActionMenuPresenter;
24import android.support.v7.internal.view.menu.ActionMenuView;
25import android.support.v7.internal.view.menu.MenuBuilder;
26import android.text.TextUtils;
27import android.util.AttributeSet;
28import android.view.LayoutInflater;
29import android.view.View;
30import android.view.ViewGroup;
31import android.widget.LinearLayout;
32import android.widget.TextView;
33
34/**
35 * @hide
36 */
37public class ActionBarContextView extends AbsActionBarView {
38
39    private static final String TAG = "ActionBarContextView";
40
41    private CharSequence mTitle;
42    private CharSequence mSubtitle;
43
44    private View mClose;
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 Drawable mSplitBackground;
52    private boolean mTitleOptional;
53
54    public ActionBarContextView(Context context) {
55        this(context, null);
56    }
57
58    public ActionBarContextView(Context context, AttributeSet attrs) {
59        this(context, attrs, R.attr.actionModeStyle);
60    }
61
62    public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) {
63        super(context, attrs, defStyle);
64
65        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionMode, defStyle, 0);
66        setBackgroundDrawable(a.getDrawable(
67                R.styleable.ActionMode_background));
68        mTitleStyleRes = a.getResourceId(
69                R.styleable.ActionMode_titleTextStyle, 0);
70        mSubtitleStyleRes = a.getResourceId(
71                R.styleable.ActionMode_subtitleTextStyle, 0);
72
73        mContentHeight = a.getLayoutDimension(
74                R.styleable.ActionMode_height, 0);
75
76        mSplitBackground = a.getDrawable(
77                R.styleable.ActionMode_backgroundSplit);
78
79        a.recycle();
80    }
81
82    @Override
83    public void onDetachedFromWindow() {
84        super.onDetachedFromWindow();
85        if (mActionMenuPresenter != null) {
86            mActionMenuPresenter.hideOverflowMenu();
87            mActionMenuPresenter.hideSubMenus();
88        }
89    }
90
91    @Override
92    public void setSplitActionBar(boolean split) {
93        if (mSplitActionBar != split) {
94            if (mActionMenuPresenter != null) {
95                // Mode is already active; move everything over and adjust the menu itself.
96                final ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
97                        ViewGroup.LayoutParams.WRAP_CONTENT,
98                        ViewGroup.LayoutParams.FILL_PARENT);
99                if (!split) {
100                    mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
101                    mMenuView.setBackgroundDrawable(null);
102                    final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
103                    if (oldParent != null) {
104                        oldParent.removeView(mMenuView);
105                    }
106                    addView(mMenuView, layoutParams);
107                } else {
108                    // Allow full screen width in split mode.
109                    mActionMenuPresenter.setWidthLimit(
110                            getContext().getResources().getDisplayMetrics().widthPixels, true);
111                    // No limit to the item count; use whatever will fit.
112                    mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
113                    // Span the whole width
114                    layoutParams.width = ViewGroup.LayoutParams.FILL_PARENT;
115                    layoutParams.height = mContentHeight;
116                    mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
117                    mMenuView.setBackgroundDrawable(mSplitBackground);
118                    final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
119                    if (oldParent != null) {
120                        oldParent.removeView(mMenuView);
121                    }
122                    mSplitView.addView(mMenuView, layoutParams);
123                }
124            }
125            super.setSplitActionBar(split);
126        }
127    }
128
129    public void setContentHeight(int height) {
130        mContentHeight = height;
131    }
132
133    public void setCustomView(View view) {
134        if (mCustomView != null) {
135            removeView(mCustomView);
136        }
137        mCustomView = view;
138        if (mTitleLayout != null) {
139            removeView(mTitleLayout);
140            mTitleLayout = null;
141        }
142        if (view != null) {
143            addView(view);
144        }
145        requestLayout();
146    }
147
148    public void setTitle(CharSequence title) {
149        mTitle = title;
150        initTitle();
151    }
152
153    public void setSubtitle(CharSequence subtitle) {
154        mSubtitle = subtitle;
155        initTitle();
156    }
157
158    public CharSequence getTitle() {
159        return mTitle;
160    }
161
162    public CharSequence getSubtitle() {
163        return mSubtitle;
164    }
165
166    private void initTitle() {
167        if (mTitleLayout == null) {
168            LayoutInflater inflater = LayoutInflater.from(getContext());
169            inflater.inflate(R.layout.action_bar_title_item, this);
170            mTitleLayout = (LinearLayout) getChildAt(getChildCount() - 1);
171            mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
172            mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
173            if (mTitleStyleRes != 0) {
174                mTitleView.setTextAppearance(getContext(), mTitleStyleRes);
175            }
176            if (mSubtitleStyleRes != 0) {
177                mSubtitleView.setTextAppearance(getContext(), mSubtitleStyleRes);
178            }
179        }
180
181        mTitleView.setText(mTitle);
182        mSubtitleView.setText(mSubtitle);
183
184        final boolean hasTitle = !TextUtils.isEmpty(mTitle);
185        final boolean hasSubtitle = !TextUtils.isEmpty(mSubtitle);
186        mSubtitleView.setVisibility(hasSubtitle ? VISIBLE : GONE);
187        mTitleLayout.setVisibility(hasTitle || hasSubtitle ? VISIBLE : GONE);
188        if (mTitleLayout.getParent() == null) {
189            addView(mTitleLayout);
190        }
191    }
192
193    public void initForMode(final ActionMode mode) {
194        if (mClose == null) {
195            LayoutInflater inflater = LayoutInflater.from(getContext());
196            mClose = inflater.inflate(R.layout.action_mode_close_item, this, false);
197            addView(mClose);
198        } else if (mClose.getParent() == null) {
199            addView(mClose);
200        }
201
202        View closeButton = mClose.findViewById(R.id.action_mode_close_button);
203        closeButton.setOnClickListener(new View.OnClickListener() {
204            public void onClick(View v) {
205                mode.finish();
206            }
207        });
208
209        final MenuBuilder menu = (MenuBuilder) mode.getMenu();
210        if (mActionMenuPresenter != null) {
211            mActionMenuPresenter.dismissPopupMenus();
212        }
213        mActionMenuPresenter = new ActionMenuPresenter(getContext());
214        mActionMenuPresenter.setReserveOverflow(true);
215
216        final ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
217                ViewGroup.LayoutParams.WRAP_CONTENT,
218                ViewGroup.LayoutParams.FILL_PARENT);
219        if (!mSplitActionBar) {
220            menu.addMenuPresenter(mActionMenuPresenter);
221            mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
222            mMenuView.setBackgroundDrawable(null);
223            addView(mMenuView, layoutParams);
224        } else {
225            // Allow full screen width in split mode.
226            mActionMenuPresenter.setWidthLimit(
227                    getContext().getResources().getDisplayMetrics().widthPixels, true);
228            // No limit to the item count; use whatever will fit.
229            mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
230            // Span the whole width
231            layoutParams.width = ViewGroup.LayoutParams.FILL_PARENT;
232            layoutParams.height = mContentHeight;
233            menu.addMenuPresenter(mActionMenuPresenter);
234            mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
235            mMenuView.setBackgroundDrawable(mSplitBackground);
236            mSplitView.addView(mMenuView, layoutParams);
237        }
238    }
239
240    public void closeMode() {
241        if (mClose == null) {
242            killMode();
243            return;
244        }
245    }
246
247    public void killMode() {
248        removeAllViews();
249        if (mSplitView != null) {
250            mSplitView.removeView(mMenuView);
251        }
252        mCustomView = null;
253        mMenuView = null;
254    }
255
256    @Override
257    public boolean showOverflowMenu() {
258        if (mActionMenuPresenter != null) {
259            return mActionMenuPresenter.showOverflowMenu();
260        }
261        return false;
262    }
263
264    @Override
265    public boolean hideOverflowMenu() {
266        if (mActionMenuPresenter != null) {
267            return mActionMenuPresenter.hideOverflowMenu();
268        }
269        return false;
270    }
271
272    @Override
273    public boolean isOverflowMenuShowing() {
274        if (mActionMenuPresenter != null) {
275            return mActionMenuPresenter.isOverflowMenuShowing();
276        }
277        return false;
278    }
279
280    @Override
281    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
282        // Used by custom views if they don't supply layout params. Everything else
283        // added to an ActionBarContextView should have them already.
284        return new ViewGroup.MarginLayoutParams(
285                ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
286    }
287
288    @Override
289    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
290        return new ViewGroup.MarginLayoutParams(getContext(), attrs);
291    }
292
293    @Override
294    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
295        final int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
296        if (widthMode != View.MeasureSpec.EXACTLY) {
297            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
298                    "with android:layout_width=\"FILL_PARENT\" (or fill_parent)");
299        }
300
301        final int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
302        if (heightMode == View.MeasureSpec.UNSPECIFIED) {
303            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
304                    "with android:layout_height=\"wrap_content\"");
305        }
306
307        final int contentWidth = View.MeasureSpec.getSize(widthMeasureSpec);
308
309        int maxHeight = mContentHeight > 0 ?
310                mContentHeight : View.MeasureSpec.getSize(heightMeasureSpec);
311
312        final int verticalPadding = getPaddingTop() + getPaddingBottom();
313        int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
314        final int height = maxHeight - verticalPadding;
315        final int childSpecHeight = View.MeasureSpec
316                .makeMeasureSpec(height, View.MeasureSpec.AT_MOST);
317
318        if (mClose != null) {
319            availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0);
320            ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mClose
321                    .getLayoutParams();
322            availableWidth -= lp.leftMargin + lp.rightMargin;
323        }
324
325        if (mMenuView != null && mMenuView.getParent() == this) {
326            availableWidth = measureChildView(mMenuView, availableWidth,
327                    childSpecHeight, 0);
328        }
329
330        if (mTitleLayout != null && mCustomView == null) {
331            if (mTitleOptional) {
332                final int titleWidthSpec = View.MeasureSpec
333                        .makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
334                mTitleLayout.measure(titleWidthSpec, childSpecHeight);
335                final int titleWidth = mTitleLayout.getMeasuredWidth();
336                final boolean titleFits = titleWidth <= availableWidth;
337                if (titleFits) {
338                    availableWidth -= titleWidth;
339                }
340                mTitleLayout.setVisibility(titleFits ? VISIBLE : GONE);
341            } else {
342                availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0);
343            }
344        }
345
346        if (mCustomView != null) {
347            ViewGroup.LayoutParams lp = mCustomView.getLayoutParams();
348            final int customWidthMode = lp.width != ViewGroup.LayoutParams.WRAP_CONTENT ?
349                    View.MeasureSpec.EXACTLY : View.MeasureSpec.AT_MOST;
350            final int customWidth = lp.width >= 0 ?
351                    Math.min(lp.width, availableWidth) : availableWidth;
352            final int customHeightMode = lp.height != ViewGroup.LayoutParams.WRAP_CONTENT ?
353                    View.MeasureSpec.EXACTLY : View.MeasureSpec.AT_MOST;
354            final int customHeight = lp.height >= 0 ?
355                    Math.min(lp.height, height) : height;
356            mCustomView.measure(View.MeasureSpec.makeMeasureSpec(customWidth, customWidthMode),
357                    View.MeasureSpec.makeMeasureSpec(customHeight, customHeightMode));
358        }
359
360        if (mContentHeight <= 0) {
361            int measuredHeight = 0;
362            final int count = getChildCount();
363            for (int i = 0; i < count; i++) {
364                View v = getChildAt(i);
365                int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
366                if (paddedViewHeight > measuredHeight) {
367                    measuredHeight = paddedViewHeight;
368                }
369            }
370            setMeasuredDimension(contentWidth, measuredHeight);
371        } else {
372            setMeasuredDimension(contentWidth, maxHeight);
373        }
374    }
375
376    @Override
377    protected void onLayout(boolean changed, int l, int t, int r, int b) {
378        int x = getPaddingLeft();
379        final int y = getPaddingTop();
380        final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
381
382        if (mClose != null && mClose.getVisibility() != GONE) {
383            ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mClose
384                    .getLayoutParams();
385            x += lp.leftMargin;
386            x += positionChild(mClose, x, y, contentHeight);
387            x += lp.rightMargin;
388
389        }
390
391        if (mTitleLayout != null && mCustomView == null && mTitleLayout.getVisibility() != GONE) {
392            x += positionChild(mTitleLayout, x, y, contentHeight);
393        }
394
395        if (mCustomView != null) {
396            x += positionChild(mCustomView, x, y, contentHeight);
397        }
398
399        x = r - l - getPaddingRight();
400
401        if (mMenuView != null) {
402            x -= positionChildInverse(mMenuView, x, y, contentHeight);
403        }
404    }
405
406    public void setTitleOptional(boolean titleOptional) {
407        if (titleOptional != mTitleOptional) {
408            requestLayout();
409        }
410        mTitleOptional = titleOptional;
411    }
412
413    public boolean isTitleOptional() {
414        return mTitleOptional;
415    }
416}
417