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