ActionBarContainer.java revision ac5fe7c617c66850fff75a9fce9979c6e5674b0f
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 androidx.appcompat.widget; 18 19import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; 20 21import android.content.Context; 22import android.content.res.TypedArray; 23import android.graphics.drawable.Drawable; 24import android.os.Build; 25import androidx.annotation.RestrictTo; 26import androidx.core.view.ViewCompat; 27import androidx.appcompat.R; 28import android.util.AttributeSet; 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 */ 39@RestrictTo(LIBRARY_GROUP) 40public class ActionBarContainer extends FrameLayout { 41 private boolean mIsTransitioning; 42 private View mTabContainer; 43 private View mActionBarView; 44 private View mContextView; 45 46 Drawable mBackground; 47 Drawable mStackedBackground; 48 Drawable mSplitBackground; 49 boolean mIsSplit; 50 boolean mIsStacked; 51 private int mHeight; 52 53 public ActionBarContainer(Context context) { 54 this(context, null); 55 } 56 57 public ActionBarContainer(Context context, AttributeSet attrs) { 58 super(context, attrs); 59 60 // Set a transparent background so that we project appropriately. 61 final Drawable bg = Build.VERSION.SDK_INT >= 21 62 ? new ActionBarBackgroundDrawableV21(this) 63 : new ActionBarBackgroundDrawable(this); 64 ViewCompat.setBackground(this, bg); 65 66 TypedArray a = context.obtainStyledAttributes(attrs, 67 R.styleable.ActionBar); 68 mBackground = a.getDrawable(R.styleable.ActionBar_background); 69 mStackedBackground = a.getDrawable( 70 R.styleable.ActionBar_backgroundStacked); 71 mHeight = a.getDimensionPixelSize(R.styleable.ActionBar_height, -1); 72 73 if (getId() == R.id.split_action_bar) { 74 mIsSplit = true; 75 mSplitBackground = a.getDrawable(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(R.id.action_bar); 87 mContextView = findViewById(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 * Set the action bar into a "transitioning" state. While transitioning the bar will block focus 188 * and touch from all of its descendants. This prevents the user from interacting with the bar 189 * while it is animating in or out. 190 * 191 * @param isTransitioning true if the bar is currently transitioning, false otherwise. 192 */ 193 public void setTransitioning(boolean isTransitioning) { 194 mIsTransitioning = isTransitioning; 195 setDescendantFocusability(isTransitioning ? FOCUS_BLOCK_DESCENDANTS 196 : FOCUS_AFTER_DESCENDANTS); 197 } 198 199 @Override 200 public boolean onInterceptTouchEvent(MotionEvent ev) { 201 return mIsTransitioning || super.onInterceptTouchEvent(ev); 202 } 203 204 @Override 205 public boolean onTouchEvent(MotionEvent ev) { 206 super.onTouchEvent(ev); 207 208 // An action bar always eats touch events. 209 return true; 210 } 211 212 @Override 213 public boolean onHoverEvent(MotionEvent ev) { 214 super.onHoverEvent(ev); 215 216 // An action bar always eats hover events. 217 return true; 218 } 219 220 public void setTabContainer(ScrollingTabContainerView tabView) { 221 if (mTabContainer != null) { 222 removeView(mTabContainer); 223 } 224 mTabContainer = tabView; 225 if (tabView != null) { 226 addView(tabView); 227 final ViewGroup.LayoutParams lp = tabView.getLayoutParams(); 228 lp.width = LayoutParams.MATCH_PARENT; 229 lp.height = LayoutParams.WRAP_CONTENT; 230 tabView.setAllowCollapse(false); 231 } 232 } 233 234 public View getTabContainer() { 235 return mTabContainer; 236 } 237 238 @Override 239 public android.view.ActionMode startActionModeForChild(View child, 240 android.view.ActionMode.Callback callback) { 241 // No starting an action mode for an action bar child! (Where would it go?) 242 return null; 243 } 244 245 @Override 246 public android.view.ActionMode startActionModeForChild(View child, 247 android.view.ActionMode.Callback callback, int type) { 248 if (type != android.view.ActionMode.TYPE_PRIMARY) { 249 return super.startActionModeForChild(child, callback, type); 250 } 251 return null; 252 } 253 254 private boolean isCollapsed(View view) { 255 return view == null || view.getVisibility() == GONE || view.getMeasuredHeight() == 0; 256 } 257 258 private int getMeasuredHeightWithMargins(View view) { 259 final LayoutParams lp = (LayoutParams) view.getLayoutParams(); 260 return view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; 261 } 262 263 @Override 264 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 265 if (mActionBarView == null && 266 MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST && mHeight >= 0) { 267 heightMeasureSpec = MeasureSpec.makeMeasureSpec( 268 Math.min(mHeight, MeasureSpec.getSize(heightMeasureSpec)), MeasureSpec.AT_MOST); 269 } 270 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 271 272 if (mActionBarView == null) return; 273 274 final int mode = MeasureSpec.getMode(heightMeasureSpec); 275 if (mTabContainer != null && mTabContainer.getVisibility() != GONE 276 && mode != MeasureSpec.EXACTLY) { 277 final int topMarginForTabs; 278 if (!isCollapsed(mActionBarView)) { 279 topMarginForTabs = getMeasuredHeightWithMargins(mActionBarView); 280 } else if (!isCollapsed(mContextView)) { 281 topMarginForTabs = getMeasuredHeightWithMargins(mContextView); 282 } else { 283 topMarginForTabs = 0; 284 } 285 final int maxHeight = mode == MeasureSpec.AT_MOST ? 286 MeasureSpec.getSize(heightMeasureSpec) : Integer.MAX_VALUE; 287 setMeasuredDimension(getMeasuredWidth(), 288 Math.min(topMarginForTabs + getMeasuredHeightWithMargins(mTabContainer), 289 maxHeight)); 290 } 291 } 292 293 @Override 294 public void onLayout(boolean changed, int l, int t, int r, int b) { 295 super.onLayout(changed, l, t, r, b); 296 297 final View tabContainer = mTabContainer; 298 final boolean hasTabs = tabContainer != null && tabContainer.getVisibility() != GONE; 299 300 if (tabContainer != null && tabContainer.getVisibility() != GONE) { 301 final int containerHeight = getMeasuredHeight(); 302 final LayoutParams lp = (LayoutParams) tabContainer.getLayoutParams(); 303 final int tabHeight = tabContainer.getMeasuredHeight(); 304 tabContainer.layout(l, containerHeight - tabHeight - lp.bottomMargin, r, 305 containerHeight - lp.bottomMargin); 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 if (mActionBarView.getVisibility() == View.VISIBLE) { 317 mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(), 318 mActionBarView.getRight(), mActionBarView.getBottom()); 319 } else if (mContextView != null && 320 mContextView.getVisibility() == View.VISIBLE) { 321 mBackground.setBounds(mContextView.getLeft(), mContextView.getTop(), 322 mContextView.getRight(), mContextView.getBottom()); 323 } else { 324 mBackground.setBounds(0, 0, 0, 0); 325 } 326 needsInvalidate = true; 327 } 328 mIsStacked = hasTabs; 329 if (hasTabs && mStackedBackground != null) { 330 mStackedBackground.setBounds(tabContainer.getLeft(), tabContainer.getTop(), 331 tabContainer.getRight(), tabContainer.getBottom()); 332 needsInvalidate = true; 333 } 334 } 335 336 if (needsInvalidate) { 337 invalidate(); 338 } 339 } 340} 341