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