1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.launcher3.util; 17 18 import android.graphics.Rect; 19 import android.graphics.RectF; 20 import android.view.MotionEvent; 21 import android.view.TouchDelegate; 22 import android.view.View; 23 24 /** 25 * This class differs from the framework {@link TouchDelegate} in that it transforms the 26 * coordinates of the motion event to the provided bounds. 27 * 28 * You can also modify the bounds post construction. Since the bounds are available during layout, 29 * this avoids new object creation during every layout. 30 */ 31 public class TransformingTouchDelegate extends TouchDelegate { 32 private static final Rect sTempRect = new Rect(); 33 34 private final RectF mBounds; 35 36 private final RectF mTouchCheckBounds; 37 private float mTouchExtension; 38 private boolean mWasTouchOutsideBounds; 39 40 private View mDelegateView; 41 private boolean mDelegateTargeted; 42 TransformingTouchDelegate(View delegateView)43 public TransformingTouchDelegate(View delegateView) { 44 super(sTempRect, delegateView); 45 46 mDelegateView = delegateView; 47 mBounds = new RectF(); 48 mTouchCheckBounds = new RectF(); 49 } 50 setBounds(int left, int top, int right, int bottom)51 public void setBounds(int left, int top, int right, int bottom) { 52 mBounds.set(left, top, right, bottom); 53 updateTouchBounds(); 54 } 55 extendTouchBounds(float extension)56 public void extendTouchBounds(float extension) { 57 mTouchExtension = extension; 58 updateTouchBounds(); 59 } 60 updateTouchBounds()61 private void updateTouchBounds() { 62 mTouchCheckBounds.set(mBounds); 63 mTouchCheckBounds.inset(-mTouchExtension, -mTouchExtension); 64 } 65 setDelegateView(View view)66 public void setDelegateView(View view) { 67 mDelegateView = view; 68 } 69 70 /** 71 * Will forward touch events to the delegate view if the event is within the bounds 72 * specified in the constructor. 73 * 74 * @param event The touch event to forward 75 * @return True if the event was forwarded to the delegate, false otherwise. 76 */ onTouchEvent(MotionEvent event)77 public boolean onTouchEvent(MotionEvent event) { 78 boolean sendToDelegate = false; 79 switch (event.getAction()) { 80 case MotionEvent.ACTION_DOWN: 81 mDelegateTargeted = mTouchCheckBounds.contains(event.getX(), event.getY()); 82 if (mDelegateTargeted) { 83 mWasTouchOutsideBounds = !mBounds.contains(event.getX(), event.getY()); 84 sendToDelegate = true; 85 } 86 break; 87 case MotionEvent.ACTION_MOVE: 88 sendToDelegate = mDelegateTargeted; 89 break; 90 case MotionEvent.ACTION_UP: 91 case MotionEvent.ACTION_CANCEL: 92 sendToDelegate = mDelegateTargeted; 93 mDelegateTargeted = false; 94 break; 95 } 96 boolean handled = false; 97 if (sendToDelegate) { 98 float x = event.getX(); 99 float y = event.getY(); 100 if (mWasTouchOutsideBounds) { 101 event.setLocation(mBounds.centerX(), mBounds.centerY()); 102 } else { 103 event.offsetLocation(-mBounds.left, -mBounds.top); 104 } 105 handled = mDelegateView.dispatchTouchEvent(event); 106 event.setLocation(x, y); 107 } 108 return handled; 109 } 110 } 111