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