19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License. 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License. 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.view; 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Rect; 209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.MotionEvent; 219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View; 229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.ViewConfiguration; 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Helper class to handle situations where you want a view to have a larger touch area than its 269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * actual view bounds. The view whose touch area is changed is called the delegate view. This 279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * class should be used by an ancestor of the delegate. To use a TouchDelegate, first create an 289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * instance that specifies the bounds that should be mapped to the delegate and the delegate 299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * view itself. 309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p> 319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The ancestor should then forward all of its touch events received in its 329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@link android.view.View#onTouchEvent(MotionEvent)} to {@link #onTouchEvent(MotionEvent)}. 339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * </p> 349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class TouchDelegate { 369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * View that should receive forwarded touch events 399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private View mDelegateView; 419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Bounds in local coordinates of the containing view that should be mapped to the delegate 449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * view. This rect is used for initial hit testing. 459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Rect mBounds; 479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * mBounds inflated to include some slop. This rect is to track whether the motion events 509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * should be considered to be be within the delegate view. 519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Rect mSlopBounds; 539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * True if the delegate had been targeted on a down event (intersected mBounds). 569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean mDelegateTargeted; 589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The touchable region of the View extends above its actual extent. 619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int ABOVE = 1; 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The touchable region of the View extends below its actual extent. 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int BELOW = 2; 689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The touchable region of the View extends to the left of its 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * actual extent. 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int TO_LEFT = 4; 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The touchable region of the View extends to the right of its 779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * actual extent. 789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int TO_RIGHT = 8; 809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mSlop; 829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Constructor 859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param bounds Bounds in local coordinates of the containing view that should be mapped to 879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the delegate view 889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param delegateView The view that should receive motion events 899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public TouchDelegate(Rect bounds, View delegateView) { 919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mBounds = bounds; 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop(); 949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSlopBounds = new Rect(bounds); 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSlopBounds.inset(-mSlop, -mSlop); 969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mDelegateView = delegateView; 979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Will forward touch events to the delegate view if the event is within the bounds 1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * specified in the constructor. 1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param event The touch event to forward 1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return True if the event was forwarded to the delegate, false otherwise. 1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean onTouchEvent(MotionEvent event) { 1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int x = (int)event.getX(); 1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int y = (int)event.getY(); 1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean sendToDelegate = false; 1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean hit = true; 1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean handled = false; 1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project switch (event.getAction()) { 1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case MotionEvent.ACTION_DOWN: 1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Rect bounds = mBounds; 1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (bounds.contains(x, y)) { 1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mDelegateTargeted = true; 1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project sendToDelegate = true; 1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case MotionEvent.ACTION_UP: 1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case MotionEvent.ACTION_MOVE: 1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project sendToDelegate = mDelegateTargeted; 1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (sendToDelegate) { 1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Rect slopBounds = mSlopBounds; 1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!slopBounds.contains(x, y)) { 1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project hit = false; 1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case MotionEvent.ACTION_CANCEL: 1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project sendToDelegate = mDelegateTargeted; 1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mDelegateTargeted = false; 1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (sendToDelegate) { 1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final View delegateView = mDelegateView; 1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (hit) { 1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Offset event coordinates to be inside the target view 1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project event.setLocation(delegateView.getWidth() / 2, delegateView.getHeight() / 2); 1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Offset event coordinates to be outside the target view (in case it does 1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // something like tracking pressed state) 1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int slop = mSlop; 1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project event.setLocation(-(slop * 2), -(slop * 2)); 1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handled = delegateView.dispatchTouchEvent(event); 1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return handled; 1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 154