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 android.support.v7.internal.widget; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.graphics.Canvas; 22import android.graphics.drawable.Drawable; 23import android.support.v7.app.ActionBar; 24import android.support.v7.appcompat.R; 25import android.support.v7.view.ActionMode; 26import android.util.AttributeSet; 27import android.view.MotionEvent; 28import android.view.View; 29import android.view.ViewGroup; 30import android.widget.FrameLayout; 31 32/** 33 * This class acts as a container for the action bar view and action mode context views. It applies 34 * special styles as needed to help handle animated transitions between them. 35 * 36 * @hide 37 */ 38public class ActionBarContainer extends FrameLayout { 39 40 private boolean mIsTransitioning; 41 private View mTabContainer; 42 private ActionBarView mActionBarView; 43 44 private Drawable mBackground; 45 private Drawable mStackedBackground; 46 private Drawable mSplitBackground; 47 private boolean mIsSplit; 48 private boolean mIsStacked; 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 setBackgroundDrawable(null); 58 59 TypedArray a = context.obtainStyledAttributes(attrs, 60 R.styleable.ActionBar); 61 mBackground = a.getDrawable(R.styleable.ActionBar_background); 62 mStackedBackground = a.getDrawable( 63 R.styleable.ActionBar_backgroundStacked); 64 65 if (getId() == R.id.split_action_bar) { 66 mIsSplit = true; 67 mSplitBackground = a.getDrawable( 68 R.styleable.ActionBar_backgroundSplit); 69 } 70 a.recycle(); 71 72 setWillNotDraw(mIsSplit ? mSplitBackground == null : 73 mBackground == null && mStackedBackground == null); 74 } 75 76 @Override 77 public void onFinishInflate() { 78 super.onFinishInflate(); 79 mActionBarView = (ActionBarView) findViewById(R.id.action_bar); 80 } 81 82 public void setPrimaryBackground(Drawable bg) { 83 if (mBackground != null) { 84 mBackground.setCallback(null); 85 unscheduleDrawable(mBackground); 86 } 87 mBackground = bg; 88 if (bg != null) { 89 bg.setCallback(this); 90 } 91 setWillNotDraw(mIsSplit ? mSplitBackground == null : 92 mBackground == null && mStackedBackground == null); 93 invalidate(); 94 } 95 96 public void setStackedBackground(Drawable bg) { 97 if (mStackedBackground != null) { 98 mStackedBackground.setCallback(null); 99 unscheduleDrawable(mStackedBackground); 100 } 101 mStackedBackground = bg; 102 if (bg != null) { 103 bg.setCallback(this); 104 } 105 setWillNotDraw(mIsSplit ? mSplitBackground == null : 106 mBackground == null && mStackedBackground == null); 107 invalidate(); 108 } 109 110 public void setSplitBackground(Drawable bg) { 111 if (mSplitBackground != null) { 112 mSplitBackground.setCallback(null); 113 unscheduleDrawable(mSplitBackground); 114 } 115 mSplitBackground = bg; 116 if (bg != null) { 117 bg.setCallback(this); 118 } 119 setWillNotDraw(mIsSplit ? mSplitBackground == null : 120 mBackground == null && mStackedBackground == null); 121 invalidate(); 122 } 123 124 @Override 125 public void setVisibility(int visibility) { 126 super.setVisibility(visibility); 127 final boolean isVisible = visibility == VISIBLE; 128 if (mBackground != null) mBackground.setVisible(isVisible, false); 129 if (mStackedBackground != null) mStackedBackground.setVisible(isVisible, false); 130 if (mSplitBackground != null) mSplitBackground.setVisible(isVisible, false); 131 } 132 133 @Override 134 protected boolean verifyDrawable(Drawable who) { 135 return (who == mBackground && !mIsSplit) || (who == mStackedBackground && mIsStacked) || 136 (who == mSplitBackground && mIsSplit) || super.verifyDrawable(who); 137 } 138 139 @Override 140 protected void drawableStateChanged() { 141 super.drawableStateChanged(); 142 if (mBackground != null && mBackground.isStateful()) { 143 mBackground.setState(getDrawableState()); 144 } 145 if (mStackedBackground != null && mStackedBackground.isStateful()) { 146 mStackedBackground.setState(getDrawableState()); 147 } 148 if (mSplitBackground != null && mSplitBackground.isStateful()) { 149 mSplitBackground.setState(getDrawableState()); 150 } 151 } 152 153 /** 154 * Set the action bar into a "transitioning" state. While transitioning the bar will block focus 155 * and touch from all of its descendants. This prevents the user from interacting with the bar 156 * while it is animating in or out. 157 * 158 * @param isTransitioning true if the bar is currently transitioning, false otherwise. 159 */ 160 public void setTransitioning(boolean isTransitioning) { 161 mIsTransitioning = isTransitioning; 162 setDescendantFocusability(isTransitioning ? FOCUS_BLOCK_DESCENDANTS 163 : FOCUS_AFTER_DESCENDANTS); 164 } 165 166 @Override 167 public boolean onInterceptTouchEvent(MotionEvent ev) { 168 return mIsTransitioning || super.onInterceptTouchEvent(ev); 169 } 170 171 @Override 172 public boolean onTouchEvent(MotionEvent ev) { 173 super.onTouchEvent(ev); 174 175 // An action bar always eats touch events. 176 return true; 177 } 178 179 //@Override 180 public boolean onHoverEvent(MotionEvent ev) { 181 //super.onHoverEvent(ev); 182 183 // An action bar always eats hover events. 184 return true; 185 } 186 187 public void setTabContainer(ScrollingTabContainerView tabView) { 188 if (mTabContainer != null) { 189 removeView(mTabContainer); 190 } 191 mTabContainer = tabView; 192 if (tabView != null) { 193 addView(tabView); 194 final ViewGroup.LayoutParams lp = tabView.getLayoutParams(); 195 lp.width = LayoutParams.FILL_PARENT; 196 lp.height = LayoutParams.WRAP_CONTENT; 197 tabView.setAllowCollapse(false); 198 } 199 } 200 201 public View getTabContainer() { 202 return mTabContainer; 203 } 204 205 @Override 206 public void onDraw(Canvas canvas) { 207 if (getWidth() == 0 || getHeight() == 0) { 208 return; 209 } 210 211 if (mIsSplit) { 212 if (mSplitBackground != null) { 213 mSplitBackground.draw(canvas); 214 } 215 } else { 216 if (mBackground != null) { 217 mBackground.draw(canvas); 218 } 219 if (mStackedBackground != null && mIsStacked) { 220 mStackedBackground.draw(canvas); 221 } 222 } 223 } 224 225 //@Override 226 public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) { 227 // No starting an action mode for an action bar child! (Where would it go?) 228 return null; 229 } 230 231 @Override 232 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 233 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 234 235 if (mActionBarView == null) { 236 return; 237 } 238 239 final LayoutParams lp = (LayoutParams) mActionBarView.getLayoutParams(); 240 final int actionBarViewHeight = mActionBarView.isCollapsed() ? 0 : 241 mActionBarView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; 242 243 if (mTabContainer != null && mTabContainer.getVisibility() != GONE) { 244 final int mode = MeasureSpec.getMode(heightMeasureSpec); 245 if (mode == MeasureSpec.AT_MOST) { 246 final int maxHeight = MeasureSpec.getSize(heightMeasureSpec); 247 setMeasuredDimension(getMeasuredWidth(), 248 Math.min(actionBarViewHeight + mTabContainer.getMeasuredHeight(), 249 maxHeight)); 250 } 251 } 252 } 253 254 @Override 255 public void onLayout(boolean changed, int l, int t, int r, int b) { 256 super.onLayout(changed, l, t, r, b); 257 258 final boolean hasTabs = mTabContainer != null && mTabContainer.getVisibility() != GONE; 259 260 if (mTabContainer != null && mTabContainer.getVisibility() != GONE) { 261 final int containerHeight = getMeasuredHeight(); 262 final int tabHeight = mTabContainer.getMeasuredHeight(); 263 264 if ((mActionBarView.getDisplayOptions() & ActionBar.DISPLAY_SHOW_HOME) == 0) { 265 // Not showing home, put tabs on top. 266 final int count = getChildCount(); 267 for (int i = 0; i < count; i++) { 268 final View child = getChildAt(i); 269 270 if (child == mTabContainer) { 271 continue; 272 } 273 274 if (!mActionBarView.isCollapsed()) { 275 child.offsetTopAndBottom(tabHeight); 276 } 277 } 278 mTabContainer.layout(l, 0, r, tabHeight); 279 } else { 280 mTabContainer.layout(l, containerHeight - tabHeight, r, containerHeight); 281 } 282 } 283 284 boolean needsInvalidate = false; 285 if (mIsSplit) { 286 if (mSplitBackground != null) { 287 mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); 288 needsInvalidate = true; 289 } 290 } else { 291 if (mBackground != null) { 292 mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(), 293 mActionBarView.getRight(), mActionBarView.getBottom()); 294 needsInvalidate = true; 295 } 296 if ((mIsStacked = hasTabs && mStackedBackground != null)) { 297 mStackedBackground.setBounds(mTabContainer.getLeft(), mTabContainer.getTop(), 298 mTabContainer.getRight(), mTabContainer.getBottom()); 299 needsInvalidate = true; 300 } 301 } 302 303 if (needsInvalidate) { 304 invalidate(); 305 } 306 } 307} 308