2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com
.actionbarsherlock
.internal
.widget
;
18 import android
.content
.Context
;
19 import android
.content
.res
.TypedArray
;
20 import android
.graphics
.drawable
.Drawable
;
21 import android
.text
.TextUtils
;
22 import android
.util
.AttributeSet
;
23 import android
.view
.LayoutInflater
;
24 import android
.view
.View
;
25 import android
.view
.ViewGroup
;
26 import android
.view
.accessibility
.AccessibilityEvent
;
27 import android
.view
.animation
.DecelerateInterpolator
;
28 import android
.widget
.LinearLayout
;
29 import android
.widget
.TextView
;
31 import com
.actionbarsherlock
.R
;
32 import com
.actionbarsherlock
.internal
.nineoldandroids
.animation
.Animator
;
33 import com
.actionbarsherlock
.internal
.nineoldandroids
.animation
.Animator
.AnimatorListener
;
34 import com
.actionbarsherlock
.internal
.nineoldandroids
.animation
.AnimatorSet
;
35 import com
.actionbarsherlock
.internal
.nineoldandroids
.animation
.ObjectAnimator
;
36 import com
.actionbarsherlock
.internal
.nineoldandroids
.view
.animation
.AnimatorProxy
;
37 import com
.actionbarsherlock
.internal
.nineoldandroids
.widget
.NineLinearLayout
;
38 import com
.actionbarsherlock
.internal
.view
.menu
.ActionMenuPresenter
;
39 import com
.actionbarsherlock
.internal
.view
.menu
.ActionMenuView
;
40 import com
.actionbarsherlock
.internal
.view
.menu
.MenuBuilder
;
41 import com
.actionbarsherlock
.view
.ActionMode
;
46 public class ActionBarContextView
extends AbsActionBarView
implements AnimatorListener
{
47 //UNUSED private static final String TAG = "ActionBarContextView";
49 private CharSequence mTitle
;
50 private CharSequence mSubtitle
;
52 private NineLinearLayout mClose
;
53 private View mCustomView
;
54 private LinearLayout mTitleLayout
;
55 private TextView mTitleView
;
56 private TextView mSubtitleView
;
57 private int mTitleStyleRes
;
58 private int mSubtitleStyleRes
;
59 private Drawable mSplitBackground
;
61 private Animator mCurrentAnimation
;
62 private boolean mAnimateInOnLayout
;
63 private int mAnimationMode
;
65 private static final int ANIMATE_IDLE
= 0;
66 private static final int ANIMATE_IN
= 1;
67 private static final int ANIMATE_OUT
= 2;
69 public ActionBarContextView(Context context
) {
73 public ActionBarContextView(Context context
, AttributeSet attrs
) {
74 this(context
, attrs
, R
.attr
.actionModeStyle
);
77 public ActionBarContextView(Context context
, AttributeSet attrs
, int defStyle
) {
78 super(context
, attrs
, defStyle
);
80 TypedArray a
= context
.obtainStyledAttributes(attrs
, R
.styleable
.SherlockActionMode
, defStyle
, 0);
81 setBackgroundDrawable(a
.getDrawable(
82 R
.styleable
.SherlockActionMode_background
));
83 mTitleStyleRes
= a
.getResourceId(
84 R
.styleable
.SherlockActionMode_titleTextStyle
, 0);
85 mSubtitleStyleRes
= a
.getResourceId(
86 R
.styleable
.SherlockActionMode_subtitleTextStyle
, 0);
88 mContentHeight
= a
.getLayoutDimension(
89 R
.styleable
.SherlockActionMode_height
, 0);
91 mSplitBackground
= a
.getDrawable(
92 R
.styleable
.SherlockActionMode_backgroundSplit
);
98 public void onDetachedFromWindow() {
99 super.onDetachedFromWindow();
100 if (mActionMenuPresenter
!= null
) {
101 mActionMenuPresenter
.hideOverflowMenu();
102 mActionMenuPresenter
.hideSubMenus();
107 public void setSplitActionBar(boolean split
) {
108 if (mSplitActionBar
!= split
) {
109 if (mActionMenuPresenter
!= null
) {
110 // Mode is already active; move everything over and adjust the menu itself.
111 final LayoutParams layoutParams
= new LayoutParams(LayoutParams
.WRAP_CONTENT
,
112 LayoutParams
.MATCH_PARENT
);
114 mMenuView
= (ActionMenuView
) mActionMenuPresenter
.getMenuView(this);
115 mMenuView
.setBackgroundDrawable(null
);
116 final ViewGroup oldParent
= (ViewGroup
) mMenuView
.getParent();
117 if (oldParent
!= null
) oldParent
.removeView(mMenuView
);
118 addView(mMenuView
, layoutParams
);
120 // Allow full screen width in split mode.
121 mActionMenuPresenter
.setWidthLimit(
122 getContext().getResources().getDisplayMetrics().widthPixels
, true
);
123 // No limit to the item count; use whatever will fit.
124 mActionMenuPresenter
.setItemLimit(Integer
.MAX_VALUE
);
125 // Span the whole width
126 layoutParams
.width
= LayoutParams
.MATCH_PARENT
;
127 layoutParams
.height
= mContentHeight
;
128 mMenuView
= (ActionMenuView
) mActionMenuPresenter
.getMenuView(this);
129 mMenuView
.setBackgroundDrawable(mSplitBackground
);
130 final ViewGroup oldParent
= (ViewGroup
) mMenuView
.getParent();
131 if (oldParent
!= null
) oldParent
.removeView(mMenuView
);
132 mSplitView
.addView(mMenuView
, layoutParams
);
135 super.setSplitActionBar(split
);
139 public void setContentHeight(int height
) {
140 mContentHeight
= height
;
143 public void setCustomView(View view
) {
144 if (mCustomView
!= null
) {
145 removeView(mCustomView
);
148 if (mTitleLayout
!= null
) {
149 removeView(mTitleLayout
);
158 public void setTitle(CharSequence title
) {
163 public void setSubtitle(CharSequence subtitle
) {
164 mSubtitle
= subtitle
;
168 public CharSequence
getTitle() {
172 public CharSequence
getSubtitle() {
176 private void initTitle() {
177 if (mTitleLayout
== null
) {
178 LayoutInflater inflater
= LayoutInflater
.from(getContext());
179 inflater
.inflate(R
.layout
.abs__action_bar_title_item
, this);
180 mTitleLayout
= (LinearLayout
) getChildAt(getChildCount() - 1);
181 mTitleView
= (TextView
) mTitleLayout
.findViewById(R
.id
.abs__action_bar_title
);
182 mSubtitleView
= (TextView
) mTitleLayout
.findViewById(R
.id
.abs__action_bar_subtitle
);
183 if (mTitleStyleRes
!= 0) {
184 mTitleView
.setTextAppearance(mContext
, mTitleStyleRes
);
186 if (mSubtitleStyleRes
!= 0) {
187 mSubtitleView
.setTextAppearance(mContext
, mSubtitleStyleRes
);
191 mTitleView
.setText(mTitle
);
192 mSubtitleView
.setText(mSubtitle
);
194 final boolean hasTitle
= !TextUtils
.isEmpty(mTitle
);
195 final boolean hasSubtitle
= !TextUtils
.isEmpty(mSubtitle
);
196 mSubtitleView
.setVisibility(hasSubtitle ? VISIBLE
: GONE
);
197 mTitleLayout
.setVisibility(hasTitle
|| hasSubtitle ? VISIBLE
: GONE
);
198 if (mTitleLayout
.getParent() == null
) {
199 addView(mTitleLayout
);
203 public void initForMode(final ActionMode mode
) {
204 if (mClose
== null
) {
205 LayoutInflater inflater
= LayoutInflater
.from(mContext
);
206 mClose
= (NineLinearLayout
)inflater
.inflate(R
.layout
.abs__action_mode_close_item
, this, false
);
208 } else if (mClose
.getParent() == null
) {
212 View closeButton
= mClose
.findViewById(R
.id
.abs__action_mode_close_button
);
213 closeButton
.setOnClickListener(new OnClickListener() {
214 public void onClick(View v
) {
219 final MenuBuilder menu
= (MenuBuilder
) mode
.getMenu();
220 if (mActionMenuPresenter
!= null
) {
221 mActionMenuPresenter
.dismissPopupMenus();
223 mActionMenuPresenter
= new ActionMenuPresenter(mContext
);
224 mActionMenuPresenter
.setReserveOverflow(true
);
226 final LayoutParams layoutParams
= new LayoutParams(LayoutParams
.WRAP_CONTENT
,
227 LayoutParams
.MATCH_PARENT
);
228 if (!mSplitActionBar
) {
229 menu
.addMenuPresenter(mActionMenuPresenter
);
230 mMenuView
= (ActionMenuView
) mActionMenuPresenter
.getMenuView(this);
231 mMenuView
.setBackgroundDrawable(null
);
232 addView(mMenuView
, layoutParams
);
234 // Allow full screen width in split mode.
235 mActionMenuPresenter
.setWidthLimit(
236 getContext().getResources().getDisplayMetrics().widthPixels
, true
);
237 // No limit to the item count; use whatever will fit.
238 mActionMenuPresenter
.setItemLimit(Integer
.MAX_VALUE
);
239 // Span the whole width
240 layoutParams
.width
= LayoutParams
.MATCH_PARENT
;
241 layoutParams
.height
= mContentHeight
;
242 menu
.addMenuPresenter(mActionMenuPresenter
);
243 mMenuView
= (ActionMenuView
) mActionMenuPresenter
.getMenuView(this);
244 mMenuView
.setBackgroundDrawable(mSplitBackground
);
245 mSplitView
.addView(mMenuView
, layoutParams
);
248 mAnimateInOnLayout
= true
;
251 public void closeMode() {
252 if (mAnimationMode
== ANIMATE_OUT
) {
253 // Called again during close; just finish what we were doing.
256 if (mClose
== null
) {
262 mAnimationMode
= ANIMATE_OUT
;
263 mCurrentAnimation
= makeOutAnimation();
264 mCurrentAnimation
.start();
267 private void finishAnimation() {
268 final Animator a
= mCurrentAnimation
;
270 mCurrentAnimation
= null
;
275 public void killMode() {
278 if (mSplitView
!= null
) {
279 mSplitView
.removeView(mMenuView
);
283 mAnimateInOnLayout
= false
;
287 public boolean showOverflowMenu() {
288 if (mActionMenuPresenter
!= null
) {
289 return mActionMenuPresenter
.showOverflowMenu();
295 public boolean hideOverflowMenu() {
296 if (mActionMenuPresenter
!= null
) {
297 return mActionMenuPresenter
.hideOverflowMenu();
303 public boolean isOverflowMenuShowing() {
304 if (mActionMenuPresenter
!= null
) {
305 return mActionMenuPresenter
.isOverflowMenuShowing();
311 protected ViewGroup
.LayoutParams
generateDefaultLayoutParams() {
312 // Used by custom views if they don't supply layout params. Everything else
313 // added to an ActionBarContextView should have them already.
314 return new MarginLayoutParams(LayoutParams
.MATCH_PARENT
, LayoutParams
.WRAP_CONTENT
);
318 public ViewGroup
.LayoutParams
generateLayoutParams(AttributeSet attrs
) {
319 return new MarginLayoutParams(getContext(), attrs
);
323 protected void onMeasure(int widthMeasureSpec
, int heightMeasureSpec
) {
324 final int widthMode
= MeasureSpec
.getMode(widthMeasureSpec
);
325 if (widthMode
!= MeasureSpec
.EXACTLY
) {
326 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
327 "with android:layout_width=\"match_parent\" (or fill_parent)");
330 final int heightMode
= MeasureSpec
.getMode(heightMeasureSpec
);
331 if (heightMode
== MeasureSpec
.UNSPECIFIED
) {
332 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
333 "with android:layout_height=\"wrap_content\"");
336 final int contentWidth
= MeasureSpec
.getSize(widthMeasureSpec
);
338 int maxHeight
= mContentHeight
> 0 ?
339 mContentHeight
: MeasureSpec
.getSize(heightMeasureSpec
);
341 final int verticalPadding
= getPaddingTop() + getPaddingBottom();
342 int availableWidth
= contentWidth
- getPaddingLeft() - getPaddingRight();
343 final int height
= maxHeight
- verticalPadding
;
344 final int childSpecHeight
= MeasureSpec
.makeMeasureSpec(height
, MeasureSpec
.AT_MOST
);
346 if (mClose
!= null
) {
347 availableWidth
= measureChildView(mClose
, availableWidth
, childSpecHeight
, 0);
348 MarginLayoutParams lp
= (MarginLayoutParams
) mClose
.getLayoutParams();
349 availableWidth
-= lp
.leftMargin
+ lp
.rightMargin
;
352 if (mMenuView
!= null
&& mMenuView
.getParent() == this) {
353 availableWidth
= measureChildView(mMenuView
, availableWidth
,
357 if (mTitleLayout
!= null
&& mCustomView
== null
) {
358 availableWidth
= measureChildView(mTitleLayout
, availableWidth
, childSpecHeight
, 0);
361 if (mCustomView
!= null
) {
362 ViewGroup
.LayoutParams lp
= mCustomView
.getLayoutParams();
363 final int customWidthMode
= lp
.width
!= LayoutParams
.WRAP_CONTENT ?
364 MeasureSpec
.EXACTLY
: MeasureSpec
.AT_MOST
;
365 final int customWidth
= lp
.width
>= 0 ?
366 Math
.min(lp
.width
, availableWidth
) : availableWidth
;
367 final int customHeightMode
= lp
.height
!= LayoutParams
.WRAP_CONTENT ?
368 MeasureSpec
.EXACTLY
: MeasureSpec
.AT_MOST
;
369 final int customHeight
= lp
.height
>= 0 ?
370 Math
.min(lp
.height
, height
) : height
;
371 mCustomView
.measure(MeasureSpec
.makeMeasureSpec(customWidth
, customWidthMode
),
372 MeasureSpec
.makeMeasureSpec(customHeight
, customHeightMode
));
375 if (mContentHeight
<= 0) {
376 int measuredHeight
= 0;
377 final int count
= getChildCount();
378 for (int i
= 0; i
< count
; i
++) {
379 View v
= getChildAt(i
);
380 int paddedViewHeight
= v
.getMeasuredHeight() + verticalPadding
;
381 if (paddedViewHeight
> measuredHeight
) {
382 measuredHeight
= paddedViewHeight
;
385 setMeasuredDimension(contentWidth
, measuredHeight
);
387 setMeasuredDimension(contentWidth
, maxHeight
);
391 private Animator
makeInAnimation() {
392 mClose
.setTranslationX(-mClose
.getWidth() -
393 ((MarginLayoutParams
) mClose
.getLayoutParams()).leftMargin
);
394 ObjectAnimator buttonAnimator
= ObjectAnimator
.ofFloat(mClose
, "translationX", 0);
395 buttonAnimator
.setDuration(200);
396 buttonAnimator
.addListener(this);
397 buttonAnimator
.setInterpolator(new DecelerateInterpolator());
399 AnimatorSet set
= new AnimatorSet();
400 AnimatorSet
.Builder b
= set
.play(buttonAnimator
);
402 if (mMenuView
!= null
) {
403 final int count
= mMenuView
.getChildCount();
405 for (int i
= count
- 1, j
= 0; i
>= 0; i
--, j
++) {
406 AnimatorProxy child
= AnimatorProxy
.wrap(mMenuView
.getChildAt(i
));
408 ObjectAnimator a
= ObjectAnimator
.ofFloat(child
, "scaleY", 0, 1);
410 a
.setStartDelay(j
* 70);
419 private Animator
makeOutAnimation() {
420 ObjectAnimator buttonAnimator
= ObjectAnimator
.ofFloat(mClose
, "translationX",
421 -mClose
.getWidth() - ((MarginLayoutParams
) mClose
.getLayoutParams()).leftMargin
);
422 buttonAnimator
.setDuration(200);
423 buttonAnimator
.addListener(this);
424 buttonAnimator
.setInterpolator(new DecelerateInterpolator());
426 AnimatorSet set
= new AnimatorSet();
427 AnimatorSet
.Builder b
= set
.play(buttonAnimator
);
429 if (mMenuView
!= null
) {
430 final int count
= mMenuView
.getChildCount();
432 for (int i
= 0; i
< 0; i
++) {
433 AnimatorProxy child
= AnimatorProxy
.wrap(mMenuView
.getChildAt(i
));
435 ObjectAnimator a
= ObjectAnimator
.ofFloat(child
, "scaleY", 0);
437 a
.setStartDelay(i
* 70);
447 protected void onLayout(boolean changed
, int l
, int t
, int r
, int b
) {
448 int x
= getPaddingLeft();
449 final int y
= getPaddingTop();
450 final int contentHeight
= b
- t
- getPaddingTop() - getPaddingBottom();
452 if (mClose
!= null
&& mClose
.getVisibility() != GONE
) {
453 MarginLayoutParams lp
= (MarginLayoutParams
) mClose
.getLayoutParams();
455 x
+= positionChild(mClose
, x
, y
, contentHeight
);
458 if (mAnimateInOnLayout
) {
459 mAnimationMode
= ANIMATE_IN
;
460 mCurrentAnimation
= makeInAnimation();
461 mCurrentAnimation
.start();
462 mAnimateInOnLayout
= false
;
466 if (mTitleLayout
!= null
&& mCustomView
== null
) {
467 x
+= positionChild(mTitleLayout
, x
, y
, contentHeight
);
470 if (mCustomView
!= null
) {
471 x
+= positionChild(mCustomView
, x
, y
, contentHeight
);
474 x
= r
- l
- getPaddingRight();
476 if (mMenuView
!= null
) {
477 x
-= positionChildInverse(mMenuView
, x
, y
, contentHeight
);
482 public void onAnimationStart(Animator animation
) {
486 public void onAnimationEnd(Animator animation
) {
487 if (mAnimationMode
== ANIMATE_OUT
) {
490 mAnimationMode
= ANIMATE_IDLE
;
494 public void onAnimationCancel(Animator animation
) {
498 public void onAnimationRepeat(Animator animation
) {
502 public boolean shouldDelayChildPressedState() {
507 public void onInitializeAccessibilityEvent(AccessibilityEvent event
) {
508 if (event
.getEventType() == AccessibilityEvent
.TYPE_WINDOW_STATE_CHANGED
) {
509 // Action mode started
510 //TODO event.setSource(this);
511 event
.setClassName(getClass().getName());
512 event
.setPackageName(getContext().getPackageName());
513 event
.setContentDescription(mTitle
);
515 //TODO super.onInitializeAccessibilityEvent(event);