| /* |
| * Copyright (C) 2006 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.view.MotionEvent; |
| import android.text.*; |
| import android.widget.TextView; |
| import android.view.View; |
| |
| /** |
| * A movement method that interprets movement keys by scrolling the text buffer. |
| */ |
| public class ScrollingMovementMethod extends BaseMovementMethod implements MovementMethod { |
| private int getTopLine(TextView widget) { |
| return widget.getLayout().getLineForVertical(widget.getScrollY()); |
| } |
| |
| private int getBottomLine(TextView widget) { |
| return widget.getLayout().getLineForVertical(widget.getScrollY() + getInnerHeight(widget)); |
| } |
| |
| private int getInnerWidth(TextView widget) { |
| return widget.getWidth() - widget.getTotalPaddingLeft() - widget.getTotalPaddingRight(); |
| } |
| |
| private int getInnerHeight(TextView widget) { |
| return widget.getHeight() - widget.getTotalPaddingTop() - widget.getTotalPaddingBottom(); |
| } |
| |
| private int getCharacterWidth(TextView widget) { |
| return (int) Math.ceil(widget.getPaint().getFontSpacing()); |
| } |
| |
| private int getScrollBoundsLeft(TextView widget) { |
| final Layout layout = widget.getLayout(); |
| final int topLine = getTopLine(widget); |
| final int bottomLine = getBottomLine(widget); |
| if (topLine > bottomLine) { |
| return 0; |
| } |
| int left = Integer.MAX_VALUE; |
| for (int line = topLine; line <= bottomLine; line++) { |
| final int lineLeft = (int) Math.floor(layout.getLineLeft(line)); |
| if (lineLeft < left) { |
| left = lineLeft; |
| } |
| } |
| return left; |
| } |
| |
| private int getScrollBoundsRight(TextView widget) { |
| final Layout layout = widget.getLayout(); |
| final int topLine = getTopLine(widget); |
| final int bottomLine = getBottomLine(widget); |
| if (topLine > bottomLine) { |
| return 0; |
| } |
| int right = Integer.MIN_VALUE; |
| for (int line = topLine; line <= bottomLine; line++) { |
| final int lineRight = (int) Math.ceil(layout.getLineRight(line)); |
| if (lineRight > right) { |
| right = lineRight; |
| } |
| } |
| return right; |
| } |
| |
| @Override |
| protected boolean left(TextView widget, Spannable buffer) { |
| final int minScrollX = getScrollBoundsLeft(widget); |
| int scrollX = widget.getScrollX(); |
| if (scrollX > minScrollX) { |
| scrollX = Math.max(scrollX - getCharacterWidth(widget), minScrollX); |
| widget.scrollTo(scrollX, widget.getScrollY()); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| protected boolean right(TextView widget, Spannable buffer) { |
| final int maxScrollX = getScrollBoundsRight(widget) - getInnerWidth(widget); |
| int scrollX = widget.getScrollX(); |
| if (scrollX < maxScrollX) { |
| scrollX = Math.min(scrollX + getCharacterWidth(widget), maxScrollX); |
| widget.scrollTo(scrollX, widget.getScrollY()); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| protected boolean up(TextView widget, Spannable buffer) { |
| final Layout layout = widget.getLayout(); |
| final int top = widget.getScrollY(); |
| int topLine = layout.getLineForVertical(top); |
| if (layout.getLineTop(topLine) == top) { |
| // If the top line is partially visible, bring it all the way |
| // into view; otherwise, bring the previous line into view. |
| topLine -= 1; |
| } |
| if (topLine >= 0) { |
| Touch.scrollTo(widget, layout, widget.getScrollX(), layout.getLineTop(topLine)); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| protected boolean down(TextView widget, Spannable buffer) { |
| final Layout layout = widget.getLayout(); |
| final int innerHeight = getInnerHeight(widget); |
| final int bottom = widget.getScrollY() + innerHeight; |
| int bottomLine = layout.getLineForVertical(bottom); |
| if (layout.getLineTop(bottomLine + 1) < bottom + 1) { |
| // Less than a pixel of this line is out of view, |
| // so we must have tried to make it entirely in view |
| // and now want the next line to be in view instead. |
| bottomLine += 1; |
| } |
| if (bottomLine <= layout.getLineCount() - 1) { |
| Touch.scrollTo(widget, layout, widget.getScrollX(), |
| layout.getLineTop(bottomLine + 1) - innerHeight); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| protected boolean pageUp(TextView widget, Spannable buffer) { |
| final Layout layout = widget.getLayout(); |
| final int top = widget.getScrollY() - getInnerHeight(widget); |
| int topLine = layout.getLineForVertical(top); |
| if (topLine >= 0) { |
| Touch.scrollTo(widget, layout, widget.getScrollX(), layout.getLineTop(topLine)); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| protected boolean pageDown(TextView widget, Spannable buffer) { |
| final Layout layout = widget.getLayout(); |
| final int innerHeight = getInnerHeight(widget); |
| final int bottom = widget.getScrollY() + innerHeight + innerHeight; |
| int bottomLine = layout.getLineForVertical(bottom); |
| if (bottomLine <= layout.getLineCount() - 1) { |
| Touch.scrollTo(widget, layout, widget.getScrollX(), |
| layout.getLineTop(bottomLine + 1) - innerHeight); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| protected boolean top(TextView widget, Spannable buffer) { |
| final Layout layout = widget.getLayout(); |
| if (getTopLine(widget) >= 0) { |
| Touch.scrollTo(widget, layout, widget.getScrollX(), layout.getLineTop(0)); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| protected boolean bottom(TextView widget, Spannable buffer) { |
| final Layout layout = widget.getLayout(); |
| final int lineCount = layout.getLineCount(); |
| if (getBottomLine(widget) <= lineCount - 1) { |
| Touch.scrollTo(widget, layout, widget.getScrollX(), |
| layout.getLineTop(lineCount) - getInnerHeight(widget)); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| protected boolean lineStart(TextView widget, Spannable buffer) { |
| final int minScrollX = getScrollBoundsLeft(widget); |
| int scrollX = widget.getScrollX(); |
| if (scrollX > minScrollX) { |
| widget.scrollTo(minScrollX, widget.getScrollY()); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| protected boolean lineEnd(TextView widget, Spannable buffer) { |
| final int maxScrollX = getScrollBoundsRight(widget) - getInnerWidth(widget); |
| int scrollX = widget.getScrollX(); |
| if (scrollX < maxScrollX) { |
| widget.scrollTo(maxScrollX, widget.getScrollY()); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| protected boolean home(TextView widget, Spannable buffer) { |
| return top(widget, buffer); |
| } |
| |
| @Override |
| protected boolean end(TextView widget, Spannable buffer) { |
| return bottom(widget, buffer); |
| } |
| |
| @Override |
| public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { |
| return Touch.onTouchEvent(widget, buffer, event); |
| } |
| |
| @Override |
| public void onTakeFocus(TextView widget, Spannable text, int dir) { |
| Layout layout = widget.getLayout(); |
| |
| if (layout != null && (dir & View.FOCUS_FORWARD) != 0) { |
| widget.scrollTo(widget.getScrollX(), |
| layout.getLineTop(0)); |
| } |
| if (layout != null && (dir & View.FOCUS_BACKWARD) != 0) { |
| int padding = widget.getTotalPaddingTop() + |
| widget.getTotalPaddingBottom(); |
| int line = layout.getLineCount() - 1; |
| |
| widget.scrollTo(widget.getScrollX(), |
| layout.getLineTop(line+1) - |
| (widget.getHeight() - padding)); |
| } |
| } |
| |
| public static MovementMethod getInstance() { |
| if (sInstance == null) |
| sInstance = new ScrollingMovementMethod(); |
| |
| return sInstance; |
| } |
| |
| private static ScrollingMovementMethod sInstance; |
| } |