• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.support.v7.widget;
18 
19 import android.content.Context;
20 import android.content.res.TypedArray;
21 import android.graphics.drawable.Drawable;
22 import android.os.Build;
23 import android.support.v7.appcompat.R;
24 import android.support.v7.view.ActionMode;
25 import android.util.AttributeSet;
26 import android.view.MotionEvent;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.widget.FrameLayout;
30 
31 /**
32  * This class acts as a container for the action bar view and action mode context views.
33  * It applies special styles as needed to help handle animated transitions between them.
34  * @hide
35  */
36 public class ActionBarContainer extends FrameLayout {
37     private boolean mIsTransitioning;
38     private View mTabContainer;
39     private View mActionBarView;
40     private View mContextView;
41 
42     Drawable mBackground;
43     Drawable mStackedBackground;
44     Drawable mSplitBackground;
45     boolean mIsSplit;
46     boolean mIsStacked;
47     private int mHeight;
48 
ActionBarContainer(Context context)49     public ActionBarContainer(Context context) {
50         this(context, null);
51     }
52 
ActionBarContainer(Context context, AttributeSet attrs)53     public ActionBarContainer(Context context, AttributeSet attrs) {
54         super(context, attrs);
55 
56         // Set a transparent background so that we project appropriately.
57         final Drawable bg = Build.VERSION.SDK_INT >= 21
58                 ? new ActionBarBackgroundDrawableV21(this)
59                 : new ActionBarBackgroundDrawable(this);
60         setBackgroundDrawable(bg);
61 
62         TypedArray a = context.obtainStyledAttributes(attrs,
63                 R.styleable.ActionBar);
64         mBackground = a.getDrawable(R.styleable.ActionBar_background);
65         mStackedBackground = a.getDrawable(
66                 R.styleable.ActionBar_backgroundStacked);
67         mHeight = a.getDimensionPixelSize(R.styleable.ActionBar_height, -1);
68 
69         if (getId() == R.id.split_action_bar) {
70             mIsSplit = true;
71             mSplitBackground = a.getDrawable(R.styleable.ActionBar_backgroundSplit);
72         }
73         a.recycle();
74 
75         setWillNotDraw(mIsSplit ? mSplitBackground == null :
76                 mBackground == null && mStackedBackground == null);
77     }
78 
79     @Override
onFinishInflate()80     public void onFinishInflate() {
81         super.onFinishInflate();
82         mActionBarView = findViewById(R.id.action_bar);
83         mContextView = findViewById(R.id.action_context_bar);
84     }
85 
setPrimaryBackground(Drawable bg)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 
setStackedBackground(Drawable bg)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 
setSplitBackground(Drawable bg)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
setVisibility(int visibility)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
verifyDrawable(Drawable who)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
drawableStateChanged()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 
jumpDrawablesToCurrentState()168     public void jumpDrawablesToCurrentState() {
169         if (Build.VERSION.SDK_INT >= 11) {
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     /**
184      * Set the action bar into a "transitioning" state. While transitioning the bar will block focus
185      * and touch from all of its descendants. This prevents the user from interacting with the bar
186      * while it is animating in or out.
187      *
188      * @param isTransitioning true if the bar is currently transitioning, false otherwise.
189      */
setTransitioning(boolean isTransitioning)190     public void setTransitioning(boolean isTransitioning) {
191         mIsTransitioning = isTransitioning;
192         setDescendantFocusability(isTransitioning ? FOCUS_BLOCK_DESCENDANTS
193                 : FOCUS_AFTER_DESCENDANTS);
194     }
195 
196     @Override
onInterceptTouchEvent(MotionEvent ev)197     public boolean onInterceptTouchEvent(MotionEvent ev) {
198         return mIsTransitioning || super.onInterceptTouchEvent(ev);
199     }
200 
201     @Override
onTouchEvent(MotionEvent ev)202     public boolean onTouchEvent(MotionEvent ev) {
203         super.onTouchEvent(ev);
204 
205         // An action bar always eats touch events.
206         return true;
207     }
208 
setTabContainer(ScrollingTabContainerView tabView)209     public void setTabContainer(ScrollingTabContainerView tabView) {
210         if (mTabContainer != null) {
211             removeView(mTabContainer);
212         }
213         mTabContainer = tabView;
214         if (tabView != null) {
215             addView(tabView);
216             final ViewGroup.LayoutParams lp = tabView.getLayoutParams();
217             lp.width = LayoutParams.MATCH_PARENT;
218             lp.height = LayoutParams.WRAP_CONTENT;
219             tabView.setAllowCollapse(false);
220         }
221     }
222 
getTabContainer()223     public View getTabContainer() {
224         return mTabContainer;
225     }
226 
startActionModeForChild(View child, android.view.ActionMode.Callback callback)227     public android.view.ActionMode startActionModeForChild(View child,
228             android.view.ActionMode.Callback callback) {
229         // No starting an action mode for an action bar child! (Where would it go?)
230         return null;
231     }
232 
startActionModeForChild(View child, android.view.ActionMode.Callback callback, int type)233     public android.view.ActionMode startActionModeForChild(View child,
234             android.view.ActionMode.Callback callback, int type) {
235         if (type != android.view.ActionMode.TYPE_PRIMARY) {
236             return super.startActionModeForChild(child, callback, type);
237         }
238         return null;
239     }
240 
isCollapsed(View view)241     private boolean isCollapsed(View view) {
242         return view == null || view.getVisibility() == GONE || view.getMeasuredHeight() == 0;
243     }
244 
getMeasuredHeightWithMargins(View view)245     private int getMeasuredHeightWithMargins(View view) {
246         final LayoutParams lp = (LayoutParams) view.getLayoutParams();
247         return view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
248     }
249 
250     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)251     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
252         if (mActionBarView == null &&
253                 MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST && mHeight >= 0) {
254             heightMeasureSpec = MeasureSpec.makeMeasureSpec(
255                     Math.min(mHeight, MeasureSpec.getSize(heightMeasureSpec)), MeasureSpec.AT_MOST);
256         }
257         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
258 
259         if (mActionBarView == null) return;
260 
261         final int mode = MeasureSpec.getMode(heightMeasureSpec);
262         if (mTabContainer != null && mTabContainer.getVisibility() != GONE
263                 && mode != MeasureSpec.EXACTLY) {
264             final int topMarginForTabs;
265             if (!isCollapsed(mActionBarView)) {
266                 topMarginForTabs = getMeasuredHeightWithMargins(mActionBarView);
267             } else if (!isCollapsed(mContextView)) {
268                 topMarginForTabs = getMeasuredHeightWithMargins(mContextView);
269             } else {
270                 topMarginForTabs = 0;
271             }
272             final int maxHeight = mode == MeasureSpec.AT_MOST ?
273                     MeasureSpec.getSize(heightMeasureSpec) : Integer.MAX_VALUE;
274             setMeasuredDimension(getMeasuredWidth(),
275                     Math.min(topMarginForTabs + getMeasuredHeightWithMargins(mTabContainer),
276                             maxHeight));
277         }
278     }
279 
280     @Override
onLayout(boolean changed, int l, int t, int r, int b)281     public void onLayout(boolean changed, int l, int t, int r, int b) {
282         super.onLayout(changed, l, t, r, b);
283 
284         final View tabContainer = mTabContainer;
285         final boolean hasTabs = tabContainer != null && tabContainer.getVisibility() != GONE;
286 
287         if (tabContainer != null && tabContainer.getVisibility() != GONE) {
288             final int containerHeight = getMeasuredHeight();
289             final LayoutParams lp = (LayoutParams) tabContainer.getLayoutParams();
290             final int tabHeight = tabContainer.getMeasuredHeight();
291             tabContainer.layout(l, containerHeight - tabHeight - lp.bottomMargin, r,
292                     containerHeight - lp.bottomMargin);
293         }
294 
295         boolean needsInvalidate = false;
296         if (mIsSplit) {
297             if (mSplitBackground != null) {
298                 mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
299                 needsInvalidate = true;
300             }
301         } else {
302             if (mBackground != null) {
303                 if (mActionBarView.getVisibility() == View.VISIBLE) {
304                     mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(),
305                             mActionBarView.getRight(), mActionBarView.getBottom());
306                 } else if (mContextView != null &&
307                         mContextView.getVisibility() == View.VISIBLE) {
308                     mBackground.setBounds(mContextView.getLeft(), mContextView.getTop(),
309                             mContextView.getRight(), mContextView.getBottom());
310                 } else {
311                     mBackground.setBounds(0, 0, 0, 0);
312                 }
313                 needsInvalidate = true;
314             }
315             mIsStacked = hasTabs;
316             if (hasTabs && mStackedBackground != null) {
317                 mStackedBackground.setBounds(tabContainer.getLeft(), tabContainer.getTop(),
318                         tabContainer.getRight(), tabContainer.getBottom());
319                 needsInvalidate = true;
320             }
321         }
322 
323         if (needsInvalidate) {
324             invalidate();
325         }
326     }
327 }
328