Loading tools/layoutlib/bridge/resources/bars/action_bar.xmldeleted 100644 → 0 +0 −6 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android"> <include layout="@android:layout/screen_action_bar" /> </merge> tools/layoutlib/bridge/src/com/android/internal/view/menu/MenuBuilderAccessor.java 0 → 100644 +12 −0 Original line number Diff line number Diff line package com.android.internal.view.menu; import java.util.ArrayList; /** * To access non public members of {@link MenuBuilder} */ public class MenuBuilderAccessor { public static ArrayList<MenuItemImpl> getNonActionItems(MenuBuilder builder) { return builder.getNonActionItems(); } } tools/layoutlib/bridge/src/com/android/internal/widget/ActionBarAccessor.java 0 → 100644 +32 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.widget; import com.android.internal.view.menu.ActionMenuPresenter; /** * To access non public members of AbsActionBarView */ public class ActionBarAccessor { /** * Returns the {@link ActionMenuPresenter} associated with the {@link AbsActionBarView} */ public static ActionMenuPresenter getActionMenuPresenter(AbsActionBarView view) { return view.mActionMenuPresenter; } } tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java +243 −79 Original line number Diff line number Diff line Loading @@ -16,108 +16,122 @@ package com.android.layoutlib.bridge.bars; import com.android.annotations.Nullable; import com.android.ide.common.rendering.api.ActionBarCallback; import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle; import com.android.ide.common.rendering.api.RenderResources; import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.SessionParams; import com.android.internal.R; import com.android.internal.app.ActionBarImpl; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuBuilderAccessor; import com.android.internal.view.menu.MenuItemImpl; import com.android.internal.widget.ActionBarAccessor; import com.android.internal.widget.ActionBarContainer; import com.android.internal.widget.ActionBarView; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.impl.ParserFactory; import com.android.layoutlib.bridge.impl.ResourceHelper; import com.android.resources.ResourceType; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.app.ActionBar; import android.app.ActionBar.Tab; import android.app.ActionBar.TabListener; import android.app.FragmentTransaction; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.RelativeLayout; import java.util.ArrayList; import java.util.List; /** * A layout representing the action bar. */ public class ActionBarLayout extends LinearLayout { // Store another reference to the context so that we don't have to cast it repeatedly. @SuppressWarnings("hiding") private final BridgeContext mContext; private final BridgeContext mBridgeContext; private final Context mThemedContext; private final ActionBar mActionBar; private final String mIcon; private final String mTitle; private final String mSubTitle; // Data for Action Bar. @Nullable private final String mIcon; @Nullable private final String mTitle; @Nullable private final String mSubTitle; private final boolean mSplit; private final boolean mShowHomeAsUp; private final List<Integer> mMenuIds; private final MenuBuilder mMenuBuilder; private final int mNavMode; public ActionBarLayout(BridgeContext context, SessionParams params) throws XmlPullParserException { super(context); mIcon = params.getAppIcon(); mTitle = params.getAppLabel(); mContext = context; mMenuIds = new ArrayList<Integer>(); ActionBarCallback callback = params.getProjectCallback().getActionBarCallback(); mSplit = callback.getSplitActionBarWhenNarrow() & context.getResources().getBoolean( com.android.internal.R.bool.split_action_bar_is_narrow); mNavMode = callback.getNavigationMode(); // TODO: Support Navigation Drawer Indicator. mShowHomeAsUp = callback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP; mSubTitle = callback.getSubTitle(); // Helper fields. private final MenuBuilder mMenuBuilder; private final int mPopupMaxWidth; private final RenderResources res; @Nullable private final ActionBarView mActionBarView; @Nullable private FrameLayout mContentRoot; private final ActionBarCallback mCallback; fillMenuIds(callback.getMenuIdNames()); mMenuBuilder = new MenuBuilder(mContext); // A fake parent for measuring views. @Nullable private ViewGroup mMeasureParent; public ActionBarLayout(BridgeContext context, SessionParams params) { super(context); setOrientation(LinearLayout.HORIZONTAL); setGravity(Gravity.CENTER_VERTICAL); LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE); XmlPullParser parser = ParserFactory.create( getClass().getResourceAsStream("/bars/action_bar.xml"), "action_bar.xml"); BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser( parser, context, false /*platformFile*/); try { inflater.inflate(bridgeParser, this, true /*attachToRoot*/); } finally { bridgeParser.ensurePopped(); } // Inflate action bar layout. LayoutInflater.from(context).inflate(R.layout.screen_action_bar, this, true /*attachToRoot*/); mActionBar = new ActionBarImpl(this); setUpActionBar(); } // Set contexts. mBridgeContext = context; mThemedContext = mActionBar.getThemedContext(); private void fillMenuIds(List<String> menuIdNames) { for (String name : menuIdNames) { int id = mContext.getProjectResourceValue(ResourceType.MENU, name, -1); if (id > -1) { mMenuIds.add(id); } } } // Set data for action bar. mCallback = params.getProjectCallback().getActionBarCallback(); mIcon = params.getAppIcon(); mTitle = params.getAppLabel(); // Split Action Bar when the screen size is narrow and the application requests split action // bar when narrow. mSplit = context.getResources().getBoolean(R.bool.split_action_bar_is_narrow) && mCallback.getSplitActionBarWhenNarrow(); mNavMode = mCallback.getNavigationMode(); // TODO: Support Navigation Drawer Indicator. mShowHomeAsUp = mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP; mSubTitle = mCallback.getSubTitle(); private void setUpActionBar() { RenderResources res = mContext.getRenderResources(); Drawable iconDrawable = getDrawable(res, mIcon, false /*isFramework*/); if (iconDrawable != null) { mActionBar.setIcon(iconDrawable); // Set helper fields. mMenuBuilder = new MenuBuilder(mThemedContext); res = mBridgeContext.getRenderResources(); mPopupMaxWidth = Math.max(mBridgeContext.getMetrics().widthPixels / 2, mThemedContext.getResources().getDimensionPixelSize( R.dimen.config_prefDialogWidth)); mActionBarView = (ActionBarView) findViewById(R.id.action_bar); mContentRoot = (FrameLayout) findViewById(android.R.id.content); setupActionBar(); } /** * Sets up the action bar by filling the appropriate data. */ private void setupActionBar() { // Add title and sub title. ResourceValue titleValue = res.findResValue(mTitle, false /*isFramework*/); if (titleValue != null && titleValue.getValue() != null) { mActionBar.setTitle(titleValue.getValue()); Loading @@ -127,41 +141,62 @@ public class ActionBarLayout extends LinearLayout { if (mSubTitle != null) { mActionBar.setSubtitle(mSubTitle); } ActionBarView actionBarView = (ActionBarView) findViewById( Bridge.getResourceId(ResourceType.ID, "action_bar")); actionBarView.setSplitView((ActionBarContainer) findViewById( Bridge.getResourceId(ResourceType.ID, "split_action_bar"))); actionBarView.setSplitActionBar(mSplit); // Add show home as up icon. if (mShowHomeAsUp) { mActionBar.setDisplayOptions(0xFF, ActionBar.DISPLAY_HOME_AS_UP); } setUpActionMenus(); // Set the navigation mode. mActionBar.setNavigationMode(mNavMode); if (mNavMode == ActionBar.NAVIGATION_MODE_TABS) { setUpTabs(3); setupTabs(3); } // Ideally we should get the icon from the mThemedContext using R.styleable.ActionBar_icon. // But for simplicity, we are using the icon passed from the session params. This has been // fixed in API 19. if (mIcon != null) { Drawable iconDrawable = getDrawable(mIcon, false /*isFramework*/); if (iconDrawable != null) { mActionBar.setIcon(iconDrawable); } } private void setUpActionMenus() { if (mMenuIds == null) { if (mActionBarView != null) { // Set action bar to be split, if needed. mActionBarView.setSplitView((ActionBarContainer) findViewById(R.id.split_action_bar)); mActionBarView.setSplitActionBar(mSplit); inflateMenus(); } } /** * Gets the menus to add to the action bar from the callback, resolves them, inflates them and * adds them to the action bar. */ private void inflateMenus() { if (mActionBarView == null) { return; } ActionBarView actionBarView = (ActionBarView) findViewById(Bridge.getResourceId( ResourceType.ID, "action_bar")); final MenuInflater inflater = new MenuInflater(mActionBar.getThemedContext()); for (int id : mMenuIds) { final MenuInflater inflater = new MenuInflater(mThemedContext); for (String name : mCallback.getMenuIdNames()) { if (mBridgeContext.getRenderResources().getProjectResource(ResourceType.MENU, name) != null) { int id = mBridgeContext.getProjectResourceValue(ResourceType.MENU, name, -1); if (id > -1) { inflater.inflate(id, mMenuBuilder); } actionBarView.setMenu(mMenuBuilder, null /*callback*/); } } mActionBarView.setMenu(mMenuBuilder, null /*callback*/); } // TODO: Use an adapter, like List View to set up tabs. private void setUpTabs(int num) { private void setupTabs(int num) { for (int i = 1; i <= num; i++) { Tab tab = mActionBar.newTab() .setText("Tab" + i) .setTabListener(new TabListener() { Tab tab = mActionBar.newTab().setText("Tab" + i).setTabListener(new TabListener() { @Override public void onTabUnselected(Tab t, FragmentTransaction ft) { // pass Loading @@ -179,12 +214,141 @@ public class ActionBarLayout extends LinearLayout { } } private Drawable getDrawable(RenderResources res, String name, boolean isFramework) { @Nullable private Drawable getDrawable(String name, boolean isFramework) { ResourceValue value = res.findResValue(name, isFramework); value = res.resolveResValue(value); if (value != null) { return ResourceHelper.getDrawable(value, mContext); return ResourceHelper.getDrawable(value, mBridgeContext); } return null; } /** * Creates a Popup and adds it to the content frame. It also adds another {@link FrameLayout} to * the content frame which shall serve as the new content root. */ public void createMenuPopup() { assert mContentRoot != null && findViewById(android.R.id.content) == mContentRoot : "Action Bar Menus have already been created."; if (!isOverflowPopupNeeded()) { return; } // Create a layout to hold the menus and the user's content. RelativeLayout layout = new RelativeLayout(mThemedContext); layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); mContentRoot.addView(layout); // Create a layout for the user's content. FrameLayout contentRoot = new FrameLayout(mBridgeContext); contentRoot.setLayoutParams(new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); // Add contentRoot and menus to the layout. layout.addView(contentRoot); layout.addView(createMenuView()); // ContentRoot is now the view we just created. mContentRoot = contentRoot; } /** * Returns a {@link LinearLayout} containing the menu list view to be embedded in a * {@link RelativeLayout} */ private View createMenuView() { DisplayMetrics metrics = mBridgeContext.getMetrics(); OverflowMenuAdapter adapter = new OverflowMenuAdapter(mMenuBuilder, mThemedContext); LinearLayout layout = new LinearLayout(mThemedContext); RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams( measureContentWidth(adapter), LayoutParams.WRAP_CONTENT); layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); if (mSplit) { layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); // TODO: Find correct value instead of hardcoded 10dp. layoutParams.bottomMargin = getPixelValue("-10dp", metrics); } else { layoutParams.topMargin = getPixelValue("-10dp", metrics); } layout.setLayoutParams(layoutParams); final TypedArray a = mThemedContext.obtainStyledAttributes(null, R.styleable.PopupWindow, R.attr.popupMenuStyle, 0); layout.setBackground(a.getDrawable(R.styleable.PopupWindow_popupBackground)); layout.setDividerDrawable(a.getDrawable(R.attr.actionBarDivider)); a.recycle(); layout.setOrientation(LinearLayout.VERTICAL); layout.setDividerPadding(getPixelValue("12dp", metrics)); layout.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE); ListView listView = new ListView(mThemedContext, null, R.attr.dropDownListViewStyle); listView.setAdapter(adapter); layout.addView(listView); return layout; } private boolean isOverflowPopupNeeded() { boolean needed = mCallback.isOverflowPopupNeeded(); if (!needed) { return false; } // Copied from android.widget.ActionMenuPresenter.updateMenuView() ArrayList<MenuItemImpl> menus = MenuBuilderAccessor.getNonActionItems(mMenuBuilder); if (ActionBarAccessor.getActionMenuPresenter(mActionBarView).isOverflowReserved() && menus != null) { final int count = menus.size(); if (count == 1) { needed = !menus.get(0).isActionViewExpanded(); } else { needed = count > 0; } } return needed; } @Nullable public FrameLayout getContentRoot() { return mContentRoot; } // Copied from com.android.internal.view.menu.MenuPopHelper.measureContentWidth() private int measureContentWidth(ListAdapter adapter) { // Menus don't tend to be long, so this is more sane than it looks. int maxWidth = 0; View itemView = null; int itemType = 0; final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); final int count = adapter.getCount(); for (int i = 0; i < count; i++) { final int positionType = adapter.getItemViewType(i); if (positionType != itemType) { itemType = positionType; itemView = null; } if (mMeasureParent == null) { mMeasureParent = new FrameLayout(mThemedContext); } itemView = adapter.getView(i, itemView, mMeasureParent); itemView.measure(widthMeasureSpec, heightMeasureSpec); final int itemWidth = itemView.getMeasuredWidth(); if (itemWidth >= mPopupMaxWidth) { return mPopupMaxWidth; } else if (itemWidth > maxWidth) { maxWidth = itemWidth; } } return maxWidth; } private int getPixelValue(String value, DisplayMetrics metrics) { TypedValue typedValue = ResourceHelper.getValue(null, value, false /*requireUnit*/); return (int) typedValue.getDimension(metrics); } } tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/OverflowMenuAdapter.java 0 → 100644 +100 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.layoutlib.bridge.bars; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuBuilderAccessor; import com.android.internal.view.menu.MenuItemImpl; import com.android.internal.view.menu.MenuView; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import java.util.ArrayList; /** * Provides an adapter for Overflow menu popup. This is very similar to * {@code MenuPopupHelper.MenuAdapter} */ public class OverflowMenuAdapter extends BaseAdapter { private final MenuBuilder mMenu; private int mExpandedIndex = -1; private final Context context; public OverflowMenuAdapter(MenuBuilder menu, Context context) { mMenu = menu; findExpandedIndex(); this.context = context; } @Override public int getCount() { ArrayList<MenuItemImpl> items = MenuBuilderAccessor.getNonActionItems(mMenu); if (mExpandedIndex < 0) { return items.size(); } return items.size() - 1; } @Override public MenuItemImpl getItem(int position) { ArrayList<MenuItemImpl> items = MenuBuilderAccessor.getNonActionItems(mMenu); if (mExpandedIndex >= 0 && position >= mExpandedIndex) { position++; } return items.get(position); } @Override public long getItemId(int position) { // Since a menu item's ID is optional, we'll use the position as an // ID for the item in the AdapterView return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { LayoutInflater mInflater = LayoutInflater.from(context); convertView = mInflater.inflate(com.android.internal.R.layout.popup_menu_item_layout, parent, false); } MenuView.ItemView itemView = (MenuView.ItemView) convertView; itemView.initialize(getItem(position), 0); return convertView; } private void findExpandedIndex() { final MenuItemImpl expandedItem = mMenu.getExpandedItem(); if (expandedItem != null) { final ArrayList<MenuItemImpl> items = MenuBuilderAccessor.getNonActionItems(mMenu); final int count = items.size(); for (int i = 0; i < count; i++) { final MenuItemImpl item = items.get(i); if (item == expandedItem) { mExpandedIndex = i; return; } } } } } Loading
tools/layoutlib/bridge/resources/bars/action_bar.xmldeleted 100644 → 0 +0 −6 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android"> <include layout="@android:layout/screen_action_bar" /> </merge>
tools/layoutlib/bridge/src/com/android/internal/view/menu/MenuBuilderAccessor.java 0 → 100644 +12 −0 Original line number Diff line number Diff line package com.android.internal.view.menu; import java.util.ArrayList; /** * To access non public members of {@link MenuBuilder} */ public class MenuBuilderAccessor { public static ArrayList<MenuItemImpl> getNonActionItems(MenuBuilder builder) { return builder.getNonActionItems(); } }
tools/layoutlib/bridge/src/com/android/internal/widget/ActionBarAccessor.java 0 → 100644 +32 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.widget; import com.android.internal.view.menu.ActionMenuPresenter; /** * To access non public members of AbsActionBarView */ public class ActionBarAccessor { /** * Returns the {@link ActionMenuPresenter} associated with the {@link AbsActionBarView} */ public static ActionMenuPresenter getActionMenuPresenter(AbsActionBarView view) { return view.mActionMenuPresenter; } }
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java +243 −79 Original line number Diff line number Diff line Loading @@ -16,108 +16,122 @@ package com.android.layoutlib.bridge.bars; import com.android.annotations.Nullable; import com.android.ide.common.rendering.api.ActionBarCallback; import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle; import com.android.ide.common.rendering.api.RenderResources; import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.SessionParams; import com.android.internal.R; import com.android.internal.app.ActionBarImpl; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuBuilderAccessor; import com.android.internal.view.menu.MenuItemImpl; import com.android.internal.widget.ActionBarAccessor; import com.android.internal.widget.ActionBarContainer; import com.android.internal.widget.ActionBarView; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.impl.ParserFactory; import com.android.layoutlib.bridge.impl.ResourceHelper; import com.android.resources.ResourceType; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.app.ActionBar; import android.app.ActionBar.Tab; import android.app.ActionBar.TabListener; import android.app.FragmentTransaction; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.RelativeLayout; import java.util.ArrayList; import java.util.List; /** * A layout representing the action bar. */ public class ActionBarLayout extends LinearLayout { // Store another reference to the context so that we don't have to cast it repeatedly. @SuppressWarnings("hiding") private final BridgeContext mContext; private final BridgeContext mBridgeContext; private final Context mThemedContext; private final ActionBar mActionBar; private final String mIcon; private final String mTitle; private final String mSubTitle; // Data for Action Bar. @Nullable private final String mIcon; @Nullable private final String mTitle; @Nullable private final String mSubTitle; private final boolean mSplit; private final boolean mShowHomeAsUp; private final List<Integer> mMenuIds; private final MenuBuilder mMenuBuilder; private final int mNavMode; public ActionBarLayout(BridgeContext context, SessionParams params) throws XmlPullParserException { super(context); mIcon = params.getAppIcon(); mTitle = params.getAppLabel(); mContext = context; mMenuIds = new ArrayList<Integer>(); ActionBarCallback callback = params.getProjectCallback().getActionBarCallback(); mSplit = callback.getSplitActionBarWhenNarrow() & context.getResources().getBoolean( com.android.internal.R.bool.split_action_bar_is_narrow); mNavMode = callback.getNavigationMode(); // TODO: Support Navigation Drawer Indicator. mShowHomeAsUp = callback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP; mSubTitle = callback.getSubTitle(); // Helper fields. private final MenuBuilder mMenuBuilder; private final int mPopupMaxWidth; private final RenderResources res; @Nullable private final ActionBarView mActionBarView; @Nullable private FrameLayout mContentRoot; private final ActionBarCallback mCallback; fillMenuIds(callback.getMenuIdNames()); mMenuBuilder = new MenuBuilder(mContext); // A fake parent for measuring views. @Nullable private ViewGroup mMeasureParent; public ActionBarLayout(BridgeContext context, SessionParams params) { super(context); setOrientation(LinearLayout.HORIZONTAL); setGravity(Gravity.CENTER_VERTICAL); LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE); XmlPullParser parser = ParserFactory.create( getClass().getResourceAsStream("/bars/action_bar.xml"), "action_bar.xml"); BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser( parser, context, false /*platformFile*/); try { inflater.inflate(bridgeParser, this, true /*attachToRoot*/); } finally { bridgeParser.ensurePopped(); } // Inflate action bar layout. LayoutInflater.from(context).inflate(R.layout.screen_action_bar, this, true /*attachToRoot*/); mActionBar = new ActionBarImpl(this); setUpActionBar(); } // Set contexts. mBridgeContext = context; mThemedContext = mActionBar.getThemedContext(); private void fillMenuIds(List<String> menuIdNames) { for (String name : menuIdNames) { int id = mContext.getProjectResourceValue(ResourceType.MENU, name, -1); if (id > -1) { mMenuIds.add(id); } } } // Set data for action bar. mCallback = params.getProjectCallback().getActionBarCallback(); mIcon = params.getAppIcon(); mTitle = params.getAppLabel(); // Split Action Bar when the screen size is narrow and the application requests split action // bar when narrow. mSplit = context.getResources().getBoolean(R.bool.split_action_bar_is_narrow) && mCallback.getSplitActionBarWhenNarrow(); mNavMode = mCallback.getNavigationMode(); // TODO: Support Navigation Drawer Indicator. mShowHomeAsUp = mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP; mSubTitle = mCallback.getSubTitle(); private void setUpActionBar() { RenderResources res = mContext.getRenderResources(); Drawable iconDrawable = getDrawable(res, mIcon, false /*isFramework*/); if (iconDrawable != null) { mActionBar.setIcon(iconDrawable); // Set helper fields. mMenuBuilder = new MenuBuilder(mThemedContext); res = mBridgeContext.getRenderResources(); mPopupMaxWidth = Math.max(mBridgeContext.getMetrics().widthPixels / 2, mThemedContext.getResources().getDimensionPixelSize( R.dimen.config_prefDialogWidth)); mActionBarView = (ActionBarView) findViewById(R.id.action_bar); mContentRoot = (FrameLayout) findViewById(android.R.id.content); setupActionBar(); } /** * Sets up the action bar by filling the appropriate data. */ private void setupActionBar() { // Add title and sub title. ResourceValue titleValue = res.findResValue(mTitle, false /*isFramework*/); if (titleValue != null && titleValue.getValue() != null) { mActionBar.setTitle(titleValue.getValue()); Loading @@ -127,41 +141,62 @@ public class ActionBarLayout extends LinearLayout { if (mSubTitle != null) { mActionBar.setSubtitle(mSubTitle); } ActionBarView actionBarView = (ActionBarView) findViewById( Bridge.getResourceId(ResourceType.ID, "action_bar")); actionBarView.setSplitView((ActionBarContainer) findViewById( Bridge.getResourceId(ResourceType.ID, "split_action_bar"))); actionBarView.setSplitActionBar(mSplit); // Add show home as up icon. if (mShowHomeAsUp) { mActionBar.setDisplayOptions(0xFF, ActionBar.DISPLAY_HOME_AS_UP); } setUpActionMenus(); // Set the navigation mode. mActionBar.setNavigationMode(mNavMode); if (mNavMode == ActionBar.NAVIGATION_MODE_TABS) { setUpTabs(3); setupTabs(3); } // Ideally we should get the icon from the mThemedContext using R.styleable.ActionBar_icon. // But for simplicity, we are using the icon passed from the session params. This has been // fixed in API 19. if (mIcon != null) { Drawable iconDrawable = getDrawable(mIcon, false /*isFramework*/); if (iconDrawable != null) { mActionBar.setIcon(iconDrawable); } } private void setUpActionMenus() { if (mMenuIds == null) { if (mActionBarView != null) { // Set action bar to be split, if needed. mActionBarView.setSplitView((ActionBarContainer) findViewById(R.id.split_action_bar)); mActionBarView.setSplitActionBar(mSplit); inflateMenus(); } } /** * Gets the menus to add to the action bar from the callback, resolves them, inflates them and * adds them to the action bar. */ private void inflateMenus() { if (mActionBarView == null) { return; } ActionBarView actionBarView = (ActionBarView) findViewById(Bridge.getResourceId( ResourceType.ID, "action_bar")); final MenuInflater inflater = new MenuInflater(mActionBar.getThemedContext()); for (int id : mMenuIds) { final MenuInflater inflater = new MenuInflater(mThemedContext); for (String name : mCallback.getMenuIdNames()) { if (mBridgeContext.getRenderResources().getProjectResource(ResourceType.MENU, name) != null) { int id = mBridgeContext.getProjectResourceValue(ResourceType.MENU, name, -1); if (id > -1) { inflater.inflate(id, mMenuBuilder); } actionBarView.setMenu(mMenuBuilder, null /*callback*/); } } mActionBarView.setMenu(mMenuBuilder, null /*callback*/); } // TODO: Use an adapter, like List View to set up tabs. private void setUpTabs(int num) { private void setupTabs(int num) { for (int i = 1; i <= num; i++) { Tab tab = mActionBar.newTab() .setText("Tab" + i) .setTabListener(new TabListener() { Tab tab = mActionBar.newTab().setText("Tab" + i).setTabListener(new TabListener() { @Override public void onTabUnselected(Tab t, FragmentTransaction ft) { // pass Loading @@ -179,12 +214,141 @@ public class ActionBarLayout extends LinearLayout { } } private Drawable getDrawable(RenderResources res, String name, boolean isFramework) { @Nullable private Drawable getDrawable(String name, boolean isFramework) { ResourceValue value = res.findResValue(name, isFramework); value = res.resolveResValue(value); if (value != null) { return ResourceHelper.getDrawable(value, mContext); return ResourceHelper.getDrawable(value, mBridgeContext); } return null; } /** * Creates a Popup and adds it to the content frame. It also adds another {@link FrameLayout} to * the content frame which shall serve as the new content root. */ public void createMenuPopup() { assert mContentRoot != null && findViewById(android.R.id.content) == mContentRoot : "Action Bar Menus have already been created."; if (!isOverflowPopupNeeded()) { return; } // Create a layout to hold the menus and the user's content. RelativeLayout layout = new RelativeLayout(mThemedContext); layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); mContentRoot.addView(layout); // Create a layout for the user's content. FrameLayout contentRoot = new FrameLayout(mBridgeContext); contentRoot.setLayoutParams(new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); // Add contentRoot and menus to the layout. layout.addView(contentRoot); layout.addView(createMenuView()); // ContentRoot is now the view we just created. mContentRoot = contentRoot; } /** * Returns a {@link LinearLayout} containing the menu list view to be embedded in a * {@link RelativeLayout} */ private View createMenuView() { DisplayMetrics metrics = mBridgeContext.getMetrics(); OverflowMenuAdapter adapter = new OverflowMenuAdapter(mMenuBuilder, mThemedContext); LinearLayout layout = new LinearLayout(mThemedContext); RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams( measureContentWidth(adapter), LayoutParams.WRAP_CONTENT); layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); if (mSplit) { layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); // TODO: Find correct value instead of hardcoded 10dp. layoutParams.bottomMargin = getPixelValue("-10dp", metrics); } else { layoutParams.topMargin = getPixelValue("-10dp", metrics); } layout.setLayoutParams(layoutParams); final TypedArray a = mThemedContext.obtainStyledAttributes(null, R.styleable.PopupWindow, R.attr.popupMenuStyle, 0); layout.setBackground(a.getDrawable(R.styleable.PopupWindow_popupBackground)); layout.setDividerDrawable(a.getDrawable(R.attr.actionBarDivider)); a.recycle(); layout.setOrientation(LinearLayout.VERTICAL); layout.setDividerPadding(getPixelValue("12dp", metrics)); layout.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE); ListView listView = new ListView(mThemedContext, null, R.attr.dropDownListViewStyle); listView.setAdapter(adapter); layout.addView(listView); return layout; } private boolean isOverflowPopupNeeded() { boolean needed = mCallback.isOverflowPopupNeeded(); if (!needed) { return false; } // Copied from android.widget.ActionMenuPresenter.updateMenuView() ArrayList<MenuItemImpl> menus = MenuBuilderAccessor.getNonActionItems(mMenuBuilder); if (ActionBarAccessor.getActionMenuPresenter(mActionBarView).isOverflowReserved() && menus != null) { final int count = menus.size(); if (count == 1) { needed = !menus.get(0).isActionViewExpanded(); } else { needed = count > 0; } } return needed; } @Nullable public FrameLayout getContentRoot() { return mContentRoot; } // Copied from com.android.internal.view.menu.MenuPopHelper.measureContentWidth() private int measureContentWidth(ListAdapter adapter) { // Menus don't tend to be long, so this is more sane than it looks. int maxWidth = 0; View itemView = null; int itemType = 0; final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); final int count = adapter.getCount(); for (int i = 0; i < count; i++) { final int positionType = adapter.getItemViewType(i); if (positionType != itemType) { itemType = positionType; itemView = null; } if (mMeasureParent == null) { mMeasureParent = new FrameLayout(mThemedContext); } itemView = adapter.getView(i, itemView, mMeasureParent); itemView.measure(widthMeasureSpec, heightMeasureSpec); final int itemWidth = itemView.getMeasuredWidth(); if (itemWidth >= mPopupMaxWidth) { return mPopupMaxWidth; } else if (itemWidth > maxWidth) { maxWidth = itemWidth; } } return maxWidth; } private int getPixelValue(String value, DisplayMetrics metrics) { TypedValue typedValue = ResourceHelper.getValue(null, value, false /*requireUnit*/); return (int) typedValue.getDimension(metrics); } }
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/OverflowMenuAdapter.java 0 → 100644 +100 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.layoutlib.bridge.bars; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuBuilderAccessor; import com.android.internal.view.menu.MenuItemImpl; import com.android.internal.view.menu.MenuView; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import java.util.ArrayList; /** * Provides an adapter for Overflow menu popup. This is very similar to * {@code MenuPopupHelper.MenuAdapter} */ public class OverflowMenuAdapter extends BaseAdapter { private final MenuBuilder mMenu; private int mExpandedIndex = -1; private final Context context; public OverflowMenuAdapter(MenuBuilder menu, Context context) { mMenu = menu; findExpandedIndex(); this.context = context; } @Override public int getCount() { ArrayList<MenuItemImpl> items = MenuBuilderAccessor.getNonActionItems(mMenu); if (mExpandedIndex < 0) { return items.size(); } return items.size() - 1; } @Override public MenuItemImpl getItem(int position) { ArrayList<MenuItemImpl> items = MenuBuilderAccessor.getNonActionItems(mMenu); if (mExpandedIndex >= 0 && position >= mExpandedIndex) { position++; } return items.get(position); } @Override public long getItemId(int position) { // Since a menu item's ID is optional, we'll use the position as an // ID for the item in the AdapterView return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { LayoutInflater mInflater = LayoutInflater.from(context); convertView = mInflater.inflate(com.android.internal.R.layout.popup_menu_item_layout, parent, false); } MenuView.ItemView itemView = (MenuView.ItemView) convertView; itemView.initialize(getItem(position), 0); return convertView; } private void findExpandedIndex() { final MenuItemImpl expandedItem = mMenu.getExpandedItem(); if (expandedItem != null) { final ArrayList<MenuItemImpl> items = MenuBuilderAccessor.getNonActionItems(mMenu); final int count = items.size(); for (int i = 0; i < count; i++) { final MenuItemImpl item = items.get(i); if (item == expandedItem) { mExpandedIndex = i; return; } } } } }