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