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