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