ActionBarView.java revision 9146ac706265cd8cce66907e617bc8572152eb97
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 */
16
17package com.android.internal.widget;
18
19import com.android.internal.R;
20import com.android.internal.view.menu.ActionMenuItem;
21import com.android.internal.view.menu.ActionMenuView;
22import com.android.internal.view.menu.MenuBuilder;
23
24import android.app.ActionBar;
25import android.app.ActionBar.NavigationCallback;
26import android.app.Activity;
27import android.content.Context;
28import android.content.pm.ApplicationInfo;
29import android.content.pm.PackageManager;
30import android.content.res.TypedArray;
31import android.graphics.PorterDuff;
32import android.graphics.PorterDuffColorFilter;
33import android.graphics.drawable.Drawable;
34import android.text.TextUtils.TruncateAt;
35import android.util.AttributeSet;
36import android.util.DisplayMetrics;
37import android.view.ActionMode;
38import android.view.Gravity;
39import android.view.LayoutInflater;
40import android.view.Menu;
41import android.view.View;
42import android.view.ViewGroup;
43import android.widget.AdapterView;
44import android.widget.ImageView;
45import android.widget.LinearLayout;
46import android.widget.Spinner;
47import android.widget.SpinnerAdapter;
48import android.widget.TextView;
49
50/**
51 * @hide
52 */
53public class ActionBarView extends ViewGroup {
54    private static final String TAG = "ActionBarView";
55
56    // TODO: This must be defined in the default theme
57    private static final int CONTENT_PADDING_DIP = 3;
58    private static final int CONTENT_SPACING_DIP = 6;
59    private static final int CONTENT_ACTION_SPACING_DIP = 12;
60
61    /**
62     * Display options applied by default
63     */
64    public static final int DISPLAY_DEFAULT = 0;
65
66    /**
67     * Display options that require re-layout as opposed to a simple invalidate
68     */
69    private static final int DISPLAY_RELAYOUT_MASK =
70            ActionBar.DISPLAY_HIDE_HOME |
71            ActionBar.DISPLAY_USE_LOGO;
72
73    private final int mContentHeight;
74
75    private int mNavigationMode;
76    private int mDisplayOptions;
77    private int mSpacing;
78    private int mActionSpacing;
79    private CharSequence mTitle;
80    private CharSequence mSubtitle;
81    private Drawable mIcon;
82    private Drawable mLogo;
83
84    private ImageView mIconView;
85    private ImageView mLogoView;
86    private LinearLayout mTitleLayout;
87    private TextView mTitleView;
88    private TextView mSubtitleView;
89    private Spinner mSpinner;
90    private LinearLayout mTabLayout;
91    private View mCustomNavView;
92
93    private int mTitleStyleRes;
94    private int mSubtitleStyleRes;
95
96    private boolean mShowMenu;
97    private boolean mUserTitle;
98
99    private MenuBuilder mOptionsMenu;
100    private ActionMenuView mMenuView;
101
102    private ActionBarContextView mContextView;
103
104    private ActionMenuItem mLogoNavItem;
105
106    private NavigationCallback mCallback;
107
108    private final AdapterView.OnItemSelectedListener mNavItemSelectedListener =
109            new AdapterView.OnItemSelectedListener() {
110        public void onItemSelected(AdapterView parent, View view, int position, long id) {
111            if (mCallback != null) {
112                mCallback.onNavigationItemSelected(position, id);
113            }
114        }
115        public void onNothingSelected(AdapterView parent) {
116            // Do nothing
117        }
118    };
119
120    private OnClickListener mHomeClickListener = null;
121
122    public ActionBarView(Context context, AttributeSet attrs) {
123        super(context, attrs);
124
125        final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
126
127        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar);
128
129        final int colorFilter = a.getColor(R.styleable.ActionBar_colorFilter, 0);
130
131        if (colorFilter != 0) {
132            final Drawable d = getBackground();
133            d.setDither(true);
134            d.setColorFilter(new PorterDuffColorFilter(colorFilter, PorterDuff.Mode.OVERLAY));
135        }
136
137        ApplicationInfo info = context.getApplicationInfo();
138        PackageManager pm = context.getPackageManager();
139        mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode,
140                ActionBar.NAVIGATION_MODE_STANDARD);
141        mTitle = a.getText(R.styleable.ActionBar_title);
142        mSubtitle = a.getText(R.styleable.ActionBar_subtitle);
143        mDisplayOptions = a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT);
144
145        mLogo = a.getDrawable(R.styleable.ActionBar_logo);
146        if (mLogo == null) {
147            mLogo = info.loadLogo(pm);
148        }
149        mIcon = a.getDrawable(R.styleable.ActionBar_icon);
150        if (mIcon == null) {
151            mIcon = info.loadIcon(pm);
152        }
153
154        Drawable background = a.getDrawable(R.styleable.ActionBar_background);
155        if (background != null) {
156            setBackgroundDrawable(background);
157        }
158
159        mTitleStyleRes = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0);
160        mSubtitleStyleRes = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0);
161
162        final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0);
163        if (customNavId != 0) {
164            LayoutInflater inflater = LayoutInflater.from(context);
165            mCustomNavView = (View) inflater.inflate(customNavId, null);
166            mNavigationMode = ActionBar.NAVIGATION_MODE_CUSTOM;
167        }
168
169        mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
170
171        a.recycle();
172
173        // TODO: Set this in the theme
174        mSpacing = (int) (CONTENT_SPACING_DIP * metrics.density + 0.5f);
175        mActionSpacing = (int) (CONTENT_ACTION_SPACING_DIP * metrics.density + 0.5f);
176
177        if (mLogo != null || mIcon != null || mTitle != null) {
178            mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle);
179            mHomeClickListener = new OnClickListener() {
180                public void onClick(View v) {
181                    Context context = getContext();
182                    if (context instanceof Activity) {
183                        Activity activity = (Activity) context;
184                        activity.onOptionsItemSelected(mLogoNavItem);
185                    }
186                }
187            };
188        }
189    }
190
191    @Override
192    public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
193        // No starting an action mode for an action bar child! (Where would it go?)
194        return null;
195    }
196
197    public void setCallback(NavigationCallback callback) {
198        mCallback = callback;
199    }
200
201    public void setMenu(Menu menu) {
202        MenuBuilder builder = (MenuBuilder) menu;
203        mOptionsMenu = builder;
204        if (mMenuView != null) {
205            removeView(mMenuView);
206        }
207        final ActionMenuView menuView = (ActionMenuView) builder.getMenuView(
208                MenuBuilder.TYPE_ACTION_BUTTON, null);
209        mActionSpacing = menuView.getItemMargin();
210        final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
211                LayoutParams.MATCH_PARENT);
212        menuView.setLayoutParams(layoutParams);
213        addView(menuView);
214        mMenuView = menuView;
215    }
216
217    public boolean showOverflowMenu() {
218        if (mMenuView != null) {
219            return mMenuView.showOverflowMenu();
220        }
221        return false;
222    }
223
224    public boolean hideOverflowMenu() {
225        if (mMenuView != null) {
226            return mMenuView.hideOverflowMenu();
227        }
228        return false;
229    }
230
231    public boolean isOverflowMenuShowing() {
232        if (mMenuView != null) {
233            return mMenuView.isOverflowMenuShowing();
234        }
235        return false;
236    }
237
238    public boolean isOverflowReserved() {
239        return mMenuView != null && mMenuView.isOverflowReserved();
240    }
241
242    public void setCustomNavigationView(View view) {
243        mCustomNavView = view;
244        if (view != null) {
245            setNavigationMode(ActionBar.NAVIGATION_MODE_CUSTOM);
246        }
247    }
248
249    public CharSequence getTitle() {
250        return mTitle;
251    }
252
253    /**
254     * Set the action bar title. This will always replace or override window titles.
255     * @param title Title to set
256     *
257     * @see #setWindowTitle(CharSequence)
258     */
259    public void setTitle(CharSequence title) {
260        mUserTitle = true;
261        setTitleImpl(title);
262    }
263
264    /**
265     * Set the window title. A window title will always be replaced or overridden by a user title.
266     * @param title Title to set
267     *
268     * @see #setTitle(CharSequence)
269     */
270    public void setWindowTitle(CharSequence title) {
271        if (!mUserTitle) {
272            setTitleImpl(title);
273        }
274    }
275
276    private void setTitleImpl(CharSequence title) {
277        mTitle = title;
278        if (mTitleView != null) {
279            mTitleView.setText(title);
280        }
281        if (mLogoNavItem != null) {
282            mLogoNavItem.setTitle(title);
283        }
284    }
285
286    public CharSequence getSubtitle() {
287        return mSubtitle;
288    }
289
290    public void setSubtitle(CharSequence subtitle) {
291        mSubtitle = subtitle;
292        if (mSubtitleView != null) {
293            mSubtitleView.setText(subtitle);
294        }
295    }
296
297    public void setDisplayOptions(int options) {
298        final int flagsChanged = options ^ mDisplayOptions;
299        mDisplayOptions = options;
300        if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) {
301            final int vis = (options & ActionBar.DISPLAY_HIDE_HOME) != 0 ? GONE : VISIBLE;
302            if (mLogoView != null) {
303                mLogoView.setVisibility(vis);
304            }
305            if (mIconView != null) {
306                mIconView.setVisibility(vis);
307            }
308
309            requestLayout();
310        } else {
311            invalidate();
312        }
313    }
314
315    public void setNavigationMode(int mode) {
316        final int oldMode = mNavigationMode;
317        if (mode != oldMode) {
318            switch (oldMode) {
319            case ActionBar.NAVIGATION_MODE_STANDARD:
320                if (mTitleLayout != null) {
321                    removeView(mTitleLayout);
322                    mTitleLayout = null;
323                    mTitleView = null;
324                    mSubtitleView = null;
325                }
326                break;
327            case ActionBar.NAVIGATION_MODE_DROPDOWN_LIST:
328                if (mSpinner != null) {
329                    removeView(mSpinner);
330                    mSpinner = null;
331                }
332                break;
333            case ActionBar.NAVIGATION_MODE_CUSTOM:
334                if (mCustomNavView != null) {
335                    removeView(mCustomNavView);
336                    mCustomNavView = null;
337                }
338                break;
339            case ActionBar.NAVIGATION_MODE_TABS:
340                if (mTabLayout != null) {
341                    removeView(mTabLayout);
342                    mTabLayout = null;
343                }
344            }
345
346            switch (mode) {
347            case ActionBar.NAVIGATION_MODE_STANDARD:
348                initTitle();
349                break;
350            case ActionBar.NAVIGATION_MODE_DROPDOWN_LIST:
351                mSpinner = new Spinner(mContext, null,
352                        com.android.internal.R.attr.dropDownSpinnerStyle);
353                mSpinner.setOnItemSelectedListener(mNavItemSelectedListener);
354                addView(mSpinner);
355                break;
356            case ActionBar.NAVIGATION_MODE_CUSTOM:
357                addView(mCustomNavView);
358                break;
359            case ActionBar.NAVIGATION_MODE_TABS:
360                mTabLayout = new LinearLayout(getContext());
361                addView(mTabLayout);
362                break;
363            }
364            mNavigationMode = mode;
365            requestLayout();
366        }
367    }
368
369    public void setDropdownAdapter(SpinnerAdapter adapter) {
370        mSpinner.setAdapter(adapter);
371    }
372
373    public void setDropdownSelectedPosition(int position) {
374        mSpinner.setSelection(position);
375    }
376
377    public int getDropdownSelectedPosition() {
378        return mSpinner.getSelectedItemPosition();
379    }
380
381    public View getCustomNavigationView() {
382        return mCustomNavView;
383    }
384
385    public int getNavigationMode() {
386        return mNavigationMode;
387    }
388
389    public int getDisplayOptions() {
390        return mDisplayOptions;
391    }
392
393    private TabView createTabView(ActionBar.Tab tab) {
394        final TabView tabView = new TabView(getContext(), tab);
395        tabView.setFocusable(true);
396        tabView.setOnClickListener(new TabClickListener());
397        return tabView;
398    }
399
400    public void addTab(ActionBar.Tab tab) {
401        final boolean isFirst = mTabLayout.getChildCount() == 0;
402        final TabView tabView = createTabView(tab);
403        mTabLayout.addView(tabView);
404        if (isFirst) {
405            tabView.setSelected(true);
406        }
407    }
408
409    public void insertTab(ActionBar.Tab tab, int position) {
410        final boolean isFirst = mTabLayout.getChildCount() == 0;
411        final TabView tabView = createTabView(tab);
412        mTabLayout.addView(tabView, position);
413        if (isFirst) {
414            tabView.setSelected(true);
415        }
416    }
417
418    public void removeTabAt(int position) {
419        mTabLayout.removeViewAt(position);
420    }
421
422    @Override
423    protected LayoutParams generateDefaultLayoutParams() {
424        // Used by custom nav views if they don't supply layout params. Everything else
425        // added to an ActionBarView should have them already.
426        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
427    }
428
429    @Override
430    protected void onFinishInflate() {
431        super.onFinishInflate();
432
433        if ((mDisplayOptions & ActionBar.DISPLAY_HIDE_HOME) == 0) {
434            if (mLogo != null && (mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) {
435                mLogoView = new ImageView(getContext());
436                mLogoView.setAdjustViewBounds(true);
437                mLogoView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
438                        LayoutParams.MATCH_PARENT));
439                mLogoView.setImageDrawable(mLogo);
440                mLogoView.setClickable(true);
441                mLogoView.setFocusable(true);
442                mLogoView.setOnClickListener(mHomeClickListener);
443                addView(mLogoView);
444            } else if (mIcon != null) {
445                mIconView = new ImageView(getContext());
446                mIconView.setAdjustViewBounds(true);
447                mIconView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
448                        LayoutParams.MATCH_PARENT));
449                mIconView.setImageDrawable(mIcon);
450                mIconView.setClickable(true);
451                mIconView.setFocusable(true);
452                mIconView.setOnClickListener(mHomeClickListener);
453                addView(mIconView);
454            }
455        }
456
457        switch (mNavigationMode) {
458        case ActionBar.NAVIGATION_MODE_STANDARD:
459            if (mLogoView == null) {
460                initTitle();
461            }
462            break;
463
464        case ActionBar.NAVIGATION_MODE_DROPDOWN_LIST:
465            throw new UnsupportedOperationException(
466                    "Inflating dropdown list navigation isn't supported yet!");
467
468        case ActionBar.NAVIGATION_MODE_TABS:
469            throw new UnsupportedOperationException(
470                    "Inflating tab navigation isn't supported yet!");
471
472        case ActionBar.NAVIGATION_MODE_CUSTOM:
473            if (mCustomNavView != null) {
474                addView(mCustomNavView);
475            }
476            break;
477        }
478    }
479
480    private void initTitle() {
481        LayoutInflater inflater = LayoutInflater.from(getContext());
482        mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item, null);
483        mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
484        mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
485
486        if (mTitleStyleRes != 0) {
487            mTitleView.setTextAppearance(mContext, mTitleStyleRes);
488        }
489        if (mTitle != null) {
490            mTitleView.setText(mTitle);
491        }
492
493        if (mSubtitleStyleRes != 0) {
494            mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
495        }
496        if (mSubtitle != null) {
497            mSubtitleView.setText(mSubtitle);
498            mSubtitleView.setVisibility(VISIBLE);
499        }
500
501        addView(mTitleLayout);
502    }
503
504    public void setTabSelected(int position) {
505        final int tabCount = mTabLayout.getChildCount();
506        for (int i = 0; i < tabCount; i++) {
507            final View child = mTabLayout.getChildAt(i);
508            child.setSelected(i == position);
509        }
510    }
511
512    public void setContextView(ActionBarContextView view) {
513        mContextView = view;
514    }
515
516    @Override
517    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
518        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
519        if (widthMode != MeasureSpec.EXACTLY) {
520            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
521                    "with android:layout_width=\"match_parent\" (or fill_parent)");
522        }
523
524        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
525        if (heightMode != MeasureSpec.AT_MOST) {
526            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
527                    "with android:layout_height=\"wrap_content\"");
528        }
529
530        int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
531
532        int maxHeight = mContentHeight > 0 ?
533                mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
534
535        final int verticalPadding = getPaddingTop() + getPaddingBottom();
536        int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
537        final int height = maxHeight - verticalPadding;
538        final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
539
540        if (mLogoView != null && mLogoView.getVisibility() != GONE) {
541            availableWidth = measureChildView(mLogoView, availableWidth, childSpecHeight, mSpacing);
542        }
543        if (mIconView != null && mIconView.getVisibility() != GONE) {
544            availableWidth = measureChildView(mIconView, availableWidth, childSpecHeight, mSpacing);
545        }
546
547        if (mMenuView != null) {
548            availableWidth = measureChildView(mMenuView, availableWidth,
549                    childSpecHeight, 0);
550        }
551
552        switch (mNavigationMode) {
553        case ActionBar.NAVIGATION_MODE_STANDARD:
554            if (mTitleLayout != null) {
555                measureChildView(mTitleLayout, availableWidth, childSpecHeight, mSpacing);
556            }
557            break;
558        case ActionBar.NAVIGATION_MODE_DROPDOWN_LIST:
559            if (mSpinner != null) {
560                mSpinner.measure(
561                        MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
562                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
563            }
564            break;
565        case ActionBar.NAVIGATION_MODE_CUSTOM:
566            if (mCustomNavView != null) {
567                LayoutParams lp = mCustomNavView.getLayoutParams();
568                final int customNavWidthMode = lp.width != LayoutParams.WRAP_CONTENT ?
569                        MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
570                final int customNavWidth = lp.width >= 0 ?
571                        Math.min(lp.width, availableWidth) : availableWidth;
572
573                // If the action bar is wrapping to its content height, don't allow a custom
574                // view to MATCH_PARENT.
575                int customNavHeightMode;
576                if (mContentHeight <= 0) {
577                    customNavHeightMode = MeasureSpec.AT_MOST;
578                } else {
579                    customNavHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
580                            MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
581                }
582                final int customNavHeight = lp.height >= 0 ?
583                        Math.min(lp.height, height) : height;
584                mCustomNavView.measure(
585                        MeasureSpec.makeMeasureSpec(customNavWidth, customNavWidthMode),
586                        MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode));
587            }
588            break;
589        case ActionBar.NAVIGATION_MODE_TABS:
590            if (mTabLayout != null) {
591                mTabLayout.measure(
592                        MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
593                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
594            }
595            break;
596        }
597
598        if (mContentHeight <= 0) {
599            int measuredHeight = 0;
600            final int count = getChildCount();
601            for (int i = 0; i < count; i++) {
602                View v = getChildAt(i);
603                int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
604                if (paddedViewHeight > measuredHeight) {
605                    measuredHeight = paddedViewHeight;
606                }
607            }
608            setMeasuredDimension(contentWidth, measuredHeight);
609        } else {
610            setMeasuredDimension(contentWidth, maxHeight);
611        }
612
613        if (mContextView != null) {
614            mContextView.setHeight(getMeasuredHeight());
615        }
616    }
617
618    private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) {
619        child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
620                childSpecHeight);
621
622        availableWidth -= child.getMeasuredWidth();
623        availableWidth -= spacing;
624
625        return availableWidth;
626    }
627
628    @Override
629    protected void onLayout(boolean changed, int l, int t, int r, int b) {
630        int x = getPaddingLeft();
631        final int y = getPaddingTop();
632        final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
633
634        if (mLogoView != null && mLogoView.getVisibility() != GONE) {
635            x += positionChild(mLogoView, x, y, contentHeight) + mSpacing;
636        }
637        if (mIconView != null && mIconView.getVisibility() != GONE) {
638            x += positionChild(mIconView, x, y, contentHeight) + mSpacing;
639        }
640
641        switch (mNavigationMode) {
642        case ActionBar.NAVIGATION_MODE_STANDARD:
643            if (mTitleLayout != null) {
644                x += positionChild(mTitleLayout, x, y, contentHeight) + mSpacing;
645            }
646            break;
647        case ActionBar.NAVIGATION_MODE_DROPDOWN_LIST:
648            if (mSpinner != null) {
649                x += positionChild(mSpinner, x, y, contentHeight) + mSpacing;
650            }
651            break;
652        case ActionBar.NAVIGATION_MODE_CUSTOM:
653            if (mCustomNavView != null) {
654                x += positionChild(mCustomNavView, x, y, contentHeight) + mSpacing;
655            }
656            break;
657        case ActionBar.NAVIGATION_MODE_TABS:
658            if (mTabLayout != null) {
659                x += positionChild(mTabLayout, x, y, contentHeight) + mSpacing;
660            }
661        }
662
663        x = r - l - getPaddingRight();
664
665        if (mMenuView != null) {
666            x -= positionChildInverse(mMenuView, x + mActionSpacing, y, contentHeight)
667                    - mActionSpacing;
668        }
669    }
670
671    private int positionChild(View child, int x, int y, int contentHeight) {
672        int childWidth = child.getMeasuredWidth();
673        int childHeight = child.getMeasuredHeight();
674        int childTop = y + (contentHeight - childHeight) / 2;
675
676        child.layout(x, childTop, x + childWidth, childTop + childHeight);
677
678        return childWidth;
679    }
680
681    private int positionChildInverse(View child, int x, int y, int contentHeight) {
682        int childWidth = child.getMeasuredWidth();
683        int childHeight = child.getMeasuredHeight();
684        int childTop = y + (contentHeight - childHeight) / 2;
685
686        child.layout(x - childWidth, childTop, x, childTop + childHeight);
687
688        return childWidth;
689    }
690
691    private static class TabView extends LinearLayout {
692        private ActionBar.Tab mTab;
693
694        public TabView(Context context, ActionBar.Tab tab) {
695            super(context);
696            mTab = tab;
697
698            // TODO Style tabs based on the theme
699
700            final Drawable icon = tab.getIcon();
701            final CharSequence text = tab.getText();
702
703            if (icon != null) {
704                ImageView iconView = new ImageView(context);
705                iconView.setImageDrawable(icon);
706                LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
707                        LayoutParams.WRAP_CONTENT);
708                lp.gravity = Gravity.CENTER_VERTICAL;
709                iconView.setLayoutParams(lp);
710                addView(iconView);
711            }
712
713            if (text != null) {
714                TextView textView = new TextView(context);
715                textView.setText(text);
716                textView.setSingleLine();
717                textView.setEllipsize(TruncateAt.END);
718                LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
719                        LayoutParams.WRAP_CONTENT);
720                lp.gravity = Gravity.CENTER_VERTICAL;
721                textView.setLayoutParams(lp);
722                addView(textView);
723            }
724
725            setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
726                    LayoutParams.MATCH_PARENT, 1));
727        }
728
729        public ActionBar.Tab getTab() {
730            return mTab;
731        }
732    }
733
734    private class TabClickListener implements OnClickListener {
735        public void onClick(View view) {
736            TabView tabView = (TabView) view;
737            tabView.getTab().select();
738            final int tabCount = mTabLayout.getChildCount();
739            for (int i = 0; i < tabCount; i++) {
740                final View child = mTabLayout.getChildAt(i);
741                child.setSelected(child == view);
742            }
743        }
744    }
745}
746