ActionBarContextView.java revision d8b3f2e8eee5f24de6653a918613674e9495f751
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 */ 16package com.android.internal.widget; 17 18import com.android.internal.R; 19import com.android.internal.view.menu.ActionMenuView; 20import com.android.internal.view.menu.MenuBuilder; 21 22import android.animation.Animator; 23import android.animation.Animator.AnimatorListener; 24import android.animation.AnimatorSet; 25import android.animation.ObjectAnimator; 26import android.content.Context; 27import android.content.res.TypedArray; 28import android.util.AttributeSet; 29import android.util.Log; 30import android.view.ActionMode; 31import android.view.LayoutInflater; 32import android.view.View; 33import android.view.ViewGroup; 34import android.view.animation.DecelerateInterpolator; 35import android.widget.LinearLayout; 36import android.widget.TextView; 37 38/** 39 * @hide 40 */ 41public class ActionBarContextView extends ViewGroup implements AnimatorListener { 42 private int mContentHeight; 43 44 private CharSequence mTitle; 45 private CharSequence mSubtitle; 46 47 private View mClose; 48 private View mCustomView; 49 private LinearLayout mTitleLayout; 50 private TextView mTitleView; 51 private TextView mSubtitleView; 52 private int mTitleStyleRes; 53 private int mSubtitleStyleRes; 54 private ActionMenuView mMenuView; 55 56 private Animator mCurrentAnimation; 57 private boolean mAnimateInOnLayout; 58 private int mAnimationMode; 59 60 private static final int ANIMATE_IDLE = 0; 61 private static final int ANIMATE_IN = 1; 62 private static final int ANIMATE_OUT = 2; 63 64 public ActionBarContextView(Context context) { 65 this(context, null); 66 } 67 68 public ActionBarContextView(Context context, AttributeSet attrs) { 69 this(context, attrs, com.android.internal.R.attr.actionModeStyle); 70 } 71 72 public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) { 73 super(context, attrs, defStyle); 74 75 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionMode, defStyle, 0); 76 setBackgroundDrawable(a.getDrawable( 77 com.android.internal.R.styleable.ActionMode_background)); 78 mTitleStyleRes = a.getResourceId( 79 com.android.internal.R.styleable.ActionMode_titleTextStyle, 0); 80 mSubtitleStyleRes = a.getResourceId( 81 com.android.internal.R.styleable.ActionMode_subtitleTextStyle, 0); 82 83 mContentHeight = a.getLayoutDimension( 84 com.android.internal.R.styleable.ActionMode_height, 0); 85 a.recycle(); 86 } 87 88 @Override 89 public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) { 90 // No starting an action mode for an existing action mode UI child! (Where would it go?) 91 return null; 92 } 93 94 public void setHeight(int height) { 95 mContentHeight = height; 96 } 97 98 public void setCustomView(View view) { 99 if (mCustomView != null) { 100 removeView(mCustomView); 101 } 102 mCustomView = view; 103 if (mTitleLayout != null) { 104 removeView(mTitleLayout); 105 mTitleLayout = null; 106 } 107 if (view != null) { 108 addView(view); 109 } 110 requestLayout(); 111 } 112 113 public void setTitle(CharSequence title) { 114 mTitle = title; 115 initTitle(); 116 } 117 118 public void setSubtitle(CharSequence subtitle) { 119 mSubtitle = subtitle; 120 initTitle(); 121 } 122 123 public CharSequence getTitle() { 124 return mTitle; 125 } 126 127 public CharSequence getSubtitle() { 128 return mSubtitle; 129 } 130 131 private void initTitle() { 132 if (mTitleLayout == null) { 133 LayoutInflater inflater = LayoutInflater.from(getContext()); 134 inflater.inflate(R.layout.action_bar_title_item, this); 135 mTitleLayout = (LinearLayout) getChildAt(getChildCount() - 1); 136 mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title); 137 mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle); 138 if (mTitle != null) { 139 mTitleView.setText(mTitle); 140 if (mTitleStyleRes != 0) { 141 mTitleView.setTextAppearance(mContext, mTitleStyleRes); 142 } 143 } 144 if (mSubtitle != null) { 145 mSubtitleView.setText(mSubtitle); 146 if (mSubtitleStyleRes != 0) { 147 mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes); 148 } 149 mSubtitleView.setVisibility(VISIBLE); 150 } 151 } else { 152 mTitleView.setText(mTitle); 153 mSubtitleView.setText(mSubtitle); 154 mSubtitleView.setVisibility(mSubtitle != null ? VISIBLE : GONE); 155 if (mTitleLayout.getParent() == null) { 156 addView(mTitleLayout); 157 } 158 } 159 } 160 161 public void initForMode(final ActionMode mode) { 162 if (mCurrentAnimation != null && mCurrentAnimation.isRunning()) { 163 mCurrentAnimation.end(); 164 killMode(); 165 } 166 if (mClose == null) { 167 LayoutInflater inflater = LayoutInflater.from(mContext); 168 mClose = inflater.inflate(R.layout.action_mode_close_item, this, false); 169 addView(mClose); 170 } else { 171 addView(mClose); 172 } 173 174 View closeButton = mClose.findViewById(R.id.action_mode_close_button); 175 closeButton.setOnClickListener(new OnClickListener() { 176 public void onClick(View v) { 177 mode.finish(); 178 } 179 }); 180 181 final MenuBuilder menu = (MenuBuilder) mode.getMenu(); 182 mMenuView = (ActionMenuView) menu.getMenuView(MenuBuilder.TYPE_ACTION_BUTTON, this); 183 mMenuView.setOverflowReserved(true); 184 mMenuView.updateChildren(false); 185 addView(mMenuView); 186 187 mAnimateInOnLayout = true; 188 } 189 190 public void closeMode() { 191 if (mClose == null) { 192 killMode(); 193 return; 194 } 195 196 mAnimationMode = ANIMATE_OUT; 197 finishAnimation(); 198 mCurrentAnimation = makeOutAnimation(); 199 mCurrentAnimation.start(); 200 } 201 202 private void finishAnimation() { 203 if (mCurrentAnimation != null && mCurrentAnimation.isRunning()) { 204 mCurrentAnimation.end(); 205 } 206 } 207 208 public void killMode() { 209 finishAnimation(); 210 removeAllViews(); 211 mCustomView = null; 212 mMenuView = null; 213 } 214 215 public boolean showOverflowMenu() { 216 if (mMenuView != null) { 217 return mMenuView.showOverflowMenu(); 218 } 219 return false; 220 } 221 222 public void openOverflowMenu() { 223 if (mMenuView != null) { 224 mMenuView.openOverflowMenu(); 225 } 226 } 227 228 public boolean hideOverflowMenu() { 229 if (mMenuView != null) { 230 return mMenuView.hideOverflowMenu(); 231 } 232 return false; 233 } 234 235 public boolean isOverflowMenuShowing() { 236 if (mMenuView != null) { 237 return mMenuView.isOverflowMenuShowing(); 238 } 239 return false; 240 } 241 242 @Override 243 protected LayoutParams generateDefaultLayoutParams() { 244 // Used by custom views if they don't supply layout params. Everything else 245 // added to an ActionBarContextView should have them already. 246 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 247 } 248 249 @Override 250 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 251 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 252 if (widthMode != MeasureSpec.EXACTLY) { 253 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 254 "with android:layout_width=\"match_parent\" (or fill_parent)"); 255 } 256 257 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 258 if (heightMode == MeasureSpec.UNSPECIFIED) { 259 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 260 "with android:layout_height=\"wrap_content\""); 261 } 262 263 final int contentWidth = MeasureSpec.getSize(widthMeasureSpec); 264 265 int maxHeight = mContentHeight > 0 ? 266 mContentHeight : MeasureSpec.getSize(heightMeasureSpec); 267 268 final int verticalPadding = getPaddingTop() + getPaddingBottom(); 269 int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight(); 270 final int height = maxHeight - verticalPadding; 271 final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); 272 273 if (mClose != null) { 274 availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0); 275 } 276 277 if (mTitleLayout != null && mCustomView == null) { 278 availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0); 279 } 280 281 final int childCount = getChildCount(); 282 for (int i = 0; i < childCount; i++) { 283 final View child = getChildAt(i); 284 if (child == mClose || child == mTitleLayout || child == mCustomView) { 285 continue; 286 } 287 288 availableWidth = measureChildView(child, availableWidth, childSpecHeight, 0); 289 } 290 291 if (mCustomView != null) { 292 LayoutParams lp = mCustomView.getLayoutParams(); 293 final int customWidthMode = lp.width != LayoutParams.WRAP_CONTENT ? 294 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 295 final int customWidth = lp.width >= 0 ? 296 Math.min(lp.width, availableWidth) : availableWidth; 297 final int customHeightMode = lp.height != LayoutParams.WRAP_CONTENT ? 298 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 299 final int customHeight = lp.height >= 0 ? 300 Math.min(lp.height, height) : height; 301 mCustomView.measure(MeasureSpec.makeMeasureSpec(customWidth, customWidthMode), 302 MeasureSpec.makeMeasureSpec(customHeight, customHeightMode)); 303 } 304 305 if (mContentHeight <= 0) { 306 int measuredHeight = 0; 307 final int count = getChildCount(); 308 for (int i = 0; i < count; i++) { 309 View v = getChildAt(i); 310 int paddedViewHeight = v.getMeasuredHeight() + verticalPadding; 311 if (paddedViewHeight > measuredHeight) { 312 measuredHeight = paddedViewHeight; 313 } 314 } 315 setMeasuredDimension(contentWidth, measuredHeight); 316 } else { 317 setMeasuredDimension(contentWidth, maxHeight); 318 } 319 } 320 321 private Animator makeInAnimation() { 322 mClose.setTranslationX(-mClose.getWidth()); 323 ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX", 0); 324 buttonAnimator.setDuration(200); 325 buttonAnimator.addListener(this); 326 buttonAnimator.setInterpolator(new DecelerateInterpolator()); 327 328 AnimatorSet set = new AnimatorSet(); 329 AnimatorSet.Builder b = set.play(buttonAnimator); 330 331 if (mMenuView != null) { 332 final int count = mMenuView.getChildCount(); 333 if (count > 0) { 334 for (int i = count - 1, j = 0; i >= 0; i--, j++) { 335 View child = mMenuView.getChildAt(i); 336 child.setScaleY(0); 337 ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0, 1); 338 a.setDuration(100); 339 a.setStartDelay(j * 70); 340 b.with(a); 341 } 342 } 343 } 344 345 return set; 346 } 347 348 private Animator makeOutAnimation() { 349 ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX", 350 0, -mClose.getWidth()); 351 buttonAnimator.setDuration(200); 352 buttonAnimator.addListener(this); 353 buttonAnimator.setInterpolator(new DecelerateInterpolator()); 354 355 AnimatorSet set = new AnimatorSet(); 356 AnimatorSet.Builder b = set.play(buttonAnimator); 357 358 if (mMenuView != null) { 359 final int count = mMenuView.getChildCount(); 360 if (count > 0) { 361 for (int i = 0; i < 0; i++) { 362 View child = mMenuView.getChildAt(i); 363 child.setScaleY(0); 364 ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 1, 0); 365 a.setDuration(100); 366 a.setStartDelay(i * 70); 367 b.with(a); 368 } 369 } 370 } 371 372 return set; 373 } 374 375 @Override 376 protected void onLayout(boolean changed, int l, int t, int r, int b) { 377 int x = getPaddingLeft(); 378 final int y = getPaddingTop(); 379 final int contentHeight = b - t - getPaddingTop() - getPaddingBottom(); 380 381 if (mClose != null && mClose.getVisibility() != GONE) { 382 x += positionChild(mClose, x, y, contentHeight); 383 384 if (mAnimateInOnLayout) { 385 mAnimationMode = ANIMATE_IN; 386 mCurrentAnimation = makeInAnimation(); 387 mCurrentAnimation.start(); 388 mAnimateInOnLayout = false; 389 } 390 } 391 392 if (mTitleLayout != null && mCustomView == null) { 393 x += positionChild(mTitleLayout, x, y, contentHeight); 394 } 395 396 if (mCustomView != null) { 397 x += positionChild(mCustomView, x, y, contentHeight); 398 } 399 400 x = r - l - getPaddingRight(); 401 402 if (mMenuView != null) { 403 x -= positionChildInverse(mMenuView, x, y, contentHeight); 404 } 405 } 406 407 private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) { 408 child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 409 childSpecHeight); 410 411 availableWidth -= child.getMeasuredWidth(); 412 availableWidth -= spacing; 413 414 return availableWidth; 415 } 416 417 private int positionChild(View child, int x, int y, int contentHeight) { 418 int childWidth = child.getMeasuredWidth(); 419 int childHeight = child.getMeasuredHeight(); 420 int childTop = y + (contentHeight - childHeight) / 2; 421 422 child.layout(x, childTop, x + childWidth, childTop + childHeight); 423 424 return childWidth; 425 } 426 427 private int positionChildInverse(View child, int x, int y, int contentHeight) { 428 int childWidth = child.getMeasuredWidth(); 429 int childHeight = child.getMeasuredHeight(); 430 int childTop = y + (contentHeight - childHeight) / 2; 431 432 child.layout(x - childWidth, childTop, x, childTop + childHeight); 433 434 return childWidth; 435 } 436 437 @Override 438 public void onAnimationStart(Animator animation) { 439 } 440 441 @Override 442 public void onAnimationEnd(Animator animation) { 443 if (mAnimationMode == ANIMATE_OUT) { 444 killMode(); 445 } 446 mAnimationMode = ANIMATE_IDLE; 447 } 448 449 @Override 450 public void onAnimationCancel(Animator animation) { 451 } 452 453 @Override 454 public void onAnimationRepeat(Animator animation) { 455 } 456} 457