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