ActionBarContainer.java revision 9dbe94d8cdd91c948160ca08c9028df455826c08
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.PixelFormat; 26import android.graphics.drawable.Drawable; 27import android.util.AttributeSet; 28import android.view.ActionMode; 29import android.view.MotionEvent; 30import android.view.View; 31import android.view.ViewGroup; 32import android.widget.FrameLayout; 33 34/** 35 * This class acts as a container for the action bar view and action mode context views. 36 * It applies special styles as needed to help handle animated transitions between them. 37 * @hide 38 */ 39public class ActionBarContainer extends FrameLayout { 40 private boolean mIsTransitioning; 41 private View mTabContainer; 42 private View mActionBarView; 43 private View mActionContextView; 44 45 private Drawable mBackground; 46 private Drawable mStackedBackground; 47 private Drawable mSplitBackground; 48 private boolean mIsSplit; 49 private boolean mIsStacked; 50 private int mHeight; 51 52 public ActionBarContainer(Context context) { 53 this(context, null); 54 } 55 56 public ActionBarContainer(Context context, AttributeSet attrs) { 57 super(context, attrs); 58 59 // Set a transparent background so that we project appropriately. 60 setBackground(new ActionBarBackgroundDrawable()); 61 62 TypedArray a = context.obtainStyledAttributes(attrs, 63 com.android.internal.R.styleable.ActionBar); 64 mBackground = a.getDrawable(com.android.internal.R.styleable.ActionBar_background); 65 mStackedBackground = a.getDrawable( 66 com.android.internal.R.styleable.ActionBar_backgroundStacked); 67 mHeight = a.getDimensionPixelSize(com.android.internal.R.styleable.ActionBar_height, -1); 68 69 if (getId() == com.android.internal.R.id.split_action_bar) { 70 mIsSplit = true; 71 mSplitBackground = a.getDrawable( 72 com.android.internal.R.styleable.ActionBar_backgroundSplit); 73 } 74 a.recycle(); 75 76 setWillNotDraw(mIsSplit ? mSplitBackground == null : 77 mBackground == null && mStackedBackground == null); 78 } 79 80 @Override 81 public void onFinishInflate() { 82 super.onFinishInflate(); 83 mActionBarView = findViewById(com.android.internal.R.id.action_bar); 84 mActionContextView = findViewById(com.android.internal.R.id.action_context_bar); 85 } 86 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 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 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 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 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 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 169 @Override 170 public void jumpDrawablesToCurrentState() { 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 * @hide 185 */ 186 @Override 187 public void onResolveDrawables(int layoutDirection) { 188 super.onResolveDrawables(layoutDirection); 189 if (mBackground != null) { 190 mBackground.setLayoutDirection(layoutDirection); 191 } 192 if (mStackedBackground != null) { 193 mStackedBackground.setLayoutDirection(layoutDirection); 194 } 195 if (mSplitBackground != null) { 196 mSplitBackground.setLayoutDirection(layoutDirection); 197 } 198 } 199 200 /** 201 * Set the action bar into a "transitioning" state. While transitioning 202 * the bar will block focus and touch from all of its descendants. This 203 * prevents the user from interacting with the bar while it is animating 204 * in or out. 205 * 206 * @param isTransitioning true if the bar is currently transitioning, false otherwise. 207 */ 208 public void setTransitioning(boolean isTransitioning) { 209 mIsTransitioning = isTransitioning; 210 setDescendantFocusability(isTransitioning ? FOCUS_BLOCK_DESCENDANTS 211 : FOCUS_AFTER_DESCENDANTS); 212 } 213 214 @Override 215 public boolean onInterceptTouchEvent(MotionEvent ev) { 216 return mIsTransitioning || super.onInterceptTouchEvent(ev); 217 } 218 219 @Override 220 public boolean onTouchEvent(MotionEvent ev) { 221 super.onTouchEvent(ev); 222 223 // An action bar always eats touch events. 224 return true; 225 } 226 227 @Override 228 public boolean onHoverEvent(MotionEvent ev) { 229 super.onHoverEvent(ev); 230 231 // An action bar always eats hover events. 232 return true; 233 } 234 235 public void setTabContainer(ScrollingTabContainerView tabView) { 236 if (mTabContainer != null) { 237 removeView(mTabContainer); 238 } 239 mTabContainer = tabView; 240 if (tabView != null) { 241 addView(tabView); 242 final ViewGroup.LayoutParams lp = tabView.getLayoutParams(); 243 lp.width = LayoutParams.MATCH_PARENT; 244 lp.height = LayoutParams.WRAP_CONTENT; 245 tabView.setAllowCollapse(false); 246 } 247 } 248 249 public View getTabContainer() { 250 return mTabContainer; 251 } 252 253 @Override 254 public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) { 255 // No starting an action mode for an action bar child! (Where would it go?) 256 return null; 257 } 258 259 private static boolean isCollapsed(View view) { 260 return view == null || view.getVisibility() == GONE || view.getMeasuredHeight() == 0; 261 } 262 263 @Override 264 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 265 if (mActionBarView == null && 266 MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST && mHeight >= 0) { 267 heightMeasureSpec = MeasureSpec.makeMeasureSpec( 268 Math.min(mHeight, MeasureSpec.getSize(heightMeasureSpec)), MeasureSpec.AT_MOST); 269 } 270 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 271 272 if (mActionBarView == null) return; 273 274 int nonTabMaxHeight = 0; 275 final int childCount = getChildCount(); 276 for (int i = 0; i < childCount; i++) { 277 final View child = getChildAt(i); 278 if (child == mTabContainer) { 279 continue; 280 } 281 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 282 nonTabMaxHeight = Math.max(nonTabMaxHeight, isCollapsed(child) ? 0 : 283 child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); 284 } 285 286 if (mTabContainer != null && mTabContainer.getVisibility() != GONE) { 287 final int mode = MeasureSpec.getMode(heightMeasureSpec); 288 if (mode == MeasureSpec.AT_MOST) { 289 final int maxHeight = MeasureSpec.getSize(heightMeasureSpec); 290 setMeasuredDimension(getMeasuredWidth(), 291 Math.min(nonTabMaxHeight + mTabContainer.getMeasuredHeight(), 292 maxHeight)); 293 } 294 } 295 } 296 297 @Override 298 public void onLayout(boolean changed, int l, int t, int r, int b) { 299 super.onLayout(changed, l, t, r, b); 300 301 final View tabContainer = mTabContainer; 302 final boolean hasTabs = tabContainer != null && tabContainer.getVisibility() != GONE; 303 304 if (tabContainer != null && tabContainer.getVisibility() != GONE) { 305 final int containerHeight = getMeasuredHeight(); 306 final int tabHeight = tabContainer.getMeasuredHeight(); 307 tabContainer.layout(l, containerHeight - tabHeight, r, containerHeight); 308 } 309 310 boolean needsInvalidate = false; 311 if (mIsSplit) { 312 if (mSplitBackground != null) { 313 mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); 314 needsInvalidate = true; 315 } 316 } else { 317 if (mBackground != null) { 318 if (mActionBarView.getVisibility() == View.VISIBLE) { 319 mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(), 320 mActionBarView.getRight(), mActionBarView.getBottom()); 321 } else if (mActionContextView != null && 322 mActionContextView.getVisibility() == View.VISIBLE) { 323 mBackground.setBounds(mActionContextView.getLeft(), mActionContextView.getTop(), 324 mActionContextView.getRight(), mActionContextView.getBottom()); 325 } else { 326 mBackground.setBounds(0, 0, 0, 0); 327 } 328 needsInvalidate = true; 329 } 330 mIsStacked = hasTabs; 331 if (hasTabs && mStackedBackground != null) { 332 mStackedBackground.setBounds(tabContainer.getLeft(), tabContainer.getTop(), 333 tabContainer.getRight(), tabContainer.getBottom()); 334 needsInvalidate = true; 335 } 336 } 337 338 if (needsInvalidate) { 339 invalidate(); 340 } 341 } 342 343 /** 344 * Dummy drawable so that we don't break background display lists and 345 * projection surfaces. 346 */ 347 private class ActionBarBackgroundDrawable extends Drawable { 348 @Override 349 public void draw(Canvas canvas) { 350 if (mIsSplit) { 351 if (mSplitBackground != null) { 352 mSplitBackground.draw(canvas); 353 } 354 } else { 355 if (mBackground != null) { 356 mBackground.draw(canvas); 357 } 358 if (mStackedBackground != null && mIsStacked) { 359 mStackedBackground.draw(canvas); 360 } 361 } 362 } 363 364 @Override 365 public void getOutline(@NonNull Outline outline) { 366 if (mIsSplit) { 367 if (mSplitBackground != null) { 368 mSplitBackground.getOutline(outline); 369 } 370 } else { 371 // ignore the stacked background for shadow casting 372 if (mBackground != null) { 373 mBackground.getOutline(outline); 374 } 375 } 376 } 377 378 @Override 379 public void setAlpha(int alpha) { 380 } 381 382 @Override 383 public void setColorFilter(ColorFilter cf) { 384 } 385 386 @Override 387 public int getOpacity() { 388 if (mIsSplit) { 389 if (mSplitBackground != null 390 && mSplitBackground.getOpacity() == PixelFormat.OPAQUE) { 391 return PixelFormat.OPAQUE; 392 } 393 } else { 394 if (mIsStacked && (mStackedBackground == null 395 || mStackedBackground.getOpacity() != PixelFormat.OPAQUE)) { 396 return PixelFormat.UNKNOWN; 397 } 398 if (!isCollapsed(mActionBarView) && mBackground != null 399 && mBackground.getOpacity() == PixelFormat.OPAQUE) { 400 return PixelFormat.OPAQUE; 401 } 402 } 403 404 return PixelFormat.UNKNOWN; 405 } 406 } 407} 408