1/* 2 * Copyright (C) 2011 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 */ 16package android.support.v7.internal.widget; 17 18import android.content.Context; 19import android.content.res.Configuration; 20import android.content.res.TypedArray; 21import android.os.Build; 22import android.support.v4.view.ViewCompat; 23import android.support.v4.view.ViewPropertyAnimatorCompat; 24import android.support.v4.view.ViewPropertyAnimatorListener; 25import android.support.v7.appcompat.R; 26import android.support.v7.internal.view.ViewPropertyAnimatorCompatSet; 27import android.support.v7.widget.ActionMenuPresenter; 28import android.support.v7.widget.ActionMenuView; 29import android.util.AttributeSet; 30import android.util.TypedValue; 31import android.view.ContextThemeWrapper; 32import android.view.View; 33import android.view.ViewGroup; 34import android.view.animation.DecelerateInterpolator; 35import android.view.animation.Interpolator; 36 37abstract class AbsActionBarView extends ViewGroup { 38 private static final Interpolator sAlphaInterpolator = new DecelerateInterpolator(); 39 40 private static final int FADE_DURATION = 200; 41 42 protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener(); 43 44 /** Context against which to inflate popup menus. */ 45 protected final Context mPopupContext; 46 47 protected ActionMenuView mMenuView; 48 protected ActionMenuPresenter mActionMenuPresenter; 49 protected ViewGroup mSplitView; 50 protected boolean mSplitActionBar; 51 protected boolean mSplitWhenNarrow; 52 protected int mContentHeight; 53 54 protected ViewPropertyAnimatorCompat mVisibilityAnim; 55 56 AbsActionBarView(Context context) { 57 this(context, null); 58 } 59 60 AbsActionBarView(Context context, AttributeSet attrs) { 61 this(context, attrs, 0); 62 } 63 64 AbsActionBarView(Context context, AttributeSet attrs, int defStyle) { 65 super(context, attrs, defStyle); 66 67 final TypedValue tv = new TypedValue(); 68 if (context.getTheme().resolveAttribute(R.attr.actionBarPopupTheme, tv, true) 69 && tv.resourceId != 0) { 70 mPopupContext = new ContextThemeWrapper(context, tv.resourceId); 71 } else { 72 mPopupContext = context; 73 } 74 } 75 76 @Override 77 protected void onConfigurationChanged(Configuration newConfig) { 78 if (Build.VERSION.SDK_INT >= 8) { 79 super.onConfigurationChanged(newConfig); 80 } 81 82 // Action bar can change size on configuration changes. 83 // Reread the desired height from the theme-specified style. 84 TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.ActionBar, 85 R.attr.actionBarStyle, 0); 86 setContentHeight(a.getLayoutDimension(R.styleable.ActionBar_height, 0)); 87 a.recycle(); 88 89 if (mActionMenuPresenter != null) { 90 mActionMenuPresenter.onConfigurationChanged(newConfig); 91 } 92 } 93 94 /** 95 * Sets whether the bar should be split right now, no questions asked. 96 * @param split true if the bar should split 97 */ 98 public void setSplitToolbar(boolean split) { 99 mSplitActionBar = split; 100 } 101 102 /** 103 * Sets whether the bar should split if we enter a narrow screen configuration. 104 * @param splitWhenNarrow true if the bar should check to split after a config change 105 */ 106 public void setSplitWhenNarrow(boolean splitWhenNarrow) { 107 mSplitWhenNarrow = splitWhenNarrow; 108 } 109 110 public void setContentHeight(int height) { 111 mContentHeight = height; 112 requestLayout(); 113 } 114 115 public int getContentHeight() { 116 return mContentHeight; 117 } 118 119 public void setSplitView(ViewGroup splitView) { 120 mSplitView = splitView; 121 } 122 123 /** 124 * @return Current visibility or if animating, the visibility being animated to. 125 */ 126 public int getAnimatedVisibility() { 127 if (mVisibilityAnim != null) { 128 return mVisAnimListener.mFinalVisibility; 129 } 130 return getVisibility(); 131 } 132 133 public void animateToVisibility(int visibility) { 134 if (mVisibilityAnim != null) { 135 mVisibilityAnim.cancel(); 136 } 137 if (visibility == VISIBLE) { 138 if (getVisibility() != VISIBLE) { 139 ViewCompat.setAlpha(this, 0f); 140 if (mSplitView != null && mMenuView != null) { 141 ViewCompat.setAlpha(mMenuView, 0f); 142 } 143 } 144 ViewPropertyAnimatorCompat anim = ViewCompat.animate(this).alpha(1f); 145 anim.setDuration(FADE_DURATION); 146 anim.setInterpolator(sAlphaInterpolator); 147 if (mSplitView != null && mMenuView != null) { 148 ViewPropertyAnimatorCompatSet set = new ViewPropertyAnimatorCompatSet(); 149 ViewPropertyAnimatorCompat splitAnim = ViewCompat.animate(mMenuView).alpha(1f); 150 splitAnim.setDuration(FADE_DURATION); 151 set.setListener(mVisAnimListener.withFinalVisibility(anim, visibility)); 152 set.play(anim).play(splitAnim); 153 set.start(); 154 } else { 155 anim.setListener(mVisAnimListener.withFinalVisibility(anim, visibility)); 156 anim.start(); 157 } 158 } else { 159 ViewPropertyAnimatorCompat anim = ViewCompat.animate(this).alpha(0f); 160 anim.setDuration(FADE_DURATION); 161 anim.setInterpolator(sAlphaInterpolator); 162 if (mSplitView != null && mMenuView != null) { 163 ViewPropertyAnimatorCompatSet set = new ViewPropertyAnimatorCompatSet(); 164 ViewPropertyAnimatorCompat splitAnim = ViewCompat.animate(mMenuView).alpha(0f); 165 splitAnim.setDuration(FADE_DURATION); 166 set.setListener(mVisAnimListener.withFinalVisibility(anim, visibility)); 167 set.play(anim).play(splitAnim); 168 set.start(); 169 } else { 170 anim.setListener(mVisAnimListener.withFinalVisibility(anim, visibility)); 171 anim.start(); 172 } 173 } 174 } 175 176 public boolean showOverflowMenu() { 177 if (mActionMenuPresenter != null) { 178 return mActionMenuPresenter.showOverflowMenu(); 179 } 180 return false; 181 } 182 183 public void postShowOverflowMenu() { 184 post(new Runnable() { 185 public void run() { 186 showOverflowMenu(); 187 } 188 }); 189 } 190 191 public boolean hideOverflowMenu() { 192 if (mActionMenuPresenter != null) { 193 return mActionMenuPresenter.hideOverflowMenu(); 194 } 195 return false; 196 } 197 198 public boolean isOverflowMenuShowing() { 199 if (mActionMenuPresenter != null) { 200 return mActionMenuPresenter.isOverflowMenuShowing(); 201 } 202 return false; 203 } 204 205 public boolean isOverflowMenuShowPending() { 206 if (mActionMenuPresenter != null) { 207 return mActionMenuPresenter.isOverflowMenuShowPending(); 208 } 209 return false; 210 } 211 212 public boolean isOverflowReserved() { 213 return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved(); 214 } 215 216 public boolean canShowOverflowMenu() { 217 return isOverflowReserved() && getVisibility() == VISIBLE; 218 } 219 220 public void dismissPopupMenus() { 221 if (mActionMenuPresenter != null) { 222 mActionMenuPresenter.dismissPopupMenus(); 223 } 224 } 225 226 protected int measureChildView(View child, int availableWidth, int childSpecHeight, 227 int spacing) { 228 child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 229 childSpecHeight); 230 231 availableWidth -= child.getMeasuredWidth(); 232 availableWidth -= spacing; 233 234 return Math.max(0, availableWidth); 235 } 236 237 static protected int next(int x, int val, boolean isRtl) { 238 return isRtl ? x - val : x + val; 239 } 240 241 protected int positionChild(View child, int x, int y, int contentHeight, boolean reverse) { 242 int childWidth = child.getMeasuredWidth(); 243 int childHeight = child.getMeasuredHeight(); 244 int childTop = y + (contentHeight - childHeight) / 2; 245 246 if (reverse) { 247 child.layout(x - childWidth, childTop, x, childTop + childHeight); 248 } else { 249 child.layout(x, childTop, x + childWidth, childTop + childHeight); 250 } 251 252 return (reverse ? -childWidth : childWidth); 253 } 254 255 protected class VisibilityAnimListener implements ViewPropertyAnimatorListener { 256 private boolean mCanceled = false; 257 int mFinalVisibility; 258 259 public VisibilityAnimListener withFinalVisibility(ViewPropertyAnimatorCompat animation, 260 int visibility) { 261 mVisibilityAnim = animation; 262 mFinalVisibility = visibility; 263 return this; 264 } 265 266 @Override 267 public void onAnimationStart(View view) { 268 setVisibility(VISIBLE); 269 mCanceled = false; 270 } 271 272 @Override 273 public void onAnimationEnd(View view) { 274 if (mCanceled) return; 275 276 mVisibilityAnim = null; 277 setVisibility(mFinalVisibility); 278 if (mSplitView != null && mMenuView != null) { 279 mMenuView.setVisibility(mFinalVisibility); 280 } 281 } 282 283 @Override 284 public void onAnimationCancel(View view) { 285 mCanceled = true; 286 } 287 } 288} 289