ActionBarContainer.java revision ccd3da23c6dbdda2af5c5d746652e4b240cb4f50
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.widget;
18
19import android.annotation.NonNull;
20import android.content.Context;
21import android.content.res.TypedArray;
22import android.graphics.Canvas;
23import android.graphics.ColorFilter;
24import android.graphics.Outline;
25import android.graphics.drawable.Drawable;
26import android.util.AttributeSet;
27import android.view.ActionMode;
28import android.view.MotionEvent;
29import android.view.View;
30import android.view.ViewGroup;
31import android.widget.FrameLayout;
32
33/**
34 * This class acts as a container for the action bar view and action mode context views.
35 * It applies special styles as needed to help handle animated transitions between them.
36 * @hide
37 */
38public class ActionBarContainer extends FrameLayout {
39    private boolean mIsTransitioning;
40    private View mTabContainer;
41    private View mActionBarView;
42    private View mActionContextView;
43
44    private Drawable mBackground;
45    private Drawable mStackedBackground;
46    private Drawable mSplitBackground;
47    private boolean mIsSplit;
48    private boolean mIsStacked;
49    private int mHeight;
50
51    public ActionBarContainer(Context context) {
52        this(context, null);
53    }
54
55    public ActionBarContainer(Context context, AttributeSet attrs) {
56        super(context, attrs);
57
58        // Set a transparent background so that we project appropriately.
59        setBackground(new ActionBarBackgroundDrawable());
60
61        TypedArray a = context.obtainStyledAttributes(attrs,
62                com.android.internal.R.styleable.ActionBar);
63        mBackground = a.getDrawable(com.android.internal.R.styleable.ActionBar_background);
64        mStackedBackground = a.getDrawable(
65                com.android.internal.R.styleable.ActionBar_backgroundStacked);
66        mHeight = a.getDimensionPixelSize(com.android.internal.R.styleable.ActionBar_height, -1);
67
68        if (getId() == com.android.internal.R.id.split_action_bar) {
69            mIsSplit = true;
70            mSplitBackground = a.getDrawable(
71                    com.android.internal.R.styleable.ActionBar_backgroundSplit);
72        }
73        a.recycle();
74
75        setWillNotDraw(mIsSplit ? mSplitBackground == null :
76                mBackground == null && mStackedBackground == null);
77    }
78
79    @Override
80    public void onFinishInflate() {
81        super.onFinishInflate();
82        mActionBarView = findViewById(com.android.internal.R.id.action_bar);
83        mActionContextView = findViewById(com.android.internal.R.id.action_context_bar);
84    }
85
86    public void setPrimaryBackground(Drawable bg) {
87        if (mBackground != null) {
88            mBackground.setCallback(null);
89            unscheduleDrawable(mBackground);
90        }
91        mBackground = bg;
92        if (bg != null) {
93            bg.setCallback(this);
94            if (mActionBarView != null) {
95                mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(),
96                        mActionBarView.getRight(), mActionBarView.getBottom());
97            }
98        }
99        setWillNotDraw(mIsSplit ? mSplitBackground == null :
100                mBackground == null && mStackedBackground == null);
101        invalidate();
102    }
103
104    public void setStackedBackground(Drawable bg) {
105        if (mStackedBackground != null) {
106            mStackedBackground.setCallback(null);
107            unscheduleDrawable(mStackedBackground);
108        }
109        mStackedBackground = bg;
110        if (bg != null) {
111            bg.setCallback(this);
112            if ((mIsStacked && mStackedBackground != null)) {
113                mStackedBackground.setBounds(mTabContainer.getLeft(), mTabContainer.getTop(),
114                        mTabContainer.getRight(), mTabContainer.getBottom());
115            }
116        }
117        setWillNotDraw(mIsSplit ? mSplitBackground == null :
118                mBackground == null && mStackedBackground == null);
119        invalidate();
120    }
121
122    public void setSplitBackground(Drawable bg) {
123        if (mSplitBackground != null) {
124            mSplitBackground.setCallback(null);
125            unscheduleDrawable(mSplitBackground);
126        }
127        mSplitBackground = bg;
128        if (bg != null) {
129            bg.setCallback(this);
130            if (mIsSplit && mSplitBackground != null) {
131                mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
132            }
133        }
134        setWillNotDraw(mIsSplit ? mSplitBackground == null :
135                mBackground == null && mStackedBackground == null);
136        invalidate();
137    }
138
139    @Override
140    public void setVisibility(int visibility) {
141        super.setVisibility(visibility);
142        final boolean isVisible = visibility == VISIBLE;
143        if (mBackground != null) mBackground.setVisible(isVisible, false);
144        if (mStackedBackground != null) mStackedBackground.setVisible(isVisible, false);
145        if (mSplitBackground != null) mSplitBackground.setVisible(isVisible, false);
146    }
147
148    @Override
149    protected boolean verifyDrawable(Drawable who) {
150        return (who == mBackground && !mIsSplit) || (who == mStackedBackground && mIsStacked) ||
151                (who == mSplitBackground && mIsSplit) || super.verifyDrawable(who);
152    }
153
154    @Override
155    protected void drawableStateChanged() {
156        super.drawableStateChanged();
157        if (mBackground != null && mBackground.isStateful()) {
158            mBackground.setState(getDrawableState());
159        }
160        if (mStackedBackground != null && mStackedBackground.isStateful()) {
161            mStackedBackground.setState(getDrawableState());
162        }
163        if (mSplitBackground != null && mSplitBackground.isStateful()) {
164            mSplitBackground.setState(getDrawableState());
165        }
166    }
167
168    @Override
169    public void jumpDrawablesToCurrentState() {
170        super.jumpDrawablesToCurrentState();
171        if (mBackground != null) {
172            mBackground.jumpToCurrentState();
173        }
174        if (mStackedBackground != null) {
175            mStackedBackground.jumpToCurrentState();
176        }
177        if (mSplitBackground != null) {
178            mSplitBackground.jumpToCurrentState();
179        }
180    }
181
182    /**
183     * @hide
184     */
185    @Override
186    public void onResolveDrawables(int layoutDirection) {
187        super.onResolveDrawables(layoutDirection);
188        if (mBackground != null) {
189            mBackground.setLayoutDirection(layoutDirection);
190        }
191        if (mStackedBackground != null) {
192            mStackedBackground.setLayoutDirection(layoutDirection);
193        }
194        if (mSplitBackground != null) {
195            mSplitBackground.setLayoutDirection(layoutDirection);
196        }
197    }
198
199    /**
200     * Set the action bar into a "transitioning" state. While transitioning
201     * the bar will block focus and touch from all of its descendants. This
202     * prevents the user from interacting with the bar while it is animating
203     * in or out.
204     *
205     * @param isTransitioning true if the bar is currently transitioning, false otherwise.
206     */
207    public void setTransitioning(boolean isTransitioning) {
208        mIsTransitioning = isTransitioning;
209        setDescendantFocusability(isTransitioning ? FOCUS_BLOCK_DESCENDANTS
210                : FOCUS_AFTER_DESCENDANTS);
211    }
212
213    @Override
214    public boolean onInterceptTouchEvent(MotionEvent ev) {
215        return mIsTransitioning || super.onInterceptTouchEvent(ev);
216    }
217
218    @Override
219    public boolean onTouchEvent(MotionEvent ev) {
220        super.onTouchEvent(ev);
221
222        // An action bar always eats touch events.
223        return true;
224    }
225
226    @Override
227    public boolean onHoverEvent(MotionEvent ev) {
228        super.onHoverEvent(ev);
229
230        // An action bar always eats hover events.
231        return true;
232    }
233
234    public void setTabContainer(ScrollingTabContainerView tabView) {
235        if (mTabContainer != null) {
236            removeView(mTabContainer);
237        }
238        mTabContainer = tabView;
239        if (tabView != null) {
240            addView(tabView);
241            final ViewGroup.LayoutParams lp = tabView.getLayoutParams();
242            lp.width = LayoutParams.MATCH_PARENT;
243            lp.height = LayoutParams.WRAP_CONTENT;
244            tabView.setAllowCollapse(false);
245        }
246    }
247
248    public View getTabContainer() {
249        return mTabContainer;
250    }
251
252    @Override
253    public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
254        // No starting an action mode for an action bar child! (Where would it go?)
255        return null;
256    }
257
258    private static boolean isCollapsed(View view) {
259        return view == null || view.getVisibility() == GONE || view.getMeasuredHeight() == 0;
260    }
261
262    @Override
263    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
264        if (mActionBarView == null &&
265                MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST && mHeight >= 0) {
266            heightMeasureSpec = MeasureSpec.makeMeasureSpec(
267                    Math.min(mHeight, MeasureSpec.getSize(heightMeasureSpec)), MeasureSpec.AT_MOST);
268        }
269        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
270
271        if (mActionBarView == null) return;
272
273        int nonTabMaxHeight = 0;
274        final int childCount = getChildCount();
275        for (int i = 0; i < childCount; i++) {
276            final View child = getChildAt(i);
277            if (child == mTabContainer) {
278                continue;
279            }
280            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
281            nonTabMaxHeight = Math.max(nonTabMaxHeight, isCollapsed(child) ? 0 :
282                    child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
283        }
284
285        if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
286            final int mode = MeasureSpec.getMode(heightMeasureSpec);
287            if (mode == MeasureSpec.AT_MOST) {
288                final int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
289                setMeasuredDimension(getMeasuredWidth(),
290                        Math.min(nonTabMaxHeight + mTabContainer.getMeasuredHeight(),
291                                maxHeight));
292            }
293        }
294    }
295
296    @Override
297    public void onLayout(boolean changed, int l, int t, int r, int b) {
298        super.onLayout(changed, l, t, r, b);
299
300        final View tabContainer = mTabContainer;
301        final boolean hasTabs = tabContainer != null && tabContainer.getVisibility() != GONE;
302
303        if (tabContainer != null && tabContainer.getVisibility() != GONE) {
304            final int containerHeight = getMeasuredHeight();
305            final int tabHeight = tabContainer.getMeasuredHeight();
306            tabContainer.layout(l, containerHeight - tabHeight, r, containerHeight);
307        }
308
309        boolean needsInvalidate = false;
310        if (mIsSplit) {
311            if (mSplitBackground != null) {
312                mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
313                needsInvalidate = true;
314            }
315        } else {
316            if (mBackground != null) {
317                if (mActionBarView.getVisibility() == View.VISIBLE) {
318                    mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(),
319                            mActionBarView.getRight(), mActionBarView.getBottom());
320                } else if (mActionContextView != null &&
321                        mActionContextView.getVisibility() == View.VISIBLE) {
322                    mBackground.setBounds(mActionContextView.getLeft(), mActionContextView.getTop(),
323                            mActionContextView.getRight(), mActionContextView.getBottom());
324                } else {
325                    mBackground.setBounds(0, 0, 0, 0);
326                }
327                needsInvalidate = true;
328            }
329            mIsStacked = hasTabs;
330            if (hasTabs && mStackedBackground != null) {
331                mStackedBackground.setBounds(tabContainer.getLeft(), tabContainer.getTop(),
332                        tabContainer.getRight(), tabContainer.getBottom());
333                needsInvalidate = true;
334            }
335        }
336
337        if (needsInvalidate) {
338            invalidate();
339        }
340    }
341
342    /**
343     * Dummy drawable so that we don't break background display lists and
344     * projection surfaces.
345     */
346    private class ActionBarBackgroundDrawable extends Drawable {
347        @Override
348        public void draw(Canvas canvas) {
349            if (mIsSplit) {
350                if (mSplitBackground != null) {
351                    mSplitBackground.draw(canvas);
352                }
353            } else {
354                if (mBackground != null) {
355                    mBackground.draw(canvas);
356                }
357                if (mStackedBackground != null && mIsStacked) {
358                    mStackedBackground.draw(canvas);
359                }
360            }
361        }
362
363        @Override
364        public void getOutline(@NonNull Outline outline) {
365            if (mIsSplit) {
366                if (mSplitBackground != null) {
367                    mSplitBackground.getOutline(outline);
368                }
369            } else {
370                // ignore the stacked background for shadow casting
371                if (mBackground != null) {
372                    mBackground.getOutline(outline);
373                }
374            }
375        }
376
377        @Override
378        public void setAlpha(int alpha) {
379        }
380
381        @Override
382        public void setColorFilter(ColorFilter cf) {
383        }
384
385        @Override
386        public int getOpacity() {
387            return 0;
388        }
389    }
390}
391