ActionBarContextView.java revision dd10b66601ed0b288dc93b43e14cd9fd71355728
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 (mAnimationMode != ANIMATE_IDLE || mAnimateInOnLayout) {
163            killMode();
164        }
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 if (mClose.getParent() == null) {
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 (mAnimationMode == ANIMATE_OUT) {
192            // Called again during close; just finish what we were doing.
193            return;
194        }
195        if (mClose == null) {
196            killMode();
197            return;
198        }
199
200        mAnimationMode = ANIMATE_OUT;
201        finishAnimation();
202        mCurrentAnimation = makeOutAnimation();
203        mCurrentAnimation.start();
204    }
205
206    private void finishAnimation() {
207        final Animator a = mCurrentAnimation;
208        if (a != null && a.isRunning()) {
209            mCurrentAnimation = null;
210            a.end();
211        }
212    }
213
214    public void killMode() {
215        finishAnimation();
216        removeAllViews();
217        mCustomView = null;
218        mMenuView = null;
219    }
220
221    public boolean showOverflowMenu() {
222        if (mMenuView != null) {
223            return mMenuView.showOverflowMenu();
224        }
225        return false;
226    }
227
228    public void openOverflowMenu() {
229        if (mMenuView != null) {
230            mMenuView.openOverflowMenu();
231        }
232    }
233
234    public boolean hideOverflowMenu() {
235        if (mMenuView != null) {
236            return mMenuView.hideOverflowMenu();
237        }
238        return false;
239    }
240
241    public boolean isOverflowMenuShowing() {
242        if (mMenuView != null) {
243            return mMenuView.isOverflowMenuShowing();
244        }
245        return false;
246    }
247
248    @Override
249    protected LayoutParams generateDefaultLayoutParams() {
250        // Used by custom views if they don't supply layout params. Everything else
251        // added to an ActionBarContextView should have them already.
252        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
253    }
254
255    @Override
256    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
257        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
258        if (widthMode != MeasureSpec.EXACTLY) {
259            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
260                    "with android:layout_width=\"match_parent\" (or fill_parent)");
261        }
262
263        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
264        if (heightMode == MeasureSpec.UNSPECIFIED) {
265            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
266                    "with android:layout_height=\"wrap_content\"");
267        }
268
269        final int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
270
271        int maxHeight = mContentHeight > 0 ?
272                mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
273
274        final int verticalPadding = getPaddingTop() + getPaddingBottom();
275        int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
276        final int height = maxHeight - verticalPadding;
277        final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
278
279        if (mClose != null) {
280            availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0);
281        }
282
283        if (mTitleLayout != null && mCustomView == null) {
284            availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0);
285        }
286
287        final int childCount = getChildCount();
288        for (int i = 0; i < childCount; i++) {
289            final View child = getChildAt(i);
290            if (child == mClose || child == mTitleLayout || child == mCustomView) {
291                continue;
292            }
293
294            availableWidth = measureChildView(child, availableWidth, childSpecHeight, 0);
295        }
296
297        if (mCustomView != null) {
298            LayoutParams lp = mCustomView.getLayoutParams();
299            final int customWidthMode = lp.width != LayoutParams.WRAP_CONTENT ?
300                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
301            final int customWidth = lp.width >= 0 ?
302                    Math.min(lp.width, availableWidth) : availableWidth;
303            final int customHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
304                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
305            final int customHeight = lp.height >= 0 ?
306                    Math.min(lp.height, height) : height;
307            mCustomView.measure(MeasureSpec.makeMeasureSpec(customWidth, customWidthMode),
308                    MeasureSpec.makeMeasureSpec(customHeight, customHeightMode));
309        }
310
311        if (mContentHeight <= 0) {
312            int measuredHeight = 0;
313            final int count = getChildCount();
314            for (int i = 0; i < count; i++) {
315                View v = getChildAt(i);
316                int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
317                if (paddedViewHeight > measuredHeight) {
318                    measuredHeight = paddedViewHeight;
319                }
320            }
321            setMeasuredDimension(contentWidth, measuredHeight);
322        } else {
323            setMeasuredDimension(contentWidth, maxHeight);
324        }
325    }
326
327    private Animator makeInAnimation() {
328        mClose.setTranslationX(-mClose.getWidth());
329        ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX", 0);
330        buttonAnimator.setDuration(200);
331        buttonAnimator.addListener(this);
332        buttonAnimator.setInterpolator(new DecelerateInterpolator());
333
334        AnimatorSet set = new AnimatorSet();
335        AnimatorSet.Builder b = set.play(buttonAnimator);
336
337        if (mMenuView != null) {
338            final int count = mMenuView.getChildCount();
339            if (count > 0) {
340                for (int i = count - 1, j = 0; i >= 0; i--, j++) {
341                    View child = mMenuView.getChildAt(i);
342                    child.setScaleY(0);
343                    ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0, 1);
344                    a.setDuration(100);
345                    a.setStartDelay(j * 70);
346                    b.with(a);
347                }
348            }
349        }
350
351        return set;
352    }
353
354    private Animator makeOutAnimation() {
355        ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX",
356                0, -mClose.getWidth());
357        buttonAnimator.setDuration(200);
358        buttonAnimator.addListener(this);
359        buttonAnimator.setInterpolator(new DecelerateInterpolator());
360
361        AnimatorSet set = new AnimatorSet();
362        AnimatorSet.Builder b = set.play(buttonAnimator);
363
364        if (mMenuView != null) {
365            final int count = mMenuView.getChildCount();
366            if (count > 0) {
367                for (int i = 0; i < 0; i++) {
368                    View child = mMenuView.getChildAt(i);
369                    child.setScaleY(0);
370                    ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 1, 0);
371                    a.setDuration(100);
372                    a.setStartDelay(i * 70);
373                    b.with(a);
374                }
375            }
376        }
377
378        return set;
379    }
380
381    @Override
382    protected void onLayout(boolean changed, int l, int t, int r, int b) {
383        int x = getPaddingLeft();
384        final int y = getPaddingTop();
385        final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
386
387        if (mClose != null && mClose.getVisibility() != GONE) {
388            x += positionChild(mClose, x, y, contentHeight);
389
390            if (mAnimateInOnLayout) {
391                mAnimationMode = ANIMATE_IN;
392                mCurrentAnimation = makeInAnimation();
393                mCurrentAnimation.start();
394                mAnimateInOnLayout = false;
395            }
396        }
397
398        if (mTitleLayout != null && mCustomView == null) {
399            x += positionChild(mTitleLayout, x, y, contentHeight);
400        }
401
402        if (mCustomView != null) {
403            x += positionChild(mCustomView, x, y, contentHeight);
404        }
405
406        x = r - l - getPaddingRight();
407
408        if (mMenuView != null) {
409            x -= positionChildInverse(mMenuView, x, y, contentHeight);
410        }
411    }
412
413    private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) {
414        child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
415                childSpecHeight);
416
417        availableWidth -= child.getMeasuredWidth();
418        availableWidth -= spacing;
419
420        return availableWidth;
421    }
422
423    private int positionChild(View child, int x, int y, int contentHeight) {
424        int childWidth = child.getMeasuredWidth();
425        int childHeight = child.getMeasuredHeight();
426        int childTop = y + (contentHeight - childHeight) / 2;
427
428        child.layout(x, childTop, x + childWidth, childTop + childHeight);
429
430        return childWidth;
431    }
432
433    private int positionChildInverse(View child, int x, int y, int contentHeight) {
434        int childWidth = child.getMeasuredWidth();
435        int childHeight = child.getMeasuredHeight();
436        int childTop = y + (contentHeight - childHeight) / 2;
437
438        child.layout(x - childWidth, childTop, x, childTop + childHeight);
439
440        return childWidth;
441    }
442
443    @Override
444    public void onAnimationStart(Animator animation) {
445    }
446
447    @Override
448    public void onAnimationEnd(Animator animation) {
449        if (mAnimationMode == ANIMATE_OUT) {
450            killMode();
451        }
452        mAnimationMode = ANIMATE_IDLE;
453    }
454
455    @Override
456    public void onAnimationCancel(Animator animation) {
457    }
458
459    @Override
460    public void onAnimationRepeat(Animator animation) {
461    }
462}
463