214 lines
7.5 KiB
Java
214 lines
7.5 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2008 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package android.text.method;
|
||
|
|
||
|
import android.text.Layout;
|
||
|
import android.text.Layout.Alignment;
|
||
|
import android.text.NoCopySpan;
|
||
|
import android.text.Spannable;
|
||
|
import android.view.KeyEvent;
|
||
|
import android.view.MotionEvent;
|
||
|
import android.view.ViewConfiguration;
|
||
|
import android.widget.TextView;
|
||
|
|
||
|
public class Touch {
|
||
|
private Touch() { }
|
||
|
|
||
|
/**
|
||
|
* Scrolls the specified widget to the specified coordinates, except
|
||
|
* constrains the X scrolling position to the horizontal regions of
|
||
|
* the text that will be visible after scrolling to the specified
|
||
|
* Y position.
|
||
|
*/
|
||
|
public static void scrollTo(TextView widget, Layout layout, int x, int y) {
|
||
|
final int horizontalPadding = widget.getTotalPaddingLeft() + widget.getTotalPaddingRight();
|
||
|
final int availableWidth = widget.getWidth() - horizontalPadding;
|
||
|
|
||
|
final int top = layout.getLineForVertical(y);
|
||
|
Alignment a = layout.getParagraphAlignment(top);
|
||
|
boolean ltr = layout.getParagraphDirection(top) > 0;
|
||
|
|
||
|
int left, right;
|
||
|
if (widget.getHorizontallyScrolling()) {
|
||
|
final int verticalPadding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom();
|
||
|
final int bottom = layout.getLineForVertical(y + widget.getHeight() - verticalPadding);
|
||
|
|
||
|
left = Integer.MAX_VALUE;
|
||
|
right = 0;
|
||
|
|
||
|
for (int i = top; i <= bottom; i++) {
|
||
|
left = (int) Math.min(left, layout.getLineLeft(i));
|
||
|
right = (int) Math.max(right, layout.getLineRight(i));
|
||
|
}
|
||
|
} else {
|
||
|
left = 0;
|
||
|
right = availableWidth;
|
||
|
}
|
||
|
|
||
|
final int actualWidth = right - left;
|
||
|
|
||
|
if (actualWidth < availableWidth) {
|
||
|
if (a == Alignment.ALIGN_CENTER) {
|
||
|
x = left - ((availableWidth - actualWidth) / 2);
|
||
|
} else if ((ltr && (a == Alignment.ALIGN_OPPOSITE)) ||
|
||
|
(!ltr && (a == Alignment.ALIGN_NORMAL)) ||
|
||
|
(a == Alignment.ALIGN_RIGHT)) {
|
||
|
// align_opposite does NOT mean align_right, we need the paragraph
|
||
|
// direction to resolve it to left or right
|
||
|
x = left - (availableWidth - actualWidth);
|
||
|
} else {
|
||
|
x = left;
|
||
|
}
|
||
|
} else {
|
||
|
x = Math.min(x, right - availableWidth);
|
||
|
x = Math.max(x, left);
|
||
|
}
|
||
|
|
||
|
widget.scrollTo(x, y);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handles touch events for dragging. You may want to do other actions
|
||
|
* like moving the cursor on touch as well.
|
||
|
*/
|
||
|
public static boolean onTouchEvent(TextView widget, Spannable buffer,
|
||
|
MotionEvent event) {
|
||
|
DragState[] ds;
|
||
|
|
||
|
switch (event.getActionMasked()) {
|
||
|
case MotionEvent.ACTION_DOWN:
|
||
|
ds = buffer.getSpans(0, buffer.length(), DragState.class);
|
||
|
|
||
|
for (int i = 0; i < ds.length; i++) {
|
||
|
buffer.removeSpan(ds[i]);
|
||
|
}
|
||
|
|
||
|
buffer.setSpan(new DragState(event.getX(), event.getY(),
|
||
|
widget.getScrollX(), widget.getScrollY()),
|
||
|
0, 0, Spannable.SPAN_MARK_MARK);
|
||
|
return true;
|
||
|
|
||
|
case MotionEvent.ACTION_UP:
|
||
|
ds = buffer.getSpans(0, buffer.length(), DragState.class);
|
||
|
|
||
|
for (int i = 0; i < ds.length; i++) {
|
||
|
buffer.removeSpan(ds[i]);
|
||
|
}
|
||
|
|
||
|
if (ds.length > 0 && ds[0].mUsed) {
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
case MotionEvent.ACTION_MOVE:
|
||
|
ds = buffer.getSpans(0, buffer.length(), DragState.class);
|
||
|
|
||
|
if (ds.length > 0) {
|
||
|
if (ds[0].mFarEnough == false) {
|
||
|
int slop = ViewConfiguration.get(widget.getContext()).getScaledTouchSlop();
|
||
|
|
||
|
if (Math.abs(event.getX() - ds[0].mX) >= slop ||
|
||
|
Math.abs(event.getY() - ds[0].mY) >= slop) {
|
||
|
ds[0].mFarEnough = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ds[0].mFarEnough) {
|
||
|
ds[0].mUsed = true;
|
||
|
boolean cap = (event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0
|
||
|
|| MetaKeyKeyListener.getMetaState(buffer,
|
||
|
MetaKeyKeyListener.META_SHIFT_ON) == 1
|
||
|
|| MetaKeyKeyListener.getMetaState(buffer,
|
||
|
MetaKeyKeyListener.META_SELECTING) != 0;
|
||
|
|
||
|
float dx;
|
||
|
float dy;
|
||
|
if (cap) {
|
||
|
// if we're selecting, we want the scroll to go in
|
||
|
// the direction of the drag
|
||
|
dx = event.getX() - ds[0].mX;
|
||
|
dy = event.getY() - ds[0].mY;
|
||
|
} else {
|
||
|
dx = ds[0].mX - event.getX();
|
||
|
dy = ds[0].mY - event.getY();
|
||
|
}
|
||
|
ds[0].mX = event.getX();
|
||
|
ds[0].mY = event.getY();
|
||
|
|
||
|
int nx = widget.getScrollX() + (int) dx;
|
||
|
int ny = widget.getScrollY() + (int) dy;
|
||
|
|
||
|
int padding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom();
|
||
|
Layout layout = widget.getLayout();
|
||
|
|
||
|
ny = Math.min(ny, layout.getHeight() - (widget.getHeight() - padding));
|
||
|
ny = Math.max(ny, 0);
|
||
|
|
||
|
int oldX = widget.getScrollX();
|
||
|
int oldY = widget.getScrollY();
|
||
|
|
||
|
scrollTo(widget, layout, nx, ny);
|
||
|
|
||
|
// If we actually scrolled, then cancel the up action.
|
||
|
if (oldX != widget.getScrollX() || oldY != widget.getScrollY()) {
|
||
|
widget.cancelLongPress();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param widget The text view.
|
||
|
* @param buffer The text buffer.
|
||
|
*/
|
||
|
public static int getInitialScrollX(TextView widget, Spannable buffer) {
|
||
|
DragState[] ds = buffer.getSpans(0, buffer.length(), DragState.class);
|
||
|
return ds.length > 0 ? ds[0].mScrollX : -1;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param widget The text view.
|
||
|
* @param buffer The text buffer.
|
||
|
*/
|
||
|
public static int getInitialScrollY(TextView widget, Spannable buffer) {
|
||
|
DragState[] ds = buffer.getSpans(0, buffer.length(), DragState.class);
|
||
|
return ds.length > 0 ? ds[0].mScrollY : -1;
|
||
|
}
|
||
|
|
||
|
private static class DragState implements NoCopySpan {
|
||
|
public float mX;
|
||
|
public float mY;
|
||
|
public int mScrollX;
|
||
|
public int mScrollY;
|
||
|
public boolean mFarEnough;
|
||
|
public boolean mUsed;
|
||
|
|
||
|
public DragState(float x, float y, int scrollX, int scrollY) {
|
||
|
mX = x;
|
||
|
mY = y;
|
||
|
mScrollX = scrollX;
|
||
|
mScrollY = scrollY;
|
||
|
}
|
||
|
}
|
||
|
}
|