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