From 31f581c5a64320d9a90ce3fb1a4625f94f4f8021 Mon Sep 17 00:00:00 2001 From: Jun Mukai Date: Thu, 2 Apr 2015 13:44:29 -0700 Subject: Introduces mouse actions for popup menu. - mouse hover moves the selected item in the menu. It moves the selection rectangle, and further up/down key or enter key will start from the hovered item. - when the mouse exits from the entire popup window, the selection is canceled. Further up/down key will start from the first item. To implement these behaviors, and consider about other keyboard behaviors which is special to menus, I believe it justifies to create another class for the menu popups rather than using ListPopupWindow directly. Bug: 19642104 Change-Id: I5e405c0491c67fdef9764898701119979ec13a9f --- core/java/android/widget/ListPopupWindow.java | 8 +- core/java/android/widget/MenuPopupWindow.java | 91 ++++++++++++++++++++++ .../internal/view/menu/MenuPopupHelper.java | 8 +- 3 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 core/java/android/widget/MenuPopupWindow.java diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index c6de5dde82d5..a02efcff4197 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -846,6 +846,10 @@ public class ListPopupWindow { return mDropDownList; } + DropDownListView createDropDownListView(Context context, boolean hijackFocus) { + return new DropDownListView(context, hijackFocus); + } + /** * The maximum number of list items that can be visible and still have * the list expand when touched. @@ -1068,7 +1072,7 @@ public class ListPopupWindow { } }; - mDropDownList = new DropDownListView(context, !mModal); + mDropDownList = createDropDownListView(context, !mModal); if (mDropDownListHighlight != null) { mDropDownList.setSelector(mDropDownListHighlight); } @@ -1494,7 +1498,7 @@ public class ListPopupWindow { * displayed on screen within a drop down. The focus is never actually * passed to the drop down in this mode; the list only looks focused.

*/ - private static class DropDownListView extends ListView { + static class DropDownListView extends ListView { /** Duration in milliseconds of the drag-to-open click animation. */ private static final long CLICK_ANIM_DURATION = 150; diff --git a/core/java/android/widget/MenuPopupWindow.java b/core/java/android/widget/MenuPopupWindow.java new file mode 100644 index 000000000000..8d42c739b627 --- /dev/null +++ b/core/java/android/widget/MenuPopupWindow.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 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 android.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.accessibility.AccessibilityManager; + +/** + * A MenuPopupWindow represents the popup window for menu. + * + * MenuPopupWindow is mostly same as ListPopupWindow, but it has customized + * behaviors specific to menus, + * + * @hide + */ +public class MenuPopupWindow extends ListPopupWindow { + public MenuPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + ListPopupWindow.DropDownListView createDropDownListView(Context context, boolean hijackFocus) { + return new MenuDropDownListView(context, hijackFocus); + } + + static class MenuDropDownListView extends ListPopupWindow.DropDownListView { + private boolean mHoveredOnDisabledItem = false; + private AccessibilityManager mAccessibilityManager; + + MenuDropDownListView(Context context, boolean hijackFocus) { + super(context, hijackFocus); + mAccessibilityManager = (AccessibilityManager) getContext().getSystemService( + Context.ACCESSIBILITY_SERVICE); + } + + @Override + protected boolean shouldShowSelector() { + return (isHovered() && !mHoveredOnDisabledItem) || super.shouldShowSelector(); + } + + @Override + public boolean onHoverEvent(MotionEvent ev) { + mHoveredOnDisabledItem = false; + + // Accessibility system should already handle hover events and selections, menu does + // not have to handle it by itself. + if (mAccessibilityManager.isTouchExplorationEnabled()) { + return super.onHoverEvent(ev); + } + + final int action = ev.getActionMasked(); + if (action == MotionEvent.ACTION_HOVER_ENTER + || action == MotionEvent.ACTION_HOVER_MOVE) { + final int position = pointToPosition((int) ev.getX(), (int) ev.getY()); + if (position != INVALID_POSITION && position != mSelectedPosition) { + final View hoveredItem = getChildAt(position - getFirstVisiblePosition()); + if (hoveredItem.isEnabled()) { + positionSelector(position, hoveredItem); + setSelectedPositionInt(position); + } else { + mHoveredOnDisabledItem = true; + } + updateSelectorState(); + } + } else { + // Do not cancel the selected position if the selection is visible by other reasons. + if (!super.shouldShowSelector()) { + setSelectedPositionInt(INVALID_POSITION); + } + } + return super.onHoverEvent(ev); + } + } +} diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index 7d4507150f8e..13654a695fcf 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -31,7 +31,7 @@ import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.FrameLayout; import android.widget.ListAdapter; -import android.widget.ListPopupWindow; +import android.widget.MenuPopupWindow; import android.widget.PopupWindow; import java.util.ArrayList; @@ -55,7 +55,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On private final int mPopupStyleRes; private View mAnchorView; - private ListPopupWindow mPopup; + private MenuPopupWindow mPopup; private ViewTreeObserver mTreeObserver; private Callback mPresenterCallback; @@ -126,7 +126,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On } } - public ListPopupWindow getPopup() { + public MenuPopupWindow getPopup() { return mPopup; } @@ -142,7 +142,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On return true; } - mPopup = new ListPopupWindow(mContext, null, mPopupStyleAttr, mPopupStyleRes); + mPopup = new MenuPopupWindow(mContext, null, mPopupStyleAttr, mPopupStyleRes); mPopup.setOnDismissListener(this); mPopup.setOnItemClickListener(this); mPopup.setAdapter(mAdapter); -- cgit v1.2.3-59-g8ed1b