ActionBarContainer.java revision 41fceb462b6eefbaa3a4d4e56b962fdb2910a6f5
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.RectF; 27import android.graphics.drawable.Drawable; 28import android.util.AttributeSet; 29import android.view.ActionMode; 30import android.view.MotionEvent; 31import android.view.View; 32import android.view.ViewGroup; 33import android.widget.FrameLayout; 34 35import java.util.List; 36 37/** 38 * This class acts as a container for the action bar view and action mode context views. 39 * It applies special styles as needed to help handle animated transitions between them. 40 * @hide 41 */ 42public class ActionBarContainer extends FrameLayout { 43 private boolean mIsTransitioning; 44 private View mTabContainer; 45 private View mActionBarView; 46 private View mActionContextView; 47 48 private Drawable mBackground; 49 private Drawable mStackedBackground; 50 private Drawable mSplitBackground; 51 private boolean mIsSplit; 52 private boolean mIsStacked; 53 private int mHeight; 54 55 public ActionBarContainer(Context context) { 56 this(context, null); 57 } 58 59 public ActionBarContainer(Context context, AttributeSet attrs) { 60 super(context, attrs); 61 62 // Set a transparent background so that we project appropriately. 63 setBackground(new ActionBarBackgroundDrawable()); 64 65 TypedArray a = context.obtainStyledAttributes(attrs, 66 com.android.internal.R.styleable.ActionBar); 67 mBackground = a.getDrawable(com.android.internal.R.styleable.ActionBar_background); 68 mStackedBackground = a.getDrawable( 69 com.android.internal.R.styleable.ActionBar_backgroundStacked); 70 mHeight = a.getDimensionPixelSize(com.android.internal.R.styleable.ActionBar_height, -1); 71 72 if (getId() == com.android.internal.R.id.split_action_bar) { 73 mIsSplit = true; 74 mSplitBackground = a.getDrawable( 75 com.android.internal.R.styleable.ActionBar_backgroundSplit); 76 } 77 a.recycle(); 78 79 setWillNotDraw(mIsSplit ? mSplitBackground == null : 80 mBackground == null && mStackedBackground == null); 81 } 82 83 @Override 84 public void onFinishInflate() { 85 super.onFinishInflate(); 86 mActionBarView = findViewById(com.android.internal.R.id.action_bar); 87 mActionContextView = findViewById(com.android.internal.R.id.action_context_bar); 88 } 89 90 public void setPrimaryBackground(Drawable bg) { 91 if (mBackground != null) { 92 mBackground.setCallback(null); 93 unscheduleDrawable(mBackground); 94 } 95 mBackground = bg; 96 if (bg != null) { 97 bg.setCallback(this); 98 if (mActionBarView != null) { 99 mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(), 100 mActionBarView.getRight(), mActionBarView.getBottom()); 101 } 102 } 103 setWillNotDraw(mIsSplit ? mSplitBackground == null : 104 mBackground == null && mStackedBackground == null); 105 invalidate(); 106 } 107 108 public void setStackedBackground(Drawable bg) { 109 if (mStackedBackground != null) { 110 mStackedBackground.setCallback(null); 111 unscheduleDrawable(mStackedBackground); 112 } 113 mStackedBackground = bg; 114 if (bg != null) { 115 bg.setCallback(this); 116 if ((mIsStacked && mStackedBackground != null)) { 117 mStackedBackground.setBounds(mTabContainer.getLeft(), mTabContainer.getTop(), 118 mTabContainer.getRight(), mTabContainer.getBottom()); 119 } 120 } 121 setWillNotDraw(mIsSplit ? mSplitBackground == null : 122 mBackground == null && mStackedBackground == null); 123 invalidate(); 124 } 125 126 public void setSplitBackground(Drawable bg) { 127 if (mSplitBackground != null) { 128 mSplitBackground.setCallback(null); 129 unscheduleDrawable(mSplitBackground); 130 } 131 mSplitBackground = bg; 132 if (bg != null) { 133 bg.setCallback(this); 134 if (mIsSplit && mSplitBackground != null) { 135 mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); 136 } 137 } 138 setWillNotDraw(mIsSplit ? mSplitBackground == null : 139 mBackground == null && mStackedBackground == null); 140 invalidate(); 141 } 142 143 @Override 144 public void setVisibility(int visibility) { 145 super.setVisibility(visibility); 146 final boolean isVisible = visibility == VISIBLE; 147 if (mBackground != null) mBackground.setVisible(isVisible, false); 148 if (mStackedBackground != null) mStackedBackground.setVisible(isVisible, false); 149 if (mSplitBackground != null) mSplitBackground.setVisible(isVisible, false); 150 } 151 152 @Override 153 protected boolean verifyDrawable(Drawable who) { 154 return (who == mBackground && !mIsSplit) || (who == mStackedBackground && mIsStacked) || 155 (who == mSplitBackground && mIsSplit) || super.verifyDrawable(who); 156 } 157 158 @Override 159 protected void drawableStateChanged() { 160 super.drawableStateChanged(); 161 if (mBackground != null && mBackground.isStateful()) { 162 mBackground.setState(getDrawableState()); 163 } 164 if (mStackedBackground != null && mStackedBackground.isStateful()) { 165 mStackedBackground.setState(getDrawableState()); 166 } 167 if (mSplitBackground != null && mSplitBackground.isStateful()) { 168 mSplitBackground.setState(getDrawableState()); 169 } 170 } 171 172 @Override 173 public void jumpDrawablesToCurrentState() { 174 super.jumpDrawablesToCurrentState(); 175 if (mBackground != null) { 176 mBackground.jumpToCurrentState(); 177 } 178 if (mStackedBackground != null) { 179 mStackedBackground.jumpToCurrentState(); 180 } 181 if (mSplitBackground != null) { 182 mSplitBackground.jumpToCurrentState(); 183 } 184 } 185 186 /** 187 * @hide 188 */ 189 @Override 190 public void onResolveDrawables(int layoutDirection) { 191 super.onResolveDrawables(layoutDirection); 192 if (mBackground != null) { 193 mBackground.setLayoutDirection(layoutDirection); 194 } 195 if (mStackedBackground != null) { 196 mStackedBackground.setLayoutDirection(layoutDirection); 197 } 198 if (mSplitBackground != null) { 199 mSplitBackground.setLayoutDirection(layoutDirection); 200 } 201 } 202 203 /** 204 * Set the action bar into a "transitioning" state. While transitioning 205 * the bar will block focus and touch from all of its descendants. This 206 * prevents the user from interacting with the bar while it is animating 207 * in or out. 208 * 209 * @param isTransitioning true if the bar is currently transitioning, false otherwise. 210 */ 211 public void setTransitioning(boolean isTransitioning) { 212 mIsTransitioning = isTransitioning; 213 setDescendantFocusability(isTransitioning ? FOCUS_BLOCK_DESCENDANTS 214 : FOCUS_AFTER_DESCENDANTS); 215 } 216 217 @Override 218 public boolean onInterceptTouchEvent(MotionEvent ev) { 219 return mIsTransitioning || super.onInterceptTouchEvent(ev); 220 } 221 222 @Override 223 public boolean onTouchEvent(MotionEvent ev) { 224 super.onTouchEvent(ev); 225 226 // An action bar always eats touch events. 227 return true; 228 } 229 230 /** 231 * @hide 232 */ 233 @Override 234 public void addClickableRectsForAccessibility(List<RectF> outRects) { 235 // This class always consumes touch events, therefore if it 236 // covers a view we do not want to send a click over it. 237 RectF bounds = new RectF(); 238 bounds.set(0, 0, getWidth(), getHeight()); 239 outRects.add(bounds); 240 } 241 242 @Override 243 public boolean onHoverEvent(MotionEvent ev) { 244 super.onHoverEvent(ev); 245 246 // An action bar always eats hover events. 247 return true; 248 } 249 250 public void setTabContainer(ScrollingTabContainerView tabView) { 251 if (mTabContainer != null) { 252 removeView(mTabContainer); 253 } 254 mTabContainer = tabView; 255 if (tabView != null) { 256 addView(tabView); 257 final ViewGroup.LayoutParams lp = tabView.getLayoutParams(); 258 lp.width = LayoutParams.MATCH_PARENT; 259 lp.height = LayoutParams.WRAP_CONTENT; 260 tabView.setAllowCollapse(false); 261 } 262 } 263 264 public View getTabContainer() { 265 return mTabContainer; 266 } 267 268 @Override 269 public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) { 270 // No starting an action mode for an action bar child! (Where would it go?) 271 return null; 272 } 273 274 private static boolean isCollapsed(View view) { 275 return view == null || view.getVisibility() == GONE || view.getMeasuredHeight() == 0; 276 } 277 278 private int getMeasuredHeightWithMargins(View view) { 279 final LayoutParams lp = (LayoutParams) view.getLayoutParams(); 280 return view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; 281 } 282 283 @Override 284 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 285 if (mActionBarView == null && 286 MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST && mHeight >= 0) { 287 heightMeasureSpec = MeasureSpec.makeMeasureSpec( 288 Math.min(mHeight, MeasureSpec.getSize(heightMeasureSpec)), MeasureSpec.AT_MOST); 289 } 290 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 291 292 if (mActionBarView == null) return; 293 294 if (mTabContainer != null && mTabContainer.getVisibility() != GONE) { 295 int nonTabMaxHeight = 0; 296 final int childCount = getChildCount(); 297 for (int i = 0; i < childCount; i++) { 298 final View child = getChildAt(i); 299 if (child == mTabContainer) { 300 continue; 301 } 302 nonTabMaxHeight = Math.max(nonTabMaxHeight, isCollapsed(child) ? 0 : 303 getMeasuredHeightWithMargins(child)); 304 } 305 final int mode = MeasureSpec.getMode(heightMeasureSpec); 306 final int maxHeight = mode == MeasureSpec.AT_MOST ? 307 MeasureSpec.getSize(heightMeasureSpec) : Integer.MAX_VALUE; 308 setMeasuredDimension(getMeasuredWidth(), 309 Math.min(nonTabMaxHeight + getMeasuredHeightWithMargins(mTabContainer), 310 maxHeight)); 311 } 312 } 313 314 @Override 315 public void onLayout(boolean changed, int l, int t, int r, int b) { 316 super.onLayout(changed, l, t, r, b); 317 318 final View tabContainer = mTabContainer; 319 final boolean hasTabs = tabContainer != null && tabContainer.getVisibility() != GONE; 320 321 if (tabContainer != null && tabContainer.getVisibility() != GONE) { 322 final int containerHeight = getMeasuredHeight(); 323 final LayoutParams lp = (LayoutParams) tabContainer.getLayoutParams(); 324 final int tabHeight = tabContainer.getMeasuredHeight(); 325 tabContainer.layout(l, containerHeight - tabHeight - lp.bottomMargin, r, 326 containerHeight - lp.bottomMargin); 327 } 328 329 boolean needsInvalidate = false; 330 if (mIsSplit) { 331 if (mSplitBackground != null) { 332 mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); 333 needsInvalidate = true; 334 } 335 } else { 336 if (mBackground != null) { 337 if (mActionBarView.getVisibility() == View.VISIBLE) { 338 mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(), 339 mActionBarView.getRight(), mActionBarView.getBottom()); 340 } else if (mActionContextView != null && 341 mActionContextView.getVisibility() == View.VISIBLE) { 342 mBackground.setBounds(mActionContextView.getLeft(), mActionContextView.getTop(), 343 mActionContextView.getRight(), mActionContextView.getBottom()); 344 } else { 345 mBackground.setBounds(0, 0, 0, 0); 346 } 347 needsInvalidate = true; 348 } 349 mIsStacked = hasTabs; 350 if (hasTabs && mStackedBackground != null) { 351 mStackedBackground.setBounds(tabContainer.getLeft(), tabContainer.getTop(), 352 tabContainer.getRight(), tabContainer.getBottom()); 353 needsInvalidate = true; 354 } 355 } 356 357 if (needsInvalidate) { 358 invalidate(); 359 } 360 } 361 362 /** 363 * Dummy drawable so that we don't break background display lists and 364 * projection surfaces. 365 */ 366 private class ActionBarBackgroundDrawable extends Drawable { 367 @Override 368 public void draw(Canvas canvas) { 369 if (mIsSplit) { 370 if (mSplitBackground != null) { 371 mSplitBackground.draw(canvas); 372 } 373 } else { 374 if (mBackground != null) { 375 mBackground.draw(canvas); 376 } 377 if (mStackedBackground != null && mIsStacked) { 378 mStackedBackground.draw(canvas); 379 } 380 } 381 } 382 383 @Override 384 public void getOutline(@NonNull Outline outline) { 385 if (mIsSplit) { 386 if (mSplitBackground != null) { 387 mSplitBackground.getOutline(outline); 388 } 389 } else { 390 // ignore the stacked background for shadow casting 391 if (mBackground != null) { 392 mBackground.getOutline(outline); 393 } 394 } 395 } 396 397 @Override 398 public void setAlpha(int alpha) { 399 } 400 401 @Override 402 public void setColorFilter(ColorFilter cf) { 403 } 404 405 @Override 406 public int getOpacity() { 407 if (mIsSplit) { 408 if (mSplitBackground != null 409 && mSplitBackground.getOpacity() == PixelFormat.OPAQUE) { 410 return PixelFormat.OPAQUE; 411 } 412 } else { 413 if (mIsStacked && (mStackedBackground == null 414 || mStackedBackground.getOpacity() != PixelFormat.OPAQUE)) { 415 return PixelFormat.UNKNOWN; 416 } 417 if (!isCollapsed(mActionBarView) && mBackground != null 418 && mBackground.getOpacity() == PixelFormat.OPAQUE) { 419 return PixelFormat.OPAQUE; 420 } 421 } 422 423 return PixelFormat.UNKNOWN; 424 } 425 } 426} 427