ActionBarContainer.java revision 1e610d479f091f1b09f2a7e5201711bf4d311f6f
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        final LayoutParams lp = (LayoutParams) mActionBarView.getLayoutParams();
272        final int actionBarViewHeight = isCollapsed(mActionBarView) ? 0 :
273                mActionBarView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
274
275        if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
276            final int mode = MeasureSpec.getMode(heightMeasureSpec);
277            if (mode == MeasureSpec.AT_MOST) {
278                final int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
279                setMeasuredDimension(getMeasuredWidth(),
280                        Math.min(actionBarViewHeight + mTabContainer.getMeasuredHeight(),
281                                maxHeight));
282            }
283        }
284    }
285
286    @Override
287    public void onLayout(boolean changed, int l, int t, int r, int b) {
288        super.onLayout(changed, l, t, r, b);
289
290        final View tabContainer = mTabContainer;
291        final boolean hasTabs = tabContainer != null && tabContainer.getVisibility() != GONE;
292
293        if (tabContainer != null && tabContainer.getVisibility() != GONE) {
294            final int containerHeight = getMeasuredHeight();
295            final int tabHeight = tabContainer.getMeasuredHeight();
296            tabContainer.layout(l, containerHeight - tabHeight, r, containerHeight);
297        }
298
299        boolean needsInvalidate = false;
300        if (mIsSplit) {
301            if (mSplitBackground != null) {
302                mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
303                needsInvalidate = true;
304            }
305        } else {
306            if (mBackground != null) {
307                mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(),
308                        mActionBarView.getRight(), mActionBarView.getBottom());
309                needsInvalidate = true;
310            }
311            mIsStacked = hasTabs;
312            if (hasTabs && mStackedBackground != null) {
313                mStackedBackground.setBounds(tabContainer.getLeft(), tabContainer.getTop(),
314                        tabContainer.getRight(), tabContainer.getBottom());
315                needsInvalidate = true;
316            }
317        }
318
319        if (needsInvalidate) {
320            invalidate();
321        }
322    }
323
324    /**
325     * Dummy drawable so that we don't break background display lists and
326     * projection surfaces.
327     */
328    private class ActionBarBackgroundDrawable extends Drawable {
329        @Override
330        public void draw(Canvas canvas) {
331            if (mIsSplit) {
332                if (mSplitBackground != null) {
333                    mSplitBackground.draw(canvas);
334                }
335            } else {
336                if (mBackground != null) {
337                    mBackground.draw(canvas);
338                }
339                if (mStackedBackground != null && mIsStacked) {
340                    mStackedBackground.draw(canvas);
341                }
342            }
343        }
344
345        @Override
346        public void getOutline(@NonNull Outline outline) {
347            if (mIsSplit) {
348                if (mSplitBackground != null) {
349                    mSplitBackground.getOutline(outline);
350                }
351            } else {
352                // ignore the stacked background for shadow casting
353                if (mBackground != null) {
354                    mBackground.getOutline(outline);
355                }
356            }
357        }
358
359        @Override
360        public void setAlpha(int alpha) {
361        }
362
363        @Override
364        public void setColorFilter(ColorFilter cf) {
365        }
366
367        @Override
368        public int getOpacity() {
369            return 0;
370        }
371    }
372}
373