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