Loading core/java/android/widget/DropDownListView.java +0 −5 Original line number Diff line number Diff line Loading @@ -132,11 +132,6 @@ public class DropDownListView extends ListView { return selectedView != null && selectedView.isEnabled() || super.shouldShowSelector(); } protected void clearSelection() { setSelectedPositionInt(-1); setNextSelectedPositionInt(-1); } @Override public boolean onHoverEvent(MotionEvent ev) { final int action = ev.getActionMasked(); Loading core/java/android/widget/MenuItemHoverListener.java 0 → 100644 +13 −0 Original line number Diff line number Diff line package android.widget; import com.android.internal.view.menu.MenuBuilder; /** * An interface notified when a menu item is hovered. Useful for cases when hover should trigger * some behavior at a higher level, like managing the opening and closing of submenus. * * @hide */ public interface MenuItemHoverListener { public void onItemHovered(MenuBuilder menu, int position); } core/java/android/widget/MenuPopupWindow.java +65 −5 Original line number Diff line number Diff line Loading @@ -22,12 +22,12 @@ import android.content.res.Resources; import android.transition.Transition; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import com.android.internal.view.menu.ListMenuItemView; import com.android.internal.view.menu.MenuAdapter; import com.android.internal.view.menu.MenuBuilder; /** * A MenuPopupWindow represents the popup window for menu. Loading @@ -37,20 +37,32 @@ import com.android.internal.view.menu.MenuAdapter; * * @hide */ public class MenuPopupWindow extends ListPopupWindow { public class MenuPopupWindow extends ListPopupWindow implements MenuItemHoverListener { private MenuItemHoverListener mHoverListener; public MenuPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override DropDownListView createDropDownListView(Context context, boolean hijackFocus) { return new MenuDropDownListView(context, hijackFocus); MenuDropDownListView view = new MenuDropDownListView(context, hijackFocus); view.setHoverListener(this); return view; } public void setEnterTransition(Transition enterTransition) { mPopup.setEnterTransition(enterTransition); } public void setExitTransition(Transition exitTransition) { mPopup.setExitTransition(exitTransition); } public void setHoverListener(MenuItemHoverListener hoverListener) { mHoverListener = hoverListener; } /** * Set whether this window is touch modal or if outside touches will be sent to * other windows behind it. Loading @@ -59,10 +71,23 @@ public class MenuPopupWindow extends ListPopupWindow { mPopup.setTouchModal(touchModal); } private static class MenuDropDownListView extends DropDownListView { @Override public void onItemHovered(MenuBuilder menu, int position) { // Forward up the chain if (mHoverListener != null) { mHoverListener.onItemHovered(menu, position); } } /** * @hide */ public static class MenuDropDownListView extends DropDownListView { final int mAdvanceKey; final int mRetreatKey; private MenuItemHoverListener mHoverListener; public MenuDropDownListView(Context context, boolean hijackFocus) { super(context, hijackFocus); Loading @@ -77,6 +102,15 @@ public class MenuPopupWindow extends ListPopupWindow { } } public void setHoverListener(MenuItemHoverListener hoverListener) { mHoverListener = hoverListener; } public void clearSelection() { setSelectedPositionInt(INVALID_POSITION); setNextSelectedPositionInt(INVALID_POSITION); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { ListMenuItemView selectedItem = (ListMenuItemView) getSelectedView(); Loading @@ -99,6 +133,32 @@ public class MenuPopupWindow extends ListPopupWindow { return super.onKeyDown(keyCode, event); } @Override public boolean onHoverEvent(MotionEvent ev) { boolean dispatchHover = false; final int position = pointToPosition((int) ev.getX(), (int) ev.getY()); final int action = ev.getActionMasked(); if (action == MotionEvent.ACTION_HOVER_ENTER || action == MotionEvent.ACTION_HOVER_MOVE) { if (position != INVALID_POSITION && position != mSelectedPosition) { final View hoveredItem = getChildAt(position - getFirstVisiblePosition()); if (hoveredItem.isEnabled()) { dispatchHover = true; } } } boolean superVal = super.onHoverEvent(ev); if (dispatchHover && mHoverListener != null) { mHoverListener.onItemHovered( ((MenuAdapter) getAdapter()).getAdapterMenu(), position); } return superVal; } } } No newline at end of file core/java/android/widget/PopupMenu.java +2 −19 Original line number Diff line number Diff line Loading @@ -270,25 +270,8 @@ public class PopupMenu implements MenuBuilder.Callback, MenuPresenter.Callback { * @hide */ public boolean onOpenSubMenu(MenuBuilder subMenu) { if (subMenu == null) return false; if (!subMenu.hasVisibleItems()) { return true; } if (!mShowCascadingMenus) { // Current menu will be dismissed by the normal helper, submenu will be shown in its // place. (If cascading menus are enabled, the cascading implementation will show the // submenu itself). new MenuPopupHelper(mContext, subMenu, mAnchor).show(); } return true; } /** * @hide */ public void onCloseSubMenu(SubMenuBuilder menu) { // The menu presenter will handle opening the submenu itself. Nothing to do here. return false; } /** Loading core/java/com/android/internal/view/menu/CascadingMenuPopup.java +139 −37 Original line number Diff line number Diff line Loading @@ -10,17 +10,22 @@ import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; import android.os.Handler; import android.os.Parcelable; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewTreeObserver; import android.view.View.OnAttachStateChangeListener; import android.view.View.OnKeyListener; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.AdapterView; import android.widget.DropDownListView; import android.widget.MenuItemHoverListener; import android.widget.ListView; import android.widget.MenuPopupWindow; import android.widget.MenuPopupWindow.MenuDropDownListView; import android.widget.PopupWindow; import android.widget.PopupWindow.OnDismissListener; Loading @@ -32,8 +37,7 @@ import com.android.internal.util.Preconditions; * @hide */ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemClickListener, MenuPresenter, OnKeyListener, PopupWindow.OnDismissListener, ViewTreeObserver.OnGlobalLayoutListener, View.OnAttachStateChangeListener{ MenuPresenter, OnKeyListener, PopupWindow.OnDismissListener { @Retention(RetentionPolicy.SOURCE) @IntDef({HORIZ_POSITION_LEFT, HORIZ_POSITION_RIGHT}) public @interface HorizPosition {} Loading @@ -41,12 +45,133 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl private static final int HORIZ_POSITION_LEFT = 0; private static final int HORIZ_POSITION_RIGHT = 1; private static final int SUBMENU_TIMEOUT_MS = 200; private final Context mContext; private final int mMenuMaxWidth; private final int mPopupStyleAttr; private final int mPopupStyleRes; private final boolean mOverflowOnly; private final int mLayoutDirection; private final Handler mSubMenuHoverHandler; private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (isShowing()) { final View anchor = mShownAnchorView; if (anchor == null || !anchor.isShown()) { dismiss(); } else if (isShowing()) { // Recompute window sizes and positions. for (MenuPopupWindow popup : mPopupWindows) { popup.show(); } } } } }; private final OnAttachStateChangeListener mAttachStateChangeListener = new OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { } @Override public void onViewDetachedFromWindow(View v) { if (mTreeObserver != null) { if (!mTreeObserver.isAlive()) { mTreeObserver = v.getViewTreeObserver(); } mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener); } v.removeOnAttachStateChangeListener(this); } }; private final MenuItemHoverListener mMenuItemHoverListener = new MenuItemHoverListener() { @Override public void onItemHovered(MenuBuilder menu, int position) { int menuIndex = -1; for (int i = 0; i < mListViews.size(); i++) { final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(i); final MenuAdapter adapter = (MenuAdapter) view.getAdapter(); if (adapter.getAdapterMenu() == menu) { menuIndex = i; break; } } if (menuIndex == -1) { return; } final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(menuIndex); final ListMenuItemView selectedItemView = (ListMenuItemView) view.getSelectedView(); if (selectedItemView != null && selectedItemView.isEnabled() && selectedItemView.getItemData().hasSubMenu()) { // If the currently selected item corresponds to a submenu, schedule to open the // submenu on a timeout. mSubMenuHoverHandler.removeCallbacksAndMessages(null); mSubMenuHoverHandler.postDelayed(new Runnable() { @Override public void run() { // Make sure the submenu item is still the one selected. if (view.getSelectedView() == selectedItemView && selectedItemView.isEnabled() && selectedItemView.getItemData().hasSubMenu()) { // Close any other submenus that might be open at the current or // a deeper level. int nextIndex = mListViews.indexOf(view) + 1; if (nextIndex < mListViews.size()) { MenuAdapter nextSubMenuAdapter = (MenuAdapter) mListViews.get(nextIndex).getAdapter(); // Disable exit animation, to prevent overlapping fading out // submenus. mPopupWindows.get(nextIndex).setExitTransition(null); nextSubMenuAdapter.getAdapterMenu().close(); } // Then open the selected submenu. view.performItemClick( selectedItemView, view.getSelectedItemPosition(), view.getSelectedItemId()); } } }, SUBMENU_TIMEOUT_MS); } else if (menuIndex + 1 < mListViews.size()) { // If the currently selected item does NOT corresponds to a submenu, check if there // is a submenu already open that is one level deeper. If so, schedule to close it // on a timeout. final MenuDropDownListView nextView = (MenuDropDownListView) mListViews.get(menuIndex + 1); final MenuAdapter nextAdapter = (MenuAdapter) nextView.getAdapter(); view.clearSelection(); mSubMenuHoverHandler.removeCallbacksAndMessages(null); mSubMenuHoverHandler.postDelayed(new Runnable() { @Override public void run() { // Make sure the menu wasn't already closed by something else and that // it wasn't re-hovered by the user since this was scheduled. int nextMenuIndex = mListViews.indexOf(nextView); if (nextMenuIndex != -1 && nextView.getSelectedView() == null) { // Disable exit animation, to prevent overlapping fading out submenus. mPopupWindows.get(nextMenuIndex).setExitTransition(null); nextAdapter.getAdapterMenu().close(); } } }, SUBMENU_TIMEOUT_MS); } } }; private int mDropDownGravity = Gravity.NO_GRAVITY; private View mAnchorView; Loading Loading @@ -86,6 +211,7 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl mPopupWindows = new ArrayList<MenuPopupWindow>(); mListViews = new ArrayList<DropDownListView>(); mOffsets = new ArrayList<int[]>(); mSubMenuHoverHandler = new Handler(); } @Override Loading @@ -96,12 +222,12 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl private MenuPopupWindow createPopupWindow() { MenuPopupWindow popupWindow = new MenuPopupWindow( mContext, null, mPopupStyleAttr, mPopupStyleRes); popupWindow.setHoverListener(mMenuItemHoverListener); popupWindow.setOnItemClickListener(this); popupWindow.setOnDismissListener(this); popupWindow.setAnchorView(mAnchorView); popupWindow.setDropDownGravity(mDropDownGravity); popupWindow.setModal(true); popupWindow.setTouchModal(false); return popupWindow; } Loading @@ -125,8 +251,10 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl if (mShownAnchorView != null) { final boolean addGlobalListener = mTreeObserver == null; mTreeObserver = mShownAnchorView.getViewTreeObserver(); // Refresh to latest if (addGlobalListener) mTreeObserver.addOnGlobalLayoutListener(this); mShownAnchorView.addOnAttachStateChangeListener(this); if (addGlobalListener) { mTreeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener); } mShownAnchorView.addOnAttachStateChangeListener(mAttachStateChangeListener); } } Loading Loading @@ -208,6 +336,7 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl int y = 0; if (addSubMenu) { popupWindow.setTouchModal(false); popupWindow.setEnterTransition(null); ListView lastListView = mListViews.get(mListViews.size() - 1); Loading Loading @@ -331,7 +460,7 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl if (menuIndex == -1 && menu == adapter.mAdapterMenu) { menuIndex = i; wasSelected = view.getSelectedItem() != null; wasSelected = view.getSelectedView() != null; } // Once the menu has been found, remove it and all submenus beneath it from the Loading Loading @@ -365,12 +494,12 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl if (mPopupWindows.size() == 0) { if (mTreeObserver != null) { if (!mTreeObserver.isAlive()) mTreeObserver = mShownAnchorView.getViewTreeObserver(); mTreeObserver.removeGlobalOnLayoutListener(this); if (mTreeObserver.isAlive()) { mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener); } mTreeObserver = null; } mShownAnchorView.removeOnAttachStateChangeListener(this); mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener); // If every [sub]menu was dismissed, that means the whole thing was dismissed, so notify // the owner. mOnDismissListener.onDismiss(); Loading Loading @@ -411,31 +540,4 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl return mListViews.size() > 0 ? mListViews.get(mListViews.size() - 1) : null; } @Override public void onGlobalLayout() { if (isShowing()) { final View anchor = mShownAnchorView; if (anchor == null || !anchor.isShown()) { dismiss(); } else if (isShowing()) { // Recompute window sizes and positions. for (MenuPopupWindow popup : mPopupWindows) { popup.show(); } } } } @Override public void onViewAttachedToWindow(View v) { } @Override public void onViewDetachedFromWindow(View v) { if (mTreeObserver != null) { if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver(); mTreeObserver.removeGlobalOnLayoutListener(this); } v.removeOnAttachStateChangeListener(this); } } No newline at end of file Loading
core/java/android/widget/DropDownListView.java +0 −5 Original line number Diff line number Diff line Loading @@ -132,11 +132,6 @@ public class DropDownListView extends ListView { return selectedView != null && selectedView.isEnabled() || super.shouldShowSelector(); } protected void clearSelection() { setSelectedPositionInt(-1); setNextSelectedPositionInt(-1); } @Override public boolean onHoverEvent(MotionEvent ev) { final int action = ev.getActionMasked(); Loading
core/java/android/widget/MenuItemHoverListener.java 0 → 100644 +13 −0 Original line number Diff line number Diff line package android.widget; import com.android.internal.view.menu.MenuBuilder; /** * An interface notified when a menu item is hovered. Useful for cases when hover should trigger * some behavior at a higher level, like managing the opening and closing of submenus. * * @hide */ public interface MenuItemHoverListener { public void onItemHovered(MenuBuilder menu, int position); }
core/java/android/widget/MenuPopupWindow.java +65 −5 Original line number Diff line number Diff line Loading @@ -22,12 +22,12 @@ import android.content.res.Resources; import android.transition.Transition; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import com.android.internal.view.menu.ListMenuItemView; import com.android.internal.view.menu.MenuAdapter; import com.android.internal.view.menu.MenuBuilder; /** * A MenuPopupWindow represents the popup window for menu. Loading @@ -37,20 +37,32 @@ import com.android.internal.view.menu.MenuAdapter; * * @hide */ public class MenuPopupWindow extends ListPopupWindow { public class MenuPopupWindow extends ListPopupWindow implements MenuItemHoverListener { private MenuItemHoverListener mHoverListener; public MenuPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override DropDownListView createDropDownListView(Context context, boolean hijackFocus) { return new MenuDropDownListView(context, hijackFocus); MenuDropDownListView view = new MenuDropDownListView(context, hijackFocus); view.setHoverListener(this); return view; } public void setEnterTransition(Transition enterTransition) { mPopup.setEnterTransition(enterTransition); } public void setExitTransition(Transition exitTransition) { mPopup.setExitTransition(exitTransition); } public void setHoverListener(MenuItemHoverListener hoverListener) { mHoverListener = hoverListener; } /** * Set whether this window is touch modal or if outside touches will be sent to * other windows behind it. Loading @@ -59,10 +71,23 @@ public class MenuPopupWindow extends ListPopupWindow { mPopup.setTouchModal(touchModal); } private static class MenuDropDownListView extends DropDownListView { @Override public void onItemHovered(MenuBuilder menu, int position) { // Forward up the chain if (mHoverListener != null) { mHoverListener.onItemHovered(menu, position); } } /** * @hide */ public static class MenuDropDownListView extends DropDownListView { final int mAdvanceKey; final int mRetreatKey; private MenuItemHoverListener mHoverListener; public MenuDropDownListView(Context context, boolean hijackFocus) { super(context, hijackFocus); Loading @@ -77,6 +102,15 @@ public class MenuPopupWindow extends ListPopupWindow { } } public void setHoverListener(MenuItemHoverListener hoverListener) { mHoverListener = hoverListener; } public void clearSelection() { setSelectedPositionInt(INVALID_POSITION); setNextSelectedPositionInt(INVALID_POSITION); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { ListMenuItemView selectedItem = (ListMenuItemView) getSelectedView(); Loading @@ -99,6 +133,32 @@ public class MenuPopupWindow extends ListPopupWindow { return super.onKeyDown(keyCode, event); } @Override public boolean onHoverEvent(MotionEvent ev) { boolean dispatchHover = false; final int position = pointToPosition((int) ev.getX(), (int) ev.getY()); final int action = ev.getActionMasked(); if (action == MotionEvent.ACTION_HOVER_ENTER || action == MotionEvent.ACTION_HOVER_MOVE) { if (position != INVALID_POSITION && position != mSelectedPosition) { final View hoveredItem = getChildAt(position - getFirstVisiblePosition()); if (hoveredItem.isEnabled()) { dispatchHover = true; } } } boolean superVal = super.onHoverEvent(ev); if (dispatchHover && mHoverListener != null) { mHoverListener.onItemHovered( ((MenuAdapter) getAdapter()).getAdapterMenu(), position); } return superVal; } } } No newline at end of file
core/java/android/widget/PopupMenu.java +2 −19 Original line number Diff line number Diff line Loading @@ -270,25 +270,8 @@ public class PopupMenu implements MenuBuilder.Callback, MenuPresenter.Callback { * @hide */ public boolean onOpenSubMenu(MenuBuilder subMenu) { if (subMenu == null) return false; if (!subMenu.hasVisibleItems()) { return true; } if (!mShowCascadingMenus) { // Current menu will be dismissed by the normal helper, submenu will be shown in its // place. (If cascading menus are enabled, the cascading implementation will show the // submenu itself). new MenuPopupHelper(mContext, subMenu, mAnchor).show(); } return true; } /** * @hide */ public void onCloseSubMenu(SubMenuBuilder menu) { // The menu presenter will handle opening the submenu itself. Nothing to do here. return false; } /** Loading
core/java/com/android/internal/view/menu/CascadingMenuPopup.java +139 −37 Original line number Diff line number Diff line Loading @@ -10,17 +10,22 @@ import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; import android.os.Handler; import android.os.Parcelable; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewTreeObserver; import android.view.View.OnAttachStateChangeListener; import android.view.View.OnKeyListener; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.AdapterView; import android.widget.DropDownListView; import android.widget.MenuItemHoverListener; import android.widget.ListView; import android.widget.MenuPopupWindow; import android.widget.MenuPopupWindow.MenuDropDownListView; import android.widget.PopupWindow; import android.widget.PopupWindow.OnDismissListener; Loading @@ -32,8 +37,7 @@ import com.android.internal.util.Preconditions; * @hide */ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemClickListener, MenuPresenter, OnKeyListener, PopupWindow.OnDismissListener, ViewTreeObserver.OnGlobalLayoutListener, View.OnAttachStateChangeListener{ MenuPresenter, OnKeyListener, PopupWindow.OnDismissListener { @Retention(RetentionPolicy.SOURCE) @IntDef({HORIZ_POSITION_LEFT, HORIZ_POSITION_RIGHT}) public @interface HorizPosition {} Loading @@ -41,12 +45,133 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl private static final int HORIZ_POSITION_LEFT = 0; private static final int HORIZ_POSITION_RIGHT = 1; private static final int SUBMENU_TIMEOUT_MS = 200; private final Context mContext; private final int mMenuMaxWidth; private final int mPopupStyleAttr; private final int mPopupStyleRes; private final boolean mOverflowOnly; private final int mLayoutDirection; private final Handler mSubMenuHoverHandler; private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (isShowing()) { final View anchor = mShownAnchorView; if (anchor == null || !anchor.isShown()) { dismiss(); } else if (isShowing()) { // Recompute window sizes and positions. for (MenuPopupWindow popup : mPopupWindows) { popup.show(); } } } } }; private final OnAttachStateChangeListener mAttachStateChangeListener = new OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { } @Override public void onViewDetachedFromWindow(View v) { if (mTreeObserver != null) { if (!mTreeObserver.isAlive()) { mTreeObserver = v.getViewTreeObserver(); } mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener); } v.removeOnAttachStateChangeListener(this); } }; private final MenuItemHoverListener mMenuItemHoverListener = new MenuItemHoverListener() { @Override public void onItemHovered(MenuBuilder menu, int position) { int menuIndex = -1; for (int i = 0; i < mListViews.size(); i++) { final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(i); final MenuAdapter adapter = (MenuAdapter) view.getAdapter(); if (adapter.getAdapterMenu() == menu) { menuIndex = i; break; } } if (menuIndex == -1) { return; } final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(menuIndex); final ListMenuItemView selectedItemView = (ListMenuItemView) view.getSelectedView(); if (selectedItemView != null && selectedItemView.isEnabled() && selectedItemView.getItemData().hasSubMenu()) { // If the currently selected item corresponds to a submenu, schedule to open the // submenu on a timeout. mSubMenuHoverHandler.removeCallbacksAndMessages(null); mSubMenuHoverHandler.postDelayed(new Runnable() { @Override public void run() { // Make sure the submenu item is still the one selected. if (view.getSelectedView() == selectedItemView && selectedItemView.isEnabled() && selectedItemView.getItemData().hasSubMenu()) { // Close any other submenus that might be open at the current or // a deeper level. int nextIndex = mListViews.indexOf(view) + 1; if (nextIndex < mListViews.size()) { MenuAdapter nextSubMenuAdapter = (MenuAdapter) mListViews.get(nextIndex).getAdapter(); // Disable exit animation, to prevent overlapping fading out // submenus. mPopupWindows.get(nextIndex).setExitTransition(null); nextSubMenuAdapter.getAdapterMenu().close(); } // Then open the selected submenu. view.performItemClick( selectedItemView, view.getSelectedItemPosition(), view.getSelectedItemId()); } } }, SUBMENU_TIMEOUT_MS); } else if (menuIndex + 1 < mListViews.size()) { // If the currently selected item does NOT corresponds to a submenu, check if there // is a submenu already open that is one level deeper. If so, schedule to close it // on a timeout. final MenuDropDownListView nextView = (MenuDropDownListView) mListViews.get(menuIndex + 1); final MenuAdapter nextAdapter = (MenuAdapter) nextView.getAdapter(); view.clearSelection(); mSubMenuHoverHandler.removeCallbacksAndMessages(null); mSubMenuHoverHandler.postDelayed(new Runnable() { @Override public void run() { // Make sure the menu wasn't already closed by something else and that // it wasn't re-hovered by the user since this was scheduled. int nextMenuIndex = mListViews.indexOf(nextView); if (nextMenuIndex != -1 && nextView.getSelectedView() == null) { // Disable exit animation, to prevent overlapping fading out submenus. mPopupWindows.get(nextMenuIndex).setExitTransition(null); nextAdapter.getAdapterMenu().close(); } } }, SUBMENU_TIMEOUT_MS); } } }; private int mDropDownGravity = Gravity.NO_GRAVITY; private View mAnchorView; Loading Loading @@ -86,6 +211,7 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl mPopupWindows = new ArrayList<MenuPopupWindow>(); mListViews = new ArrayList<DropDownListView>(); mOffsets = new ArrayList<int[]>(); mSubMenuHoverHandler = new Handler(); } @Override Loading @@ -96,12 +222,12 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl private MenuPopupWindow createPopupWindow() { MenuPopupWindow popupWindow = new MenuPopupWindow( mContext, null, mPopupStyleAttr, mPopupStyleRes); popupWindow.setHoverListener(mMenuItemHoverListener); popupWindow.setOnItemClickListener(this); popupWindow.setOnDismissListener(this); popupWindow.setAnchorView(mAnchorView); popupWindow.setDropDownGravity(mDropDownGravity); popupWindow.setModal(true); popupWindow.setTouchModal(false); return popupWindow; } Loading @@ -125,8 +251,10 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl if (mShownAnchorView != null) { final boolean addGlobalListener = mTreeObserver == null; mTreeObserver = mShownAnchorView.getViewTreeObserver(); // Refresh to latest if (addGlobalListener) mTreeObserver.addOnGlobalLayoutListener(this); mShownAnchorView.addOnAttachStateChangeListener(this); if (addGlobalListener) { mTreeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener); } mShownAnchorView.addOnAttachStateChangeListener(mAttachStateChangeListener); } } Loading Loading @@ -208,6 +336,7 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl int y = 0; if (addSubMenu) { popupWindow.setTouchModal(false); popupWindow.setEnterTransition(null); ListView lastListView = mListViews.get(mListViews.size() - 1); Loading Loading @@ -331,7 +460,7 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl if (menuIndex == -1 && menu == adapter.mAdapterMenu) { menuIndex = i; wasSelected = view.getSelectedItem() != null; wasSelected = view.getSelectedView() != null; } // Once the menu has been found, remove it and all submenus beneath it from the Loading Loading @@ -365,12 +494,12 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl if (mPopupWindows.size() == 0) { if (mTreeObserver != null) { if (!mTreeObserver.isAlive()) mTreeObserver = mShownAnchorView.getViewTreeObserver(); mTreeObserver.removeGlobalOnLayoutListener(this); if (mTreeObserver.isAlive()) { mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener); } mTreeObserver = null; } mShownAnchorView.removeOnAttachStateChangeListener(this); mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener); // If every [sub]menu was dismissed, that means the whole thing was dismissed, so notify // the owner. mOnDismissListener.onDismiss(); Loading Loading @@ -411,31 +540,4 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl return mListViews.size() > 0 ? mListViews.get(mListViews.size() - 1) : null; } @Override public void onGlobalLayout() { if (isShowing()) { final View anchor = mShownAnchorView; if (anchor == null || !anchor.isShown()) { dismiss(); } else if (isShowing()) { // Recompute window sizes and positions. for (MenuPopupWindow popup : mPopupWindows) { popup.show(); } } } } @Override public void onViewAttachedToWindow(View v) { } @Override public void onViewDetachedFromWindow(View v) { if (mTreeObserver != null) { if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver(); mTreeObserver.removeGlobalOnLayoutListener(this); } v.removeOnAttachStateChangeListener(this); } } No newline at end of file