diff options
author | Alan Viverette <alanv@google.com> | 2013-08-21 17:56:00 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-08-21 17:56:00 +0000 |
commit | a2e88ef89611d69b65b1830cc5ffebfb679e9d4d (patch) | |
tree | 65eaf556d9a02a9b7970b10ed13f501cd5b7fd80 /core | |
parent | 5feb0ad1d234cc7146286e96f96ae162bd628363 (diff) | |
parent | ca6a3611cdb28a514834adba35fcce2da6f2e7c2 (diff) | |
download | frameworks_base-a2e88ef89611d69b65b1830cc5ffebfb679e9d4d.zip frameworks_base-a2e88ef89611d69b65b1830cc5ffebfb679e9d4d.tar.gz frameworks_base-a2e88ef89611d69b65b1830cc5ffebfb679e9d4d.tar.bz2 |
Merge "Move forwarding code to ListPopupWindow, add drag-to-open in Spinner" into klp-dev
Diffstat (limited to 'core')
5 files changed, 208 insertions, 170 deletions
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index 2b4e520..8919248 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -33,6 +33,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.View.MeasureSpec; import android.view.View.OnTouchListener; +import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewParent; import android.view.animation.AccelerateDecelerateInterpolator; @@ -961,33 +962,6 @@ public class ListPopupWindow { } /** - * Receives motion events forwarded from a source view. This is used - * internally to implement support for drag-to-open. - * - * @param src view from which the event was forwarded - * @param srcEvent forwarded motion event in source-local coordinates - * @param activePointerId id of the pointer that activated forwarding - * @return whether the event was handled - * @hide - */ - public boolean onForwardedEvent(View src, MotionEvent srcEvent, int activePointerId) { - final DropDownListView dst = mDropDownList; - if (dst == null || !dst.isShown()) { - return false; - } - - // Convert event to local coordinates. - final MotionEvent dstEvent = MotionEvent.obtainNoHistory(srcEvent); - src.toGlobalMotionEvent(dstEvent); - dst.toLocalMotionEvent(dstEvent); - - // Forward converted event, then recycle it. - final boolean handled = dst.onForwardedEvent(dstEvent, activePointerId); - dstEvent.recycle(); - return handled; - } - - /** * <p>Builds the popup window's content and returns the height the popup * should have. Returns -1 when the content already exists.</p> * @@ -1155,6 +1129,147 @@ public class ListPopupWindow { } /** + * Abstract class that forwards touch events to a {@link ListPopupWindow}. + * + * @hide + */ + public static abstract class ForwardingListener implements View.OnTouchListener { + /** Scaled touch slop, used for detecting movement outside bounds. */ + private final float mScaledTouchSlop; + + /** Whether this listener is currently forwarding touch events. */ + private boolean mForwarding; + + /** The id of the first pointer down in the current event stream. */ + private int mActivePointerId; + + public ForwardingListener(Context context) { + mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + } + + /** + * Returns the popup to which this listener is forwarding events. + * <p> + * Override this to return the correct popup. If the popup is displayed + * asynchronously, you may also need to override + * {@link #onForwardingStopped} to prevent premature cancelation of + * forwarding. + * + * @return the popup to which this listener is forwarding events + */ + public abstract ListPopupWindow getPopup(); + + @Override + public boolean onTouch(View v, MotionEvent event) { + final boolean wasForwarding = mForwarding; + final boolean forwarding; + if (wasForwarding) { + forwarding = onTouchForwarded(v, event) || !onForwardingStopped(); + } else { + forwarding = onTouchObserved(v, event) && onForwardingStarted(); + } + + mForwarding = forwarding; + return forwarding || wasForwarding; + } + + /** + * Called when forwarding would like to start. + * <p> + * By default, this will show the popup returned by {@link #getPopup()}. + * It may be overridden to perform another action, like clicking the + * source view or preparing the popup before showing it. + * + * @return true to start forwarding, false otherwise + */ + public boolean onForwardingStarted() { + final ListPopupWindow popup = getPopup(); + if (popup != null && !popup.isShowing()) { + popup.show(); + } + return true; + } + + /** + * Called when forwarding would like to stop. + * <p> + * By default, this will dismiss the popup returned by + * {@link #getPopup()}. It may be overridden to perform some other + * action. + * + * @return true to stop forwarding, false otherwise + */ + public boolean onForwardingStopped() { + final ListPopupWindow popup = getPopup(); + if (popup != null && popup.isShowing()) { + popup.dismiss(); + } + return true; + } + + /** + * Observes motion events and determines when to start forwarding. + * + * @param src view from which the event originated + * @param srcEvent motion event in source view coordinates + * @return true to start forwarding motion events, false otherwise + */ + private boolean onTouchObserved(View src, MotionEvent srcEvent) { + if (!src.isEnabled()) { + return false; + } + + // The first pointer down is always the active pointer. + final int actionMasked = srcEvent.getActionMasked(); + if (actionMasked == MotionEvent.ACTION_DOWN) { + mActivePointerId = srcEvent.getPointerId(0); + } + + final int activePointerIndex = srcEvent.findPointerIndex(mActivePointerId); + if (activePointerIndex >= 0) { + final float x = srcEvent.getX(activePointerIndex); + final float y = srcEvent.getY(activePointerIndex); + if (!src.pointInView(x, y, mScaledTouchSlop)) { + // The pointer has moved outside of the view. + return true; + } + } + + return false; + } + + /** + * Handled forwarded motion events and determines when to stop + * forwarding. + * + * @param src view from which the event originated + * @param srcEvent motion event in source view coordinates + * @return true to continue forwarding motion events, false to cancel + */ + private boolean onTouchForwarded(View src, MotionEvent srcEvent) { + final ListPopupWindow popup = getPopup(); + if (popup == null || !popup.isShowing()) { + return false; + } + + final DropDownListView dst = popup.mDropDownList; + if (dst == null || !dst.isShown()) { + return false; + } + + // Convert event to destination-local coordinates. + final MotionEvent dstEvent = MotionEvent.obtainNoHistory(srcEvent); + src.toGlobalMotionEvent(dstEvent); + dst.toLocalMotionEvent(dstEvent); + + // Forward converted event to destination view, then recycle it. + final boolean handled = dst.onForwardedEvent(dstEvent, mActivePointerId); + dstEvent.recycle(); + return handled; + } + } + + /** * <p>Wrapper class for a ListView. This wrapper can hijack the focus to * make sure the list uses the appropriate drawables and states when * displayed on screen within a drop down. The focus is never actually diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java index b707cef..7c7df96 100644 --- a/core/java/android/widget/Spinner.java +++ b/core/java/android/widget/Spinner.java @@ -30,12 +30,14 @@ import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.ListPopupWindow.ForwardingListener; import android.widget.PopupWindow.OnDismissListener; @@ -76,7 +78,10 @@ public class Spinner extends AbsSpinner implements OnClickListener { * Use the theme-supplied value to select the dropdown mode. */ private static final int MODE_THEME = -1; - + + /** Forwarding listener used to implement drag-to-open. */ + private ForwardingListener mForwardingListener; + private SpinnerPopup mPopup; private DropDownAdapter mTempAdapter; int mDropDownWidth; @@ -173,7 +178,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { } case MODE_DROPDOWN: { - DropdownPopup popup = new DropdownPopup(context, attrs, defStyle); + final DropdownPopup popup = new DropdownPopup(context, attrs, defStyle); mDropDownWidth = a.getLayoutDimension( com.android.internal.R.styleable.Spinner_dropDownWidth, @@ -193,6 +198,20 @@ public class Spinner extends AbsSpinner implements OnClickListener { } mPopup = popup; + mForwardingListener = new ForwardingListener(context) { + @Override + public ListPopupWindow getPopup() { + return popup; + } + + @Override + public boolean onForwardingStarted() { + if (!mPopup.isShowing()) { + mPopup.show(getTextDirection(), getTextAlignment()); + } + return true; + } + }; break; } } @@ -449,6 +468,15 @@ public class Spinner extends AbsSpinner implements OnClickListener { } @Override + public boolean onTouchEvent(MotionEvent event) { + if (mForwardingListener != null && mForwardingListener.onTouch(this, event)) { + return true; + } + + return super.onTouchEvent(event); + } + + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mPopup != null && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) { diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index 30f9793..5d0a603 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -32,6 +32,8 @@ import android.view.View.MeasureSpec; import android.view.accessibility.AccessibilityNodeInfo; import android.view.ViewGroup; import android.widget.ImageButton; +import android.widget.ListPopupWindow; +import android.widget.ListPopupWindow.ForwardingListener; import com.android.internal.view.ActionBarPolicy; import com.android.internal.view.menu.ActionMenuView.ActionMenuChildView; @@ -562,7 +564,36 @@ public class ActionMenuPresenter extends BaseMenuPresenter setFocusable(true); setVisibility(VISIBLE); setEnabled(true); - setOnTouchListener(new OverflowForwardListener(context)); + + setOnTouchListener(new ForwardingListener(context) { + @Override + public ListPopupWindow getPopup() { + if (mOverflowPopup == null) { + return null; + } + + return mOverflowPopup.getPopup(); + } + + @Override + public boolean onForwardingStarted() { + showOverflowMenu(); + return true; + } + + @Override + public boolean onForwardingStopped() { + // Displaying the popup occurs asynchronously, so wait for + // the runnable to finish before deciding whether to stop + // forwarding. + if (mPostedOpenRunnable != null) { + return false; + } + + hideOverflowMenu(); + return true; + } + }); } @Override @@ -687,56 +718,4 @@ public class ActionMenuPresenter extends BaseMenuPresenter mPostedOpenRunnable = null; } } - - private class OverflowForwardListener extends TouchForwardingListener { - /** Scaled touch slop, used for detecting movement outside bounds. */ - private final float mScaledTouchSlop; - - private int mActivePointerId = MotionEvent.INVALID_POINTER_ID; - - public OverflowForwardListener(Context context) { - mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); - } - - @Override - public boolean onTouchObserved(View src, MotionEvent srcEvent) { - if (!src.isEnabled()) { - return false; - } - - // Always start forwarding events when the source view is touched. - mActivePointerId = srcEvent.getPointerId(0); - src.performClick(); - return true; - } - - @Override - public boolean onTouchForwarded(View src, MotionEvent srcEvent) { - final OverflowPopup popup = mOverflowPopup; - if (popup != null && popup.isShowing()) { - final int activePointerId = mActivePointerId; - if (activePointerId != MotionEvent.INVALID_POINTER_ID && src.isEnabled() - && popup.forwardMotionEvent(src, srcEvent, activePointerId)) { - // Handled the motion event, continue forwarding. - return true; - } - - final int activePointerIndex = srcEvent.findPointerIndex(activePointerId); - if (activePointerIndex >= 0) { - final float x = srcEvent.getX(activePointerIndex); - final float y = srcEvent.getY(activePointerIndex); - if (src.pointInView(x, y, mScaledTouchSlop)) { - // The user is touching the source view. Cancel - // forwarding, but don't dismiss the popup. - return false; - } - } - - popup.dismiss(); - } - - // Cancel forwarding. - return false; - } - } } diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index 9b266df..dbb78c2 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -108,6 +108,10 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On } } + public ListPopupWindow getPopup() { + return mPopup; + } + public boolean tryShow() { mPopup = new ListPopupWindow(mContext, null, com.android.internal.R.attr.popupMenuStyle); mPopup.setOnDismissListener(this); @@ -159,22 +163,6 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On return mPopup != null && mPopup.isShowing(); } - /** - * Forwards motion events from a source view to the popup window. - * - * @param src view from which the event was forwarded - * @param event forwarded motion event in source-local coordinates - * @param activePointerId id of the pointer that activated forwarding - * @return whether the event was handled - */ - public boolean forwardMotionEvent(View src, MotionEvent event, int activePointerId) { - if (mPopup == null || !mPopup.isShowing()) { - return false; - } - - return mPopup.onForwardedEvent(src, event, activePointerId); - } - @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { MenuAdapter adapter = mAdapter; diff --git a/core/java/com/android/internal/view/menu/TouchForwardingListener.java b/core/java/com/android/internal/view/menu/TouchForwardingListener.java deleted file mode 100644 index d1086de..0000000 --- a/core/java/com/android/internal/view/menu/TouchForwardingListener.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2013 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.view.menu; - -import android.view.MotionEvent; -import android.view.View; - -/** - * Touch listener used to intercept touches and forward them out of a view. - */ -abstract class TouchForwardingListener implements View.OnTouchListener { - /** Whether this listener is currently forwarding touch events. */ - private boolean mForwarding; - - @Override - public boolean onTouch(View v, MotionEvent ev) { - final int actionMasked = ev.getActionMasked(); - - if (mForwarding) { - // Rejecting the event or ending the stream stops forwarding. - if (!onTouchForwarded(v, ev) || actionMasked == MotionEvent.ACTION_UP - || actionMasked == MotionEvent.ACTION_CANCEL) { - stopForwarding(); - } - } else { - if (onTouchObserved(v, ev)) { - startForwarding(); - } - } - - return mForwarding; - } - - public void startForwarding() { - mForwarding = true; - } - - public void stopForwarding() { - mForwarding = false; - } - - /** - * Attempts to start forwarding motion events. - * - * @param v The view that triggered forwarding. - * @return True to start forwarding motion events, or false to cancel. - */ - public abstract boolean onTouchObserved(View v, MotionEvent ev); - - /** - * Handles forwarded motion events. - * - * @param v The view from which the event was forwarded. - * @param ev The forwarded motion event. - * @return True to continue forwarding motion events, or false to cancel. - */ - public abstract boolean onTouchForwarded(View v, MotionEvent ev); -} |