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