Touch.java revision c982f60e982c1d2df9f115ed9a5c3ef3643d0892
1/* 2 * Copyright (C) 2008 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 17package android.text.method; 18 19import android.text.Layout; 20import android.text.NoCopySpan; 21import android.text.Spannable; 22import android.text.Layout.Alignment; 23import android.view.KeyEvent; 24import android.view.MotionEvent; 25import android.view.ViewConfiguration; 26import android.widget.TextView; 27 28public class Touch { 29 private Touch() { } 30 31 /** 32 * Scrolls the specified widget to the specified coordinates, except 33 * constrains the X scrolling position to the horizontal regions of 34 * the text that will be visible after scrolling to the specified 35 * Y position. 36 */ 37 public static void scrollTo(TextView widget, Layout layout, int x, int y) { 38 int padding = widget.getTotalPaddingTop() + 39 widget.getTotalPaddingBottom(); 40 int top = layout.getLineForVertical(y); 41 int bottom = layout.getLineForVertical(y + widget.getHeight() - 42 padding); 43 44 int left = Integer.MAX_VALUE; 45 int right = 0; 46 Alignment a = null; 47 boolean ltr = true; 48 49 for (int i = top; i <= bottom; i++) { 50 left = (int) Math.min(left, layout.getLineLeft(i)); 51 right = (int) Math.max(right, layout.getLineRight(i)); 52 53 if (a == null) { 54 a = layout.getParagraphAlignment(i); 55 ltr = layout.getParagraphDirection(i) > 0; 56 } 57 } 58 59 padding = widget.getTotalPaddingLeft() + widget.getTotalPaddingRight(); 60 int width = widget.getWidth(); 61 int diff = 0; 62 63 // align_opposite does NOT mean align_right, we need the paragraph 64 // direction to resolve it to left or right 65 if (right - left < width - padding) { 66 if (a == Alignment.ALIGN_CENTER) { 67 diff = (width - padding - (right - left)) / 2; 68 } else if (ltr == (a == Alignment.ALIGN_OPPOSITE)) { 69 diff = width - padding - (right - left); 70 } 71 } 72 73 x = Math.min(x, right - (width - padding) - diff); 74 x = Math.max(x, left - diff); 75 76 widget.scrollTo(x, y); 77 } 78 79 /** 80 * @hide 81 * Returns the maximum scroll value in x. 82 */ 83 public static int getMaxScrollX(TextView widget, Layout layout, int y) { 84 int top = layout.getLineForVertical(y); 85 int bottom = layout.getLineForVertical(y + widget.getHeight() 86 - widget.getTotalPaddingTop() -widget.getTotalPaddingBottom()); 87 int left = Integer.MAX_VALUE; 88 int right = 0; 89 for (int i = top; i <= bottom; i++) { 90 left = (int) Math.min(left, layout.getLineLeft(i)); 91 right = (int) Math.max(right, layout.getLineRight(i)); 92 } 93 return right - left - widget.getWidth() - widget.getTotalPaddingLeft() 94 - widget.getTotalPaddingRight(); 95 } 96 97 /** 98 * Handles touch events for dragging. You may want to do other actions 99 * like moving the cursor on touch as well. 100 */ 101 public static boolean onTouchEvent(TextView widget, Spannable buffer, 102 MotionEvent event) { 103 DragState[] ds; 104 105 switch (event.getAction()) { 106 case MotionEvent.ACTION_DOWN: 107 ds = buffer.getSpans(0, buffer.length(), DragState.class); 108 109 for (int i = 0; i < ds.length; i++) { 110 buffer.removeSpan(ds[i]); 111 } 112 113 buffer.setSpan(new DragState(event.getX(), event.getY(), 114 widget.getScrollX(), widget.getScrollY()), 115 0, 0, Spannable.SPAN_MARK_MARK); 116 return true; 117 118 case MotionEvent.ACTION_UP: 119 ds = buffer.getSpans(0, buffer.length(), DragState.class); 120 121 for (int i = 0; i < ds.length; i++) { 122 buffer.removeSpan(ds[i]); 123 } 124 125 if (ds.length > 0 && ds[0].mUsed) { 126 return true; 127 } else { 128 return false; 129 } 130 131 case MotionEvent.ACTION_MOVE: 132 ds = buffer.getSpans(0, buffer.length(), DragState.class); 133 134 if (ds.length > 0) { 135 if (ds[0].mFarEnough == false) { 136 int slop = ViewConfiguration.get(widget.getContext()).getScaledTouchSlop(); 137 138 if (Math.abs(event.getX() - ds[0].mX) >= slop || 139 Math.abs(event.getY() - ds[0].mY) >= slop) { 140 ds[0].mFarEnough = true; 141 } 142 } 143 144 if (ds[0].mFarEnough) { 145 ds[0].mUsed = true; 146 boolean cap = (MetaKeyKeyListener.getMetaState(buffer, 147 KeyEvent.META_SHIFT_ON) == 1) || 148 (MetaKeyKeyListener.getMetaState(buffer, 149 MetaKeyKeyListener.META_SELECTING) != 0); 150 float dx; 151 float dy; 152 if (cap) { 153 // if we're selecting, we want the scroll to go in 154 // the direction of the drag 155 dx = event.getX() - ds[0].mX; 156 dy = event.getY() - ds[0].mY; 157 } else { 158 dx = ds[0].mX - event.getX(); 159 dy = ds[0].mY - event.getY(); 160 } 161 ds[0].mX = event.getX(); 162 ds[0].mY = event.getY(); 163 164 int nx = widget.getScrollX() + (int) dx; 165 int ny = widget.getScrollY() + (int) dy; 166 167 int padding = widget.getTotalPaddingTop() + 168 widget.getTotalPaddingBottom(); 169 Layout layout = widget.getLayout(); 170 171 ny = Math.min(ny, layout.getHeight() - (widget.getHeight() - 172 padding)); 173 ny = Math.max(ny, 0); 174 175 int oldX = widget.getScrollX(); 176 int oldY = widget.getScrollY(); 177 178 scrollTo(widget, layout, nx, ny); 179 180 // If we actually scrolled, then cancel the up action. 181 if (oldX != widget.getScrollX() 182 || oldY != widget.getScrollY()) { 183 widget.cancelLongPress(); 184 } 185 186 return true; 187 } 188 } 189 } 190 191 return false; 192 } 193 194 public static int getInitialScrollX(TextView widget, Spannable buffer) { 195 DragState[] ds = buffer.getSpans(0, buffer.length(), DragState.class); 196 return ds.length > 0 ? ds[0].mScrollX : -1; 197 } 198 199 public static int getInitialScrollY(TextView widget, Spannable buffer) { 200 DragState[] ds = buffer.getSpans(0, buffer.length(), DragState.class); 201 return ds.length > 0 ? ds[0].mScrollY : -1; 202 } 203 204 private static class DragState implements NoCopySpan { 205 public float mX; 206 public float mY; 207 public int mScrollX; 208 public int mScrollY; 209 public boolean mFarEnough; 210 public boolean mUsed; 211 212 public DragState(float x, float y, int scrollX, int scrollY) { 213 mX = x; 214 mY = y; 215 mScrollX = scrollX; 216 mScrollY = scrollY; 217 } 218 } 219} 220