Move forwarding code to ListPopupWindow, add drag-to-open in Spinner

BUG: 9437139
Change-Id: I4599cf65a472b2ce74d2301988359d87917a6eec
This commit is contained in:
Alan Viverette
2013-08-16 14:41:06 -07:00
parent c05027214f
commit ca6a3611cd
5 changed files with 208 additions and 170 deletions

View File

@ -31,6 +31,8 @@ import android.view.ViewConfiguration;
import android.view.View.MeasureSpec;
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;
@ -561,7 +563,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
@ -680,56 +711,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;
}
}
}

View File

@ -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;

View File

@ -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);
}