Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9a3a6baf authored by Adam Powell's avatar Adam Powell Committed by Android (Google) Code Review
Browse files

Merge "Fix bug 5122319 - When action bar tabs run out of space they should...

Merge "Fix bug 5122319 - When action bar tabs run out of space they should collapse in to a spinner."
parents 246ae501 f5645cba
Loading
Loading
Loading
Loading
+16 −16
Original line number Diff line number Diff line
@@ -156,8 +156,6 @@ public class ActionBarImpl extends ActionBar {
                    "with a compatible window decor layout");
        }

        mHasEmbeddedTabs = mContext.getResources().getBoolean(
                com.android.internal.R.bool.action_bar_embed_tabs);
        mActionView.setContextView(mContextView);
        mContextDisplayMode = mActionView.isSplitActionBar() ?
                CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL;
@@ -166,25 +164,31 @@ public class ActionBarImpl extends ActionBar {
        // Newer apps need to enable it explicitly.
        setHomeButtonEnabled(mContext.getApplicationInfo().targetSdkVersion <
                Build.VERSION_CODES.ICE_CREAM_SANDWICH);

        setHasEmbeddedTabs(mContext.getResources().getBoolean(
                com.android.internal.R.bool.action_bar_embed_tabs));
    }

    public void onConfigurationChanged(Configuration newConfig) {
        mHasEmbeddedTabs = mContext.getResources().getBoolean(
                com.android.internal.R.bool.action_bar_embed_tabs);
        setHasEmbeddedTabs(mContext.getResources().getBoolean(
                com.android.internal.R.bool.action_bar_embed_tabs));
    }

    private void setHasEmbeddedTabs(boolean hasEmbeddedTabs) {
        mHasEmbeddedTabs = hasEmbeddedTabs;
        // Switch tab layout configuration if needed
        if (!mHasEmbeddedTabs) {
            mActionView.setEmbeddedTabView(null);
            mContainerView.setTabContainer(mTabScrollView);
        } else {
            mContainerView.setTabContainer(null);
            if (mTabScrollView != null) {
                mTabScrollView.setVisibility(View.VISIBLE);
            }
            mActionView.setEmbeddedTabView(mTabScrollView);
        }
        mActionView.setCollapsable(!mHasEmbeddedTabs &&
                getNavigationMode() == NAVIGATION_MODE_TABS);
        final boolean isInTabMode = getNavigationMode() == NAVIGATION_MODE_TABS;
        if (mTabScrollView != null) {
            mTabScrollView.setVisibility(isInTabMode ? View.VISIBLE : View.GONE);
        }
        mActionView.setCollapsable(!mHasEmbeddedTabs && isInTabMode);
    }

    private void ensureTabsExist() {
@@ -192,7 +196,7 @@ public class ActionBarImpl extends ActionBar {
            return;
        }

        ScrollingTabContainerView tabScroller = mActionView.createTabContainer();
        ScrollingTabContainerView tabScroller = new ScrollingTabContainerView(mContext);

        if (mHasEmbeddedTabs) {
            tabScroller.setVisibility(View.VISIBLE);
@@ -925,18 +929,14 @@ public class ActionBarImpl extends ActionBar {
            case NAVIGATION_MODE_TABS:
                mSavedTabPosition = getSelectedNavigationIndex();
                selectTab(null);
                if (!mActionView.hasEmbeddedTabs()) {
                mTabScrollView.setVisibility(View.GONE);
                }
                break;
        }
        mActionView.setNavigationMode(mode);
        switch (mode) {
            case NAVIGATION_MODE_TABS:
                ensureTabsExist();
                if (!mActionView.hasEmbeddedTabs()) {
                mTabScrollView.setVisibility(View.VISIBLE);
                }
                if (mSavedTabPosition != INVALID_POSITION) {
                    setSelectedNavigationItem(mSavedTabPosition);
                    mSavedTabPosition = INVALID_POSITION;
+2 −1
Original line number Diff line number Diff line
@@ -102,7 +102,7 @@ public class ActionBarContainer extends FrameLayout {
        return true;
    }

    public void setTabContainer(View tabView) {
    public void setTabContainer(ScrollingTabContainerView tabView) {
        if (mTabContainer != null) {
            removeView(mTabContainer);
        }
@@ -110,6 +110,7 @@ public class ActionBarContainer extends FrameLayout {
        if (tabView != null) {
            addView(tabView);
            tabView.getLayoutParams().width = LayoutParams.MATCH_PARENT;
            tabView.setAllowCollapse(false);
        }
    }

+4 −13
Original line number Diff line number Diff line
@@ -332,7 +332,10 @@ public class ActionBarView extends AbsActionBarView {
        mIncludeTabs = tabs != null;
        if (mIncludeTabs && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) {
            addView(mTabScrollView);
            mTabScrollView.getLayoutParams().width = LayoutParams.WRAP_CONTENT;
            ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams();
            lp.width = LayoutParams.WRAP_CONTENT;
            lp.height = LayoutParams.MATCH_PARENT;
            tabs.setAllowCollapse(true);
        }
    }

@@ -649,18 +652,6 @@ public class ActionBarView extends AbsActionBarView {
        }
    }

    public ScrollingTabContainerView createTabContainer() {
        final LinearLayout tabLayout = new LinearLayout(getContext(), null,
                com.android.internal.R.attr.actionBarTabBarStyle);
        tabLayout.setMeasureWithLargestChildEnabled(true);
        tabLayout.setLayoutParams(new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT, mContentHeight));

        final ScrollingTabContainerView scroller = new ScrollingTabContainerView(mContext);
        scroller.setTabLayout(tabLayout);
        return scroller;
    }

    public void setDropdownAdapter(SpinnerAdapter adapter) {
        mSpinnerAdapter = adapter;
        if (mSpinner != null) {
+213 −34
Original line number Diff line number Diff line
@@ -30,18 +30,32 @@ import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;

public class ScrollingTabContainerView extends HorizontalScrollView {
/**
 * This widget implements the dynamic action bar tab behavior that can change
 * across different configurations or circumstances.
 */
public class ScrollingTabContainerView extends HorizontalScrollView
        implements AdapterView.OnItemSelectedListener {
    private static final String TAG = "ScrollingTabContainerView";
    Runnable mTabSelector;
    private TabClickListener mTabClickListener;

    private LinearLayout mTabLayout;
    private Spinner mTabSpinner;
    private boolean mAllowCollapse;

    int mMaxTabWidth;
    private int mContentHeight;
    private int mSelectedTabIndex;

    protected Animator mVisibilityAnim;
    protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
@@ -53,14 +67,19 @@ public class ScrollingTabContainerView extends HorizontalScrollView {
    public ScrollingTabContainerView(Context context) {
        super(context);
        setHorizontalScrollBarEnabled(false);

        mTabLayout = createTabLayout();
        addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
    }

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        setFillViewport(widthMode == MeasureSpec.EXACTLY);
        final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY;
        setFillViewport(lockedExpanded);

        final int childCount = getChildCount();
        final int childCount = mTabLayout.getChildCount();
        if (childCount > 1 &&
                (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) {
            if (childCount > 2) {
@@ -72,14 +91,85 @@ public class ScrollingTabContainerView extends HorizontalScrollView {
            mMaxTabWidth = -1;
        }

        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        if (heightMode != MeasureSpec.UNSPECIFIED) {
            if (mContentHeight == 0 && heightMode == MeasureSpec.EXACTLY) {
                // Use this as our content height.
                mContentHeight = heightSize;
            }
            heightSize = Math.min(heightSize, mContentHeight);
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
        }

        final boolean canCollapse = !lockedExpanded && mAllowCollapse;

        if (canCollapse) {
            // See if we should expand
            mTabLayout.measure(MeasureSpec.UNSPECIFIED, heightMeasureSpec);
            if (mTabLayout.getMeasuredWidth() > MeasureSpec.getSize(widthMeasureSpec)) {
                performCollapse();
            } else {
                performExpand();
            }
        } else {
            performExpand();
        }

        final int oldWidth = getMeasuredWidth();
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        final int newWidth = getMeasuredWidth();

        if (lockedExpanded && oldWidth != newWidth) {
            // Recenter the tab display if we're at a new (scrollable) size.
            setTabSelected(mSelectedTabIndex);
        }
    }

    public void setTabSelected(int position) {
        if (mTabLayout == null) {
            return;
    /**
     * Indicates whether this view is collapsed into a dropdown menu instead
     * of traditional tabs.
     * @return true if showing as a spinner
     */
    private boolean isCollapsed() {
        return mTabSpinner != null && mTabSpinner.getParent() == this;
    }

    public void setAllowCollapse(boolean allowCollapse) {
        mAllowCollapse = allowCollapse;
    }

    private void performCollapse() {
        if (isCollapsed()) return;

        if (mTabSpinner == null) {
            mTabSpinner = createSpinner();
        }
        removeView(mTabLayout);
        addView(mTabSpinner, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        if (mTabSpinner.getAdapter() == null) {
            mTabSpinner.setAdapter(new TabAdapter());
        }
        if (mTabSelector != null) {
            removeCallbacks(mTabSelector);
            mTabSelector = null;
        }
        mTabSpinner.setSelection(mSelectedTabIndex);
    }

    private boolean performExpand() {
        if (!isCollapsed()) return false;

        removeView(mTabSpinner);
        addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        setTabSelected(mTabSpinner.getSelectedItemPosition());
        return false;
    }

    public void setTabSelected(int position) {
        mSelectedTabIndex = position;
        final int tabCount = mTabLayout.getChildCount();
        for (int i = 0; i < tabCount; i++) {
            final View child = mTabLayout.getChildAt(i);
@@ -92,10 +182,28 @@ public class ScrollingTabContainerView extends HorizontalScrollView {
    }

    public void setContentHeight(int contentHeight) {
        mTabLayout.getLayoutParams().height = contentHeight;
        mContentHeight = contentHeight;
        requestLayout();
    }

    private LinearLayout createTabLayout() {
        final LinearLayout tabLayout = new LinearLayout(getContext(), null,
                com.android.internal.R.attr.actionBarTabBarStyle);
        tabLayout.setMeasureWithLargestChildEnabled(true);
        tabLayout.setLayoutParams(new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT));
        return tabLayout;
    }

    private Spinner createSpinner() {
        final Spinner spinner = new Spinner(getContext(), null,
                com.android.internal.R.attr.actionDropDownStyle);
        spinner.setLayoutParams(new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT));
        spinner.setOnItemSelectedListener(this);
        return spinner;
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
@@ -132,7 +240,7 @@ public class ScrollingTabContainerView extends HorizontalScrollView {
        }
    }

    public void animateToTab(int position) {
    public void animateToTab(final int position) {
        final View tabView = mTabLayout.getChildAt(position);
        if (mTabSelector != null) {
            removeCallbacks(mTabSelector);
@@ -147,20 +255,13 @@ public class ScrollingTabContainerView extends HorizontalScrollView {
        post(mTabSelector);
    }

    public void setTabLayout(LinearLayout tabLayout) {
        if (mTabLayout != tabLayout) {
            if (mTabLayout != null) {
                ((ViewGroup) mTabLayout.getParent()).removeView(mTabLayout);
            }
            if (tabLayout != null) {
                addView(tabLayout);
            }
            mTabLayout = tabLayout;
        }
    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (mTabSelector != null) {
            // Re-post the selector we saved
            post(mTabSelector);
        }

    public LinearLayout getTabLayout() {
        return mTabLayout;
    }

    @Override
@@ -171,49 +272,91 @@ public class ScrollingTabContainerView extends HorizontalScrollView {
        }
    }

    private TabView createTabView(ActionBar.Tab tab) {
        final TabView tabView = new TabView(getContext(), tab);
    private TabView createTabView(ActionBar.Tab tab, boolean forAdapter) {
        final TabView tabView = new TabView(getContext(), tab, forAdapter);
        if (forAdapter) {
            tabView.setBackgroundDrawable(null);
            tabView.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT,
                    mContentHeight));
        } else {
            tabView.setFocusable(true);

            if (mTabClickListener == null) {
                mTabClickListener = new TabClickListener();
            }
            tabView.setOnClickListener(mTabClickListener);
        }
        return tabView;
    }

    public void addTab(ActionBar.Tab tab, boolean setSelected) {
        View tabView = createTabView(tab);
        TabView tabView = createTabView(tab, false);
        mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0,
                LayoutParams.MATCH_PARENT, 1));
        if (mTabSpinner != null) {
            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
        }
        if (setSelected) {
            tabView.setSelected(true);
        }
        if (mAllowCollapse) {
            requestLayout();
        }
    }

    public void addTab(ActionBar.Tab tab, int position, boolean setSelected) {
        final TabView tabView = createTabView(tab);
        final TabView tabView = createTabView(tab, false);
        mTabLayout.addView(tabView, position, new LinearLayout.LayoutParams(
                0, LayoutParams.MATCH_PARENT, 1));
        if (mTabSpinner != null) {
            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
        }
        if (setSelected) {
            tabView.setSelected(true);
        }
        if (mAllowCollapse) {
            requestLayout();
        }
    }

    public void updateTab(int position) {
        ((TabView) mTabLayout.getChildAt(position)).update();
        if (mTabSpinner != null) {
            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
        }
        if (mAllowCollapse) {
            requestLayout();
        }
    }

    public void removeTabAt(int position) {
        if (mTabLayout != null) {
        mTabLayout.removeViewAt(position);
        if (mTabSpinner != null) {
            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
        }
        if (mAllowCollapse) {
            requestLayout();
        }
    }

    public void removeAllTabs() {
        if (mTabLayout != null) {
        mTabLayout.removeAllViews();
        if (mTabSpinner != null) {
            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
        }
        if (mAllowCollapse) {
            requestLayout();
        }
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        TabView tabView = (TabView) view;
        tabView.getTab().select();
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
    }

    private class TabView extends LinearLayout {
@@ -222,10 +365,19 @@ public class ScrollingTabContainerView extends HorizontalScrollView {
        private ImageView mIconView;
        private View mCustomView;

        public TabView(Context context, ActionBar.Tab tab) {
        public TabView(Context context, ActionBar.Tab tab, boolean forList) {
            super(context, null, com.android.internal.R.attr.actionBarTabStyle);
            mTab = tab;

            if (forList) {
                setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
            }

            update();
        }

        public void bindTab(ActionBar.Tab tab) {
            mTab = tab;
            update();
        }

@@ -303,6 +455,33 @@ public class ScrollingTabContainerView extends HorizontalScrollView {
        }
    }

    private class TabAdapter extends BaseAdapter {
        @Override
        public int getCount() {
            return mTabLayout.getChildCount();
        }

        @Override
        public Object getItem(int position) {
            return ((TabView) mTabLayout.getChildAt(position)).getTab();
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = createTabView((ActionBar.Tab) getItem(position), true);
            } else {
                ((TabView) convertView).bindTab((ActionBar.Tab) getItem(position));
            }
            return convertView;
        }
    }

    private class TabClickListener implements OnClickListener {
        public void onClick(View view) {
            TabView tabView = (TabView) view;