Bug 878929: Optimize custom menu's list view. [r=mfinkle] [r=wesj]

--HG--
extra : rebase_source : 58caf961c4d7a0521224508b44a605c466968543
This commit is contained in:
Sriram Ramasubramanian 2013-06-05 15:28:02 -07:00
parent b938121333
commit 1238976040
16 changed files with 363 additions and 387 deletions

View File

@ -102,8 +102,6 @@ public class BrowserToolbar implements Tabs.OnTabsChangedListener,
private boolean mShowSiteSecurity;
private boolean mShowReader;
private static List<View> sActionItems;
private boolean mAnimatingEntry;
private AlphaAnimation mLockFadeIn;
@ -135,7 +133,6 @@ public class BrowserToolbar implements Tabs.OnTabsChangedListener,
// BrowserToolbar is attached to BrowserApp only.
mActivity = activity;
sActionItems = new ArrayList<View>();
Tabs.registerOnTabsChangedListener(this);
mSwitchingTabs = true;
@ -1114,20 +1111,11 @@ public class BrowserToolbar implements Tabs.OnTabsChangedListener,
@Override
public void addActionItem(View actionItem) {
mActionItemBar.addView(actionItem);
if (!sActionItems.contains(actionItem))
sActionItems.add(actionItem);
}
@Override
public void removeActionItem(int index) {
mActionItemBar.removeViewAt(index);
sActionItems.remove(index);
}
@Override
public int getActionItemsCount() {
return sActionItems.size();
public void removeActionItem(View actionItem) {
mActionItemBar.removeView(actionItem);
}
public void show() {

View File

@ -5,6 +5,7 @@
package org.mozilla.gecko;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.menu.GeckoMenu;
import org.mozilla.gecko.menu.MenuItemActionBar;
import org.mozilla.gecko.menu.MenuItemDefault;
import org.mozilla.gecko.widget.AboutHomeView;
@ -66,6 +67,7 @@ public final class GeckoViewsFactory implements LayoutInflater.Factory {
mFactoryMap.put("LinkTextView", LinkTextView.class.getConstructor(arg1Class, arg2Class));
mFactoryMap.put("MenuItemActionBar", MenuItemActionBar.class.getConstructor(arg1Class, arg2Class));
mFactoryMap.put("MenuItemDefault", MenuItemDefault.class.getConstructor(arg1Class, arg2Class));
mFactoryMap.put("GeckoMenu$DefaultActionItemBar", GeckoMenu.DefaultActionItemBar.class.getConstructor(arg1Class, arg2Class));
mFactoryMap.put("FindInPageBar", FindInPageBar.class.getConstructor(arg1Class, arg2Class));
mFactoryMap.put("IconTabWidget", IconTabWidget.class.getConstructor(arg1Class, arg2Class));
mFactoryMap.put("RemoteTabs", RemoteTabs.class.getConstructor(arg1Class, arg2Class));

View File

@ -1005,7 +1005,6 @@ RES_COLOR = \
res/color/abouthome_section_title.xml \
res/color/awesome_bar_title.xml \
res/color/awesome_bar_title_hint.xml \
res/color/menu_item_title.xml \
res/color/primary_text.xml \
res/color/primary_text_inverse.xml \
res/color/secondary_text.xml \

View File

@ -5,7 +5,6 @@
package org.mozilla.gecko.menu;
import org.mozilla.gecko.R;
import org.mozilla.gecko.widget.Divider;
import android.content.ComponentName;
import android.content.Context;
@ -25,18 +24,16 @@ import android.widget.LinearLayout;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GeckoMenu extends ListView
implements Menu,
MenuItem.OnMenuItemClickListener,
AdapterView.OnItemClickListener,
GeckoMenuItem.OnVisibilityChangedListener,
GeckoMenuItem.OnShowAsActionChangedListener {
private static final String LOGTAG = "GeckoMenu";
private Context mContext;
/*
* A callback for a menu item selected event.
*/
@ -65,17 +62,14 @@ public class GeckoMenu extends ListView
* An interface for a presenter of action-items.
* Either an Activity or a View can be a presenter, that can watch for events
* and add/remove action-items. If not ActionItemBarPresenter, the menu uses a
* DefaultActionItemBarPresenter, that shows the action-items as a header over list-view.
* DefaultActionItemBar, that shows the action-items as a header over list-view.
*/
public static interface ActionItemBarPresenter {
// Add an action-item.
public void addActionItem(View actionItem);
// Remove an action-item based on the index. The index used while adding.
public void removeActionItem(int index);
// Get the number of action-items shown by the presenter.
public int getActionItemsCount();
// Remove an action-item.
public void removeActionItem(View actionItem);
}
protected static final int NO_ID = 0;
@ -83,8 +77,8 @@ public class GeckoMenu extends ListView
// List of all menu items.
private List<GeckoMenuItem> mItems;
// List of items in action-bar.
private List<GeckoMenuItem> mActionItems;
// Map of items in action-bar and their views.
private Map<GeckoMenuItem, View> mActionItems;
// Reference to a callback for menu events.
private Callback mCallback;
@ -98,87 +92,86 @@ public class GeckoMenu extends ListView
// Adapter to hold the list of menu items.
private MenuItemsAdapter mAdapter;
// ActionBar to show the menu items as icons.
private LinearLayout mActionBar;
public GeckoMenu(Context context) {
this(context, null);
}
public GeckoMenu(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
this(context, attrs, android.R.attr.listViewStyle);
}
public GeckoMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));
// Add a header view that acts as an action-bar.
mActionBar = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.menu_action_bar, null);
// Attach an adapter.
mAdapter = new MenuItemsAdapter(context);
setAdapter(mAdapter);
setOnItemClickListener(this);
mItems = new ArrayList<GeckoMenuItem>();
mActionItems = new ArrayList<GeckoMenuItem>();
mActionItems = new HashMap<GeckoMenuItem, View>();
mActionItemBarPresenter = new DefaultActionItemBarPresenter(mContext, mActionBar);
mActionItemBarPresenter = (DefaultActionItemBar) LayoutInflater.from(context).inflate(R.layout.menu_action_bar, null);
}
@Override
public MenuItem add(CharSequence title) {
GeckoMenuItem menuItem = new GeckoMenuItem(mContext, NO_ID);
menuItem.setTitle(title);
GeckoMenuItem menuItem = new GeckoMenuItem(this, NO_ID, 0, title);
addItem(menuItem);
return menuItem;
}
@Override
public MenuItem add(int groupId, int itemId, int order, int titleRes) {
GeckoMenuItem menuItem = new GeckoMenuItem(mContext, itemId, order);
menuItem.setTitle(titleRes);
GeckoMenuItem menuItem = new GeckoMenuItem(this, itemId, order, titleRes);
addItem(menuItem);
return menuItem;
}
@Override
public MenuItem add(int titleRes) {
GeckoMenuItem menuItem = new GeckoMenuItem(mContext, NO_ID);
menuItem.setTitle(titleRes);
GeckoMenuItem menuItem = new GeckoMenuItem(this, NO_ID, 0, titleRes);
addItem(menuItem);
return menuItem;
}
@Override
public MenuItem add(int groupId, int itemId, int order, CharSequence title) {
GeckoMenuItem menuItem = new GeckoMenuItem(mContext, itemId, order);
menuItem.setTitle(title);
GeckoMenuItem menuItem = new GeckoMenuItem(this, itemId, order, title);
addItem(menuItem);
return menuItem;
}
private void addItem(GeckoMenuItem menuItem) {
menuItem.setMenu(this);
menuItem.setOnShowAsActionChangedListener(this);
menuItem.setOnVisibilityChangedListener(this);
menuItem.setOnMenuItemClickListener(this);
mAdapter.addMenuItem(menuItem);
mItems.add(menuItem);
}
private void addActionItem(GeckoMenuItem menuItem) {
menuItem.setMenu(this);
private void addActionItem(final GeckoMenuItem menuItem) {
menuItem.setOnShowAsActionChangedListener(this);
menuItem.setOnVisibilityChangedListener(null);
menuItem.setOnMenuItemClickListener(this);
if (mActionItems.size() == 0 &&
mActionItemBarPresenter instanceof DefaultActionItemBarPresenter) {
mActionItemBarPresenter instanceof DefaultActionItemBar) {
// Reset the adapter before adding the header view to a list.
setAdapter(null);
addHeaderView(mActionBar);
addHeaderView((DefaultActionItemBar) mActionItemBarPresenter);
setAdapter(mAdapter);
}
mActionItems.add(menuItem);
mActionItemBarPresenter.addActionItem(menuItem.getLayout());
View actionView = menuItem.getActionView();
actionView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
handleMenuItemClick(menuItem);
}
});
mActionItems.put(menuItem, actionView);
mActionItemBarPresenter.addActionItem(actionView);
mItems.add(menuItem);
}
@ -190,40 +183,29 @@ public class GeckoMenu extends ListView
@Override
public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) {
MenuItem menuItem = add(groupId, itemId, order, title);
GeckoSubMenu subMenu = new GeckoSubMenu(mContext, null);
subMenu.setMenuItem(menuItem);
subMenu.setCallback(mCallback);
subMenu.setMenuPresenter(mMenuPresenter);
((GeckoMenuItem) menuItem).setSubMenu(subMenu);
return subMenu;
return addSubMenu(menuItem);
}
@Override
public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) {
MenuItem menuItem = add(groupId, itemId, order, titleRes);
GeckoSubMenu subMenu = new GeckoSubMenu(mContext, null);
subMenu.setMenuItem(menuItem);
subMenu.setCallback(mCallback);
subMenu.setMenuPresenter(mMenuPresenter);
((GeckoMenuItem) menuItem).setSubMenu(subMenu);
return subMenu;
return addSubMenu(menuItem);
}
@Override
public SubMenu addSubMenu(CharSequence title) {
MenuItem menuItem = add(title);
GeckoSubMenu subMenu = new GeckoSubMenu(mContext, null);
subMenu.setMenuItem(menuItem);
subMenu.setCallback(mCallback);
subMenu.setMenuPresenter(mMenuPresenter);
((GeckoMenuItem) menuItem).setSubMenu(subMenu);
return subMenu;
return addSubMenu(menuItem);
}
@Override
public SubMenu addSubMenu(int titleRes) {
MenuItem menuItem = add(titleRes);
GeckoSubMenu subMenu = new GeckoSubMenu(mContext, null);
return addSubMenu(menuItem);
}
private SubMenu addSubMenu(MenuItem menuItem) {
GeckoSubMenu subMenu = new GeckoSubMenu(getContext());
subMenu.setMenuItem(menuItem);
subMenu.setCallback(mCallback);
subMenu.setMenuPresenter(mMenuPresenter);
@ -235,8 +217,7 @@ public class GeckoMenu extends ListView
public void clear() {
for (GeckoMenuItem menuItem : mItems) {
if (menuItem.hasSubMenu()) {
SubMenu subMenu = menuItem.getSubMenu();
subMenu.clear();
menuItem.getSubMenu().clear();
}
}
@ -252,6 +233,11 @@ public class GeckoMenu extends ListView
mMenuPresenter.closeMenu();
}
private void showMenu(View viewForMenu) {
if (mMenuPresenter != null)
mMenuPresenter.showMenu(viewForMenu);
}
@Override
public MenuItem findItem(int id) {
for (GeckoMenuItem menuItem : mItems) {
@ -309,19 +295,21 @@ public class GeckoMenu extends ListView
@Override
public void removeItem(int id) {
GeckoMenuItem item = (GeckoMenuItem) findItem(id);
if (item != null) {
if (mActionItems.contains(item)) {
if (item == null)
return;
if (mActionItems.containsKey(item)) {
if (mActionItemBarPresenter != null)
mActionItemBarPresenter.removeActionItem(mActionItems.indexOf(item));
mActionItemBarPresenter.removeActionItem(mActionItems.get(item));
mActionItems.remove(item);
mItems.remove(item);
if (mActionItems.size() == 0 &&
mActionItemBarPresenter instanceof DefaultActionItemBarPresenter) {
mActionItemBarPresenter instanceof DefaultActionItemBar) {
// Reset the adapter before removing the header view from a list.
setAdapter(null);
removeHeaderView(mActionBar);
removeHeaderView((DefaultActionItemBar) mActionItemBarPresenter);
setAdapter(mAdapter);
}
@ -331,7 +319,6 @@ public class GeckoMenu extends ListView
mAdapter.removeMenuItem(item);
mItems.remove(item);
}
}
@Override
public void setGroupCheckable(int group, boolean checkable, boolean exclusive) {
@ -369,53 +356,63 @@ public class GeckoMenu extends ListView
addItem(item);
}
public void onItemChanged(GeckoMenuItem item) {
if (item.isActionItem()) {
final MenuItemActionBar actionView = (MenuItemActionBar) mActionItems.get(item);
if (actionView != null) {
// The update could be coming from the background thread.
// Post a runnable on the UI thread of the view for it to update.
final GeckoMenuItem menuItem = item;
actionView.post(new Runnable() {
@Override
public void onVisibilityChanged(GeckoMenuItem item, boolean isVisible) {
if (isVisible)
mAdapter.addMenuItem(item);
else
mAdapter.removeMenuItem(item);
public void run() {
if (menuItem.isVisible()) {
actionView.setVisibility(View.VISIBLE);
actionView.initialize(menuItem);
} else {
actionView.setVisibility(View.GONE);
}
}
});
}
} else {
mAdapter.notifyDataSetChanged();
}
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// We might be showing headers. Account them while using the position.
position -= getHeaderViewsCount();
GeckoMenuItem item = mAdapter.getItem(position);
if (item.isEnabled())
item.onClick(item.getLayout());
handleMenuItemClick(item);
}
@Override
public boolean onMenuItemClick(MenuItem item) {
if (!item.hasSubMenu()) {
if (mMenuPresenter != null)
mMenuPresenter.closeMenu();
private void handleMenuItemClick(GeckoMenuItem item) {
if (!item.isEnabled())
return;
return mCallback.onMenuItemSelected(item);
} else {
if (item.invoke()) {
close();
} else if (item.hasSubMenu()) {
// Refresh the submenu for the provider.
ActionProvider provider = item.getActionProvider();
if (provider != null) {
GeckoSubMenu subMenu = new GeckoSubMenu(mContext, null);
GeckoSubMenu subMenu = new GeckoSubMenu(getContext());
provider.onPrepareSubMenu(subMenu);
((GeckoMenuItem) item).setSubMenu(subMenu);
item.setSubMenu(subMenu);
}
// Show the submenu.
if (mMenuPresenter != null)
mMenuPresenter.showMenu((GeckoSubMenu) item.getSubMenu());
return true;
GeckoSubMenu subMenu = (GeckoSubMenu) item.getSubMenu();
showMenu(subMenu);
} else {
close();
mCallback.onMenuItemSelected(item);
}
}
public boolean onCustomMenuItemClick(MenuItem item, MenuItem.OnMenuItemClickListener listener) {
if (mMenuPresenter != null)
mMenuPresenter.closeMenu();
return listener.onMenuItemClick(item);
}
public Callback getCallback() {
return mCallback;
}
@ -454,15 +451,14 @@ public class GeckoMenu extends ListView
// Action Items are added to the header view by default.
// URL bar can register itself as a presenter, in case it has a different place to show them.
private class DefaultActionItemBarPresenter implements ActionItemBarPresenter {
private Context mContext;
private LinearLayout mContainer;
private List<View> mItems;
public static class DefaultActionItemBar extends LinearLayout
implements ActionItemBarPresenter {
public DefaultActionItemBar(Context context) {
super(context);
}
public DefaultActionItemBarPresenter(Context context, LinearLayout container) {
mContext = context;
mContainer = container;
mItems = new ArrayList<View>();
public DefaultActionItemBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
@ -470,56 +466,52 @@ public class GeckoMenu extends ListView
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(actionItem.getLayoutParams());
params.weight = 1.0f;
actionItem.setLayoutParams(params);
if (mItems.size() > 0) {
Divider divider = new Divider(mContext, null);
divider.setOrientation(Divider.Orientation.VERTICAL);
divider.setBackgroundColor(0xFFD1D5DA);
mContainer.addView(divider);
}
mContainer.addView(actionItem);
mItems.add(actionItem);
addView(actionItem);
}
@Override
public void removeActionItem(int index) {
// Remove the icon and the vertical divider.
mContainer.removeViewAt(index * 2);
if (index != 0)
mContainer.removeViewAt(index * 2 - 1);
mItems.remove(index);
if (mItems.size() == 0)
mContainer.setVisibility(View.GONE);
}
@Override
public int getActionItemsCount() {
return mItems.size();
public void removeActionItem(View actionItem) {
removeView(actionItem);
}
}
// Adapter to bind menu items to the list.
private class MenuItemsAdapter extends BaseAdapter {
private Context mContext;
private static final int VIEW_TYPE_DEFAULT = 0;
private static final int VIEW_TYPE_ACTION_MODE = 1;
private List<GeckoMenuItem> mItems;
public MenuItemsAdapter(Context context) {
mContext = context;
mItems = new ArrayList<GeckoMenuItem>();
}
@Override
public int getCount() {
return (mItems == null ? 0 : mItems.size());
if (mItems == null)
return 0;
int visibleCount = 0;
for (GeckoMenuItem item : mItems) {
if (item.isVisible())
visibleCount++;
}
return visibleCount;
}
@Override
public GeckoMenuItem getItem(int position) {
return mItems.get(position);
for (GeckoMenuItem item : mItems) {
if (item.isVisible()) {
position--;
if (position < 0)
return item;
}
}
return null;
}
@Override
@ -529,12 +521,53 @@ public class GeckoMenu extends ListView
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return mItems.get(position).getLayout();
GeckoMenuItem item = getItem(position);
GeckoMenuItem.Layout view = null;
// Try to re-use the view.
if (convertView == null && getItemViewType(position) == VIEW_TYPE_DEFAULT) {
view = new MenuItemDefault(parent.getContext(), null);
} else {
view = (GeckoMenuItem.Layout) convertView;
}
if (view == null || view instanceof MenuItemActionView) {
// Always get from the menu item.
// This will ensure that the default activity is refreshed.
view = (MenuItemActionView) item.getActionView();
// ListView will not perform an item click if the row has a focusable view in it.
// Hence, forward the click event on the menu item in the action-view to the ListView.
final View actionView = (View) view;
final int pos = position;
final long id = getItemId(position);
((MenuItemActionView) view).setMenuItemClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
GeckoMenu listView = GeckoMenu.this;
listView.performItemClick(actionView, pos + listView.getHeaderViewsCount(), id);
}
});
}
// Initialize the view.
view.initialize(item);
return (View) view;
}
@Override
public int getItemViewType (int position) {
return AdapterView.ITEM_VIEW_TYPE_IGNORE;
public int getItemViewType(int position) {
return getItem(position).getActionProvider() == null ? VIEW_TYPE_DEFAULT : VIEW_TYPE_ACTION_MODE;
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override

View File

@ -6,32 +6,21 @@ package org.mozilla.gecko.menu;
import org.mozilla.gecko.widget.GeckoActionProvider;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.ActionProvider;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
public class GeckoMenuItem implements MenuItem, View.OnClickListener {
public class GeckoMenuItem implements MenuItem {
private static final String LOGTAG = "GeckoMenuItem";
// A View that can show a MenuItem should be able to initialize from
// the properties of the MenuItem.
public static interface Layout {
public void setId(int id);
public void setIcon(Drawable icon);
public void setIcon(int iconRes);
public void setTitle(CharSequence title);
public void setEnabled(boolean enabled);
public void setCheckable(boolean checkable);
public void setChecked(boolean checked);
public void setOnClickListener(View.OnClickListener listener);
public void setSubMenuIndicator(boolean hasSubMenu);
public void setVisibility(int visible);
public View getView();
public void initialize(GeckoMenuItem item);
}
public static interface OnShowAsActionChangedListener {
@ -39,48 +28,36 @@ public class GeckoMenuItem implements MenuItem, View.OnClickListener {
public void onShowAsActionChanged(GeckoMenuItem item, boolean isActionItem);
}
public static interface OnVisibilityChangedListener {
public void onVisibilityChanged(GeckoMenuItem item, boolean isVisible);
}
private Context mContext;
private int mId;
private int mOrder;
private Layout mLayout;
private boolean mActionItem;
private View mActionView;
private boolean mActionItem = false;
private CharSequence mTitle;
private CharSequence mTitleCondensed;
private boolean mCheckable;
private boolean mChecked;
private boolean mVisible;
private boolean mEnabled;
private boolean mCheckable = false;
private boolean mChecked = false;
private boolean mVisible = true;
private boolean mEnabled = true;
private Drawable mIcon;
private int mIconRes;
private ActionProvider mActionProvider;
private GeckoMenu mMenu;
private GeckoSubMenu mSubMenu;
private MenuItem.OnMenuItemClickListener mMenuItemClickListener;
private OnVisibilityChangedListener mVisibilityChangedListener;
private MenuItem.OnMenuItemClickListener mMenuItemClickListener = null;
private OnShowAsActionChangedListener mShowAsActionChangedListener;
public GeckoMenuItem(Context context, int id) {
mContext = context;
mLayout = new MenuItemDefault(context, null);
mLayout.setId(id);
public GeckoMenuItem(GeckoMenu menu, int id, int order, int titleRes) {
mMenu = menu;
mId = id;
mOrder = 0;
mActionItem = false;
mVisible = true;
mEnabled = true;
mCheckable = true;
mChecked = false;
mMenuItemClickListener = null;
mOrder = order;
setTitle(titleRes);
}
public GeckoMenuItem(Context context, int id, int order) {
this(context, id);
public GeckoMenuItem(GeckoMenu menu, int id, int order, CharSequence title) {
mMenu = menu;
mId = id;
mOrder = order;
setTitle(title);
}
@Override
@ -101,12 +78,10 @@ public class GeckoMenuItem implements MenuItem, View.OnClickListener {
@Override
public View getActionView() {
if (mActionProvider != null && mActionProvider instanceof GeckoActionProvider) {
final View view = ((GeckoActionProvider) mActionProvider).getView(this);
view.setOnClickListener(this);
return view;
return ((GeckoActionProvider) mActionProvider).getView();
}
return null;
return mActionView;
}
@Override
@ -123,7 +98,7 @@ public class GeckoMenuItem implements MenuItem, View.OnClickListener {
public Drawable getIcon() {
if (mIcon == null) {
if (mIconRes != 0)
return mContext.getResources().getDrawable(mIconRes);
return mMenu.getResources().getDrawable(mIconRes);
else
return null;
} else {
@ -141,17 +116,6 @@ public class GeckoMenuItem implements MenuItem, View.OnClickListener {
return mId;
}
public View getLayout() {
if (mActionProvider != null)
return getActionView();
return mLayout.getView();
}
public void setMenu(GeckoMenu menu) {
mMenu = menu;
}
@Override
public ContextMenu.ContextMenuInfo getMenuInfo() {
return null;
@ -190,6 +154,10 @@ public class GeckoMenuItem implements MenuItem, View.OnClickListener {
return (mSubMenu != null);
}
public boolean isActionItem() {
return mActionItem;
}
@Override
public boolean isActionViewExpanded() {
return false;
@ -249,35 +217,35 @@ public class GeckoMenuItem implements MenuItem, View.OnClickListener {
@Override
public MenuItem setCheckable(boolean checkable) {
mCheckable = checkable;
mLayout.setCheckable(checkable);
mMenu.onItemChanged(this);
return this;
}
@Override
public MenuItem setChecked(boolean checked) {
mChecked = checked;
mLayout.setChecked(checked);
mMenu.onItemChanged(this);
return this;
}
@Override
public MenuItem setEnabled(boolean enabled) {
mEnabled = enabled;
mLayout.setEnabled(enabled);
mMenu.onItemChanged(this);
return this;
}
@Override
public MenuItem setIcon(Drawable icon) {
mIcon = icon;
mLayout.setIcon(icon);
mMenu.onItemChanged(this);
return this;
}
@Override
public MenuItem setIcon(int iconRes) {
mIconRes = iconRes;
mLayout.setIcon(iconRes);
mMenu.onItemChanged(this);
return this;
}
@ -320,27 +288,12 @@ public class GeckoMenuItem implements MenuItem, View.OnClickListener {
return;
// Change the type to just an icon
mLayout = new MenuItemActionBar(mContext, null);
} else {
// Change the type to default
mLayout = new MenuItemDefault(mContext, null);
}
MenuItemActionBar actionView = new MenuItemActionBar(mMenu.getContext(), null);
actionView.initialize(this);
mActionView = actionView;
mActionItem = (actionEnum > 0);
mLayout.setId(mId);
mLayout.setOnClickListener(this);
setTitle(mTitle);
setVisible(mVisible);
setEnabled(mEnabled);
setCheckable(mCheckable);
setChecked(mChecked);
if (mIcon == null)
setIcon(mIconRes);
else
setIcon(mIcon);
}
mShowAsActionChangedListener.onShowAsActionChanged(this, mActionItem);
}
@ -352,21 +305,20 @@ public class GeckoMenuItem implements MenuItem, View.OnClickListener {
public MenuItem setSubMenu(GeckoSubMenu subMenu) {
mSubMenu = subMenu;
mLayout.setSubMenuIndicator(mActionProvider == null && subMenu != null);
return this;
}
@Override
public MenuItem setTitle(CharSequence title) {
mTitle = title;
mLayout.setTitle(mTitle);
mMenu.onItemChanged(this);
return this;
}
@Override
public MenuItem setTitle(int title) {
mTitle = mContext.getResources().getString(title);
mLayout.setTitle(mTitle);
mTitle = mMenu.getResources().getString(title);
mMenu.onItemChanged(this);
return this;
}
@ -379,30 +331,18 @@ public class GeckoMenuItem implements MenuItem, View.OnClickListener {
@Override
public MenuItem setVisible(boolean visible) {
mVisible = visible;
mLayout.setVisibility(visible ? View.VISIBLE : View.GONE);
if (mVisibilityChangedListener != null)
mVisibilityChangedListener.onVisibilityChanged(this, visible);
mMenu.onItemChanged(this);
return this;
}
@Override
public void onClick(View view) {
// If there is a custom listener, pass it to parent menu, so that it can do default cleanups.
if (mMenuItemClickListener != null) {
if (mMenuItemClickListener instanceof GeckoMenu)
mMenuItemClickListener.onMenuItemClick(this);
public boolean invoke() {
if (mMenuItemClickListener != null)
return mMenuItemClickListener.onMenuItemClick(this);
else
mMenu.onCustomMenuItemClick(this, mMenuItemClickListener);
}
return false;
}
public void setOnShowAsActionChangedListener(OnShowAsActionChangedListener listener) {
mShowAsActionChangedListener = listener;
}
public void setOnVisibilityChangedListener(OnVisibilityChangedListener listener) {
mVisibilityChangedListener = listener;
}
}

View File

@ -15,14 +15,19 @@ public class GeckoSubMenu extends GeckoMenu
implements SubMenu {
private static final String LOGTAG = "GeckoSubMenu";
private Context mContext;
// MenuItem associated with this submenu.
private MenuItem mMenuItem;
public GeckoSubMenu(Context context) {
super(context);
}
public GeckoSubMenu(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
}
public GeckoSubMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override

View File

@ -9,34 +9,39 @@ import org.mozilla.gecko.R;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
public class MenuItemActionBar extends ImageButton
implements GeckoMenuItem.Layout {
private static final String LOGTAG = "GeckoMenuItemActionBar";
public MenuItemActionBar(Context context) {
this(context, null);
}
public MenuItemActionBar(Context context, AttributeSet attrs) {
super(context, attrs);
this(context, attrs, R.attr.menuItemActionBarStyle);
}
public MenuItemActionBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
int size = (int) (context.getResources().getDimension(R.dimen.browser_toolbar_height));
setLayoutParams(new ViewGroup.LayoutParams(size, size));
int padding = (int) (context.getResources().getDimension(R.dimen.browser_toolbar_button_padding));
setPadding(padding, padding, padding, padding);
setBackgroundResource(R.drawable.action_bar_button);
setScaleType(ImageView.ScaleType.FIT_CENTER);
}
@Override
public View getView() {
return this;
public void initialize(GeckoMenuItem item) {
if (item == null)
return;
setIcon(item.getIcon());
setTitle(item.getTitle());
setEnabled(item.isEnabled());
}
@Override
public void setIcon(Drawable icon) {
private void setIcon(Drawable icon) {
if (icon != null) {
setImageDrawable(icon);
setVisibility(VISIBLE);
@ -45,8 +50,7 @@ public class MenuItemActionBar extends ImageButton
}
}
@Override
public void setIcon(int icon) {
private void setIcon(int icon) {
if (icon != 0) {
setImageResource(icon);
setVisibility(VISIBLE);
@ -55,8 +59,7 @@ public class MenuItemActionBar extends ImageButton
}
}
@Override
public void setTitle(CharSequence title) {
private void setTitle(CharSequence title) {
// set accessibility contentDescription here
setContentDescription(title);
}
@ -66,16 +69,4 @@ public class MenuItemActionBar extends ImageButton
super.setEnabled(enabled);
setColorFilter(enabled ? 0 : 0xFF999999);
}
@Override
public void setCheckable(boolean checkable) {
}
@Override
public void setChecked(boolean checked) {
}
@Override
public void setSubMenuIndicator(boolean hasSubMenu) {
}
}

View File

@ -7,14 +7,13 @@ package org.mozilla.gecko.menu;
import org.mozilla.gecko.R;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ImageButton;
@ -25,8 +24,17 @@ public class MenuItemActionView extends LinearLayout
private MenuItemDefault mMenuItem;
private ImageButton mActionButton;
public MenuItemActionView(Context context) {
this(context, null);
}
public MenuItemActionView(Context context, AttributeSet attrs) {
super(context, attrs);
this(context, attrs, R.attr.menuItemActionViewStyle);
}
@TargetApi(11)
public MenuItemActionView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
Resources res = context.getResources();
int width = res.getDimensionPixelSize(R.dimen.menu_item_row_width);
@ -34,34 +42,30 @@ public class MenuItemActionView extends LinearLayout
setMinimumWidth(width);
setMinimumHeight(height);
setShowDividers(SHOW_DIVIDER_MIDDLE);
setDividerDrawable(res.getDrawable(R.drawable.divider_vertical));
setDividerPadding((int) (8 * res.getDisplayMetrics().density));
LayoutInflater.from(context).inflate(R.layout.menu_item_action_view, this);
mMenuItem = (MenuItemDefault) findViewById(R.id.menu_item);
mActionButton = (ImageButton) findViewById(R.id.action_button);
mMenuItem.setBackgroundResource(R.drawable.action_bar_button);
}
@Override
public View getView() {
return this;
public void initialize(GeckoMenuItem item) {
if (item == null)
return;
setTitle(item.getTitle());
setIcon(item.getIcon());
setEnabled(item.isEnabled());
}
@Override
public void setIcon(Drawable icon) {
private void setIcon(Drawable icon) {
mMenuItem.setIcon(icon);
}
@Override
public void setIcon(int icon) {
private void setIcon(int icon) {
mMenuItem.setIcon(icon);
}
@Override
public void setTitle(CharSequence title) {
private void setTitle(CharSequence title) {
mMenuItem.setTitle(title);
}
@ -76,16 +80,8 @@ public class MenuItemActionView extends LinearLayout
}
}
@Override
public void setCheckable(boolean checkable) {
}
@Override
public void setChecked(boolean checked) {
}
@Override
public void setSubMenuIndicator(boolean hasSubMenu) {
public void setMenuItemClickListener(View.OnClickListener listener) {
mMenuItem.setOnClickListener(listener);
}
public void setActionButtonClickListener(View.OnClickListener listener) {

View File

@ -11,9 +11,6 @@ import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
public class MenuItemDefault extends TextView
@ -35,25 +32,17 @@ public class MenuItemDefault extends TextView
}
public MenuItemDefault(Context context, AttributeSet attrs) {
this(context, attrs, R.style.MenuItemDefault);
this(context, attrs, R.attr.menuItemDefaultStyle);
}
public MenuItemDefault(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
Resources res = context.getResources();
Resources res = getResources();
int width = res.getDimensionPixelSize(R.dimen.menu_item_row_width);
int height = res.getDimensionPixelSize(R.dimen.menu_item_row_height);
setMinimumWidth(width);
setMinimumHeight(height);
setGravity(Gravity.CENTER_VERTICAL);
float density = res.getDisplayMetrics().density;
int padding = (int) (10 * density);
setPadding(padding, 0, padding, 0);
int drawablePadding = (int) (6 * density);
setCompoundDrawablePadding(drawablePadding);
int stateIconSize = res.getDimensionPixelSize(R.dimen.menu_item_state_icon);
Rect stateIconBounds = new Rect(0, 0, stateIconSize, stateIconSize);
@ -84,32 +73,39 @@ public class MenuItemDefault extends TextView
}
@Override
public View getView() {
return this;
public void initialize(GeckoMenuItem item) {
if (item == null)
return;
setTitle(item.getTitle());
setIcon(item.getIcon());
setEnabled(item.isEnabled());
setCheckable(item.isCheckable());
setChecked(item.isChecked());
setSubMenuIndicator(item.hasSubMenu());
}
@Override
public void setIcon(Drawable icon) {
void setIcon(Drawable icon) {
mIcon = icon;
if (mIcon != null)
if (mIcon != null) {
mIcon.setBounds(sIconBounds);
mIcon.setAlpha(isEnabled() ? 255 : 99);
}
setCompoundDrawables(mIcon, null, mState, null);
}
@Override
public void setIcon(int icon) {
void setIcon(int icon) {
Drawable drawable = null;
if (icon != 0)
drawable = getContext().getResources().getDrawable(icon);
drawable = getResources().getDrawable(icon);
setIcon(drawable);
}
@Override
public void setTitle(CharSequence title) {
void setTitle(CharSequence title) {
setText(title);
}
@ -124,24 +120,21 @@ public class MenuItemDefault extends TextView
mState.setAlpha(enabled ? 255 : 99);
}
@Override
public void setCheckable(boolean checkable) {
private void setCheckable(boolean checkable) {
if (mCheckable != checkable) {
mCheckable = checkable;
refreshDrawableState();
}
}
@Override
public void setChecked(boolean checked) {
private void setChecked(boolean checked) {
if (mChecked != checked) {
mChecked = checked;
refreshDrawableState();
}
}
@Override
public void setSubMenuIndicator(boolean hasSubMenu) {
private void setSubMenuIndicator(boolean hasSubMenu) {
if (mHasSubMenu != hasSubMenu) {
mHasSubMenu = hasSubMenu;
refreshDrawableState();

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="#999999" />
<item android:color="#222222"/>
</selector>

View File

@ -1,5 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!--
Note: This layout is intended to be used only above 11+.
android:showDividers are available only 11+
-->
<view xmlns:android="http://schemas.android.com/apk/res/android"
class="org.mozilla.gecko.menu.GeckoMenu$DefaultActionItemBar"
android:layout_width="@dimen/menu_item_row_width"
android:layout_height="@dimen/browser_toolbar_height"
android:orientation="horizontal"/>
android:orientation="horizontal"
android:divider="@drawable/divider_vertical"
android:showDividers="middle"
android:dividerPadding="0dip"/>

View File

@ -9,7 +9,10 @@
android:id="@+id/menu_item"
android:layout_width="0dip"
android:layout_height="fill_parent"
android:layout_weight="1.0"/>
android:layout_weight="1.0"
android:background="@drawable/action_bar_button"
android:clickable="true"
android:focusable="true"/>
<ImageButton android:id="@+id/action_button"
android:layout_width="@dimen/menu_item_action_icon"

View File

@ -44,6 +44,9 @@
<item name="android:listViewStyle">@style/Widget.ListView</item>
<item name="android:spinnerStyle">@style/Widget.Spinner</item>
<item name="android:spinnerItemStyle">@style/Widget.TextView.SpinnerItem</item>
<item name="menuItemActionBarStyle">@style/Widget.MenuItemActionBar</item>
<item name="menuItemActionViewStyle">@style/Widget.MenuItemActionView</item>
<item name="menuItemDefaultStyle">@style/Widget.MenuItemDefault</item>
</style>
</resources>

View File

@ -5,6 +5,20 @@
<resources>
<!-- Theme level attributes -->
<declare-styleable name="GeckoTheme">
<!-- Style for MenuItemActionBar -->
<attr name="menuItemActionBarStyle" format="reference"/>
<!-- Style for MenuItemActionView -->
<attr name="menuItemActionViewStyle" format="reference"/>
<!-- Style for MenuItemDefault -->
<attr name="menuItemDefaultStyle" format="reference"/>
</declare-styleable>
<declare-styleable name="AboutHomeSection">
<attr name="title" format="string"/>
<attr name="subtitle" format="string"/>

View File

@ -71,6 +71,28 @@
<style name="Widget.TextView.SpinnerItem" parent="android:style/Widget.TextView.SpinnerItem"/>
<style name="Widget.MenuItemActionBar">
<item name="android:padding">@dimen/browser_toolbar_button_padding</item>
<item name="android:background">@drawable/action_bar_button</item>
<item name="android:scaleType">fitCenter</item>
</style>
<style name="Widget.MenuItemActionView">
<item name="android:divider">@drawable/divider_vertical</item>
<item name="android:showDividers">middle</item>
<item name="android:dividerPadding">8dip</item>
</style>
<style name="Widget.MenuItemDefault">
<item name="android:paddingLeft">10dip</item>
<item name="android:paddingRight">10dip</item>
<item name="android:drawablePadding">6dip</item>
<item name="android:gravity">center_vertical</item>
<item name="android:textAppearance">@style/TextAppearance</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">middle</item>
</style>
<!--
TextAppearance
Note: Gecko uses light theme as default, while Android uses dark.
@ -440,11 +462,4 @@
<item name="@android:windowExitAnimation">@anim/popup_hide</item>
</style>
<style name="MenuItemDefault">
<item name="android:textSize">16sp</item>
<item name="android:textColor">@color/menu_item_title</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">middle</item>
</style>
</resources>

View File

@ -60,19 +60,8 @@ public class GeckoActionProvider extends ActionProvider {
return view;
}
@Override
public View onCreateActionView(MenuItem item) {
MenuItemActionView view = (MenuItemActionView) onCreateActionView();
view.setId(item.getItemId());
view.setTitle(item.getTitle());
view.setIcon(item.getIcon());
view.setVisibility(item.isVisible() ? View.VISIBLE : View.GONE);
view.setEnabled(item.isEnabled());
return view;
}
public View getView(MenuItem item) {
return onCreateActionView(item);
public View getView() {
return onCreateActionView();
}
@Override