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