下面列出了android.widget.TextView#getLayout ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
private String getLinkText(final TextView widget, final Spannable buffer, final MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
if (link.length != 0) {
return buffer.subSequence(buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0])).toString();
}
return "";
}
private TouchableSpan getPressedSpan(TextView textView, Spannable spannable, MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= textView.getTotalPaddingLeft();
y -= textView.getTotalPaddingTop();
x += textView.getScrollX();
y += textView.getScrollY();
Layout layout = textView.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
TouchableSpan[] link = spannable.getSpans(off, off, TouchableSpan.class);
TouchableSpan touchedSpan = null;
if (link.length > 0) {
touchedSpan = link[0];
}
return touchedSpan;
}
/**
* Performs a scroll up action.
* Scrolls up by the specified number of lines.
*
* @param widget The text view.
* @param buffer The text buffer.
* @param amount The number of lines to scroll by. Must be at least 1.
* @return True if the event was handled.
* @hide
*/
protected boolean scrollUp(TextView widget, Spannable buffer, int amount) {
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) {
topLine = Math.max(topLine - amount + 1, 0);
Touch.scrollTo(widget, layout, widget.getScrollX(), layout.getLineTop(topLine));
return true;
}
return false;
}
public static float GetLetterPositionX(@NonNull TextView aView, int aLetterIndex, boolean aClamp) {
Layout layout = aView.getLayout();
if (layout == null) {
return 0;
}
float x = layout.getPrimaryHorizontal(aLetterIndex);
x += aView.getPaddingLeft();
x -= aView.getScrollX();
if (aClamp && x > (aView.getMeasuredWidth() - aView.getPaddingRight())) {
x = aView.getMeasuredWidth() - aView.getPaddingRight();
}
if (aClamp && x < aView.getPaddingLeft()) {
x = aView.getPaddingLeft();
}
return x;
}
/**
* Performs a scroll to bottom action.
* Scrolls to the bottom of the document.
*
* @param widget The text view.
* @param buffer The text buffer.
* @return True if the event was handled.
* @hide
*/
protected boolean scrollBottom(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;
}
private float getMaxLineWidth(TextView textView) {
Layout layout = textView.getLayout();
float max_width = 0.0f;
int lines = layout.getLineCount();
for (int i = 0; i < lines; i++) {
if (layout.getLineWidth(i) > max_width) {
max_width = layout.getLineWidth(i);
}
}
return max_width;
}
public static boolean isEllipsed(@NonNull TextView textView) {
Layout layout = textView.getLayout();
if (layout != null) {
int lines = layout.getLineCount();
if (lines > 0) {
return IntStream.range(0, lines).anyMatch(line -> layout.getEllipsisCount(line) > 0);
}
}
return false;
}
@Override
protected boolean right(TextView widget, Spannable buffer) {
final Layout layout = widget.getLayout();
if (isSelecting(buffer)) {
return Selection.extendRight(buffer, layout);
} else {
return Selection.moveRight(buffer, layout);
}
}
@Override
public void onTakeFocus(TextView view, Spannable text, int dir) {
if ((dir & (View.FOCUS_FORWARD | View.FOCUS_DOWN)) != 0) {
if (view.getLayout() == null) {
// This shouldn't be null, but do something sensible if it is.
Selection.setSelection(text, text.length());
}
} else {
Selection.setSelection(text, text.length());
}
}
@Override
protected boolean lineStart(TextView widget, Spannable buffer) {
final Layout layout = widget.getLayout();
if (isSelecting(buffer)) {
return Selection.extendToLeftEdge(buffer, layout);
} else {
return Selection.moveToLeftEdge(buffer, layout);
}
}
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
if (link.length > 0) {
if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget);
} else {
Selection.setSelection(buffer, buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
}
return true;
} else {
Selection.removeSelection(buffer);
}
}
return false;
}
/**
* Find the span that was clicked
*
* @param widget view the user clicked
* @param spannable spannable string inside the clicked view
* @param event motion event that occurred
* @return the touchable span that was pressed
*/
private URLSpan getPressedSpan(TextView widget, Spannable spannable, MotionEvent event) {
int x = Math.round(event.getX());
int y = Math.round(event.getY());
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off;
try {
off = layout.getOffsetForHorizontal(line, (float) x);
} catch (IndexOutOfBoundsException e) {
return null;
}
int end = layout.getLineEnd(line);
// offset seems like it can be one off in some cases
// Could be what was causing issue 7 in the first place:
// https://github.com/klinker24/Android-TextView-LinkBuilder/issues/7
if (off != end && off != end - 1) {
URLSpan[] links = spannable.getSpans(off, off, URLSpan.class);
if (links.length > 0) {
return links[0];
}
}
return null;
}
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
int action = event.getAction();
// LogUtils.printLog(WeiboTextView.class, "MotionEvent:" + action);
// if (action == MotionEvent.ACTION_MOVE) {
// isPressed = false;
// }
switch (action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
buffer.setSpan(new BackgroundColorSpan(0x00000000), 0, weiboTextView.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Selection.setSelection(buffer, 0, weiboTextView.length());
break;
}
if (action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_DOWN) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
if (link.length != 0) {
int start = buffer.getSpanStart(link[0]);
int end = buffer.getSpanEnd(link[0]);
// LogUtils.printLog(WebViewUtils.class, "start:" + start + " end:" + end);
if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget);
buffer.setSpan(new BackgroundColorSpan(0x00000000), start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Selection.setSelection(buffer, start, end);
} else if (action == MotionEvent.ACTION_DOWN) {
buffer.setSpan(new BackgroundColorSpan(weiboTextView.clickBgClolr), start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Selection.setSelection(buffer, start, end);
}
if (widget instanceof WeiboTextView) {
((WeiboTextView) widget).linkHit = true;
}
return true;
} else {
Selection.removeSelection(buffer);
Touch.onTouchEvent(widget, buffer, event);
return false;
}
}
return Touch.onTouchEvent(widget, buffer, event);
}
@Override
public boolean onTouchEvent(TextView widget, final Spannable buffer, MotionEvent event) {
comm = (SpoilerRobotoTextView) widget;
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
link = buffer.getSpans(off, off, URLSpan.class);
if (link.length > 0) {
comm.setLongClickable(false);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
position = event.getY(); //used to see if the user scrolled or not
}
if (!(event.getAction() == MotionEvent.ACTION_UP
|| event.getAction() == MotionEvent.ACTION_DOWN)) {
if (Math.abs((position - event.getY())) > 25) {
handler.removeCallbacksAndMessages(null);
}
return super.onTouchEvent(widget, buffer, event);
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
clickHandled = false;
this.event = event;
if (SettingValues.peek) {
handler.postDelayed(longClicked,
android.view.ViewConfiguration.getTapTimeout() + 50);
} else {
handler.postDelayed(longClicked,
android.view.ViewConfiguration.getLongPressTimeout());
}
break;
case MotionEvent.ACTION_UP:
comm.setLongClickable(true);
handler.removeCallbacksAndMessages(null);
if (!clickHandled) {
// regular click
if (link.length != 0) {
int i = 0;
if (sequence != null) {
i = sequence.getSpanEnd(link[0]);
}
if (!link[0].getURL().isEmpty()) {
clickableText.onLinkClick(link[0].getURL(), i, subreddit, link[0]);
}
} else {
return false;
}
}
break;
}
return true;
} else {
Selection.removeSelection(buffer);
return false;
}
}
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_DOWN) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
LongClickCopySpan longClickCopySpan[] = buffer.getSpans(off, off, LongClickCopySpan.class);
if (longClickCopySpan.length != 0) {
LongClickCopySpan aSingleSpan = longClickCopySpan[0];
if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer, buffer.getSpanStart(aSingleSpan),
buffer.getSpanEnd(aSingleSpan));
aSingleSpan.setHighlighted(true,
ContextCompat.getColor(widget.getContext(), R.color.touch_highlight));
} else {
Selection.removeSelection(buffer);
aSingleSpan.setHighlighted(false, Color.TRANSPARENT);
}
this.currentSpan = aSingleSpan;
this.widget = widget;
return gestureDetector.onTouchEvent(event);
}
} else if (action == MotionEvent.ACTION_CANCEL) {
// Remove Selections.
LongClickCopySpan[] spans = buffer.getSpans(Selection.getSelectionStart(buffer),
Selection.getSelectionEnd(buffer), LongClickCopySpan.class);
for (LongClickCopySpan aSpan : spans) {
aSpan.setHighlighted(false, Color.TRANSPARENT);
}
Selection.removeSelection(buffer);
return gestureDetector.onTouchEvent(event);
}
return super.onTouchEvent(widget, buffer, event);
}
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer,
MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_DOWN) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
ClickableSpan span = link[0];
if (span instanceof URLSpan) {
UrlOpener.openUrl(widget.getContext(), ((URLSpan) span).getURL(), true);
} else {
span.onClick(widget);
}
} else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
}
return true;
} else {
Selection.removeSelection(buffer);
}
}
return super.onTouchEvent(widget, buffer, event);
}
private boolean action(int what, TextView widget, Spannable buffer) {
Layout layout = widget.getLayout();
int padding = widget.getTotalPaddingTop() +
widget.getTotalPaddingBottom();
int areatop = widget.getScrollY();
int areabot = areatop + widget.getHeight() - padding;
int linetop = layout.getLineForVertical(areatop);
int linebot = layout.getLineForVertical(areabot);
int first = layout.getLineStart(linetop);
int last = layout.getLineEnd(linebot);
ClickableSpan[] candidates = buffer.getSpans(first, last, ClickableSpan.class);
int a = Selection.getSelectionStart(buffer);
int b = Selection.getSelectionEnd(buffer);
int selStart = Math.min(a, b);
int selEnd = Math.max(a, b);
if (selStart < 0) {
if (buffer.getSpanStart(FROM_BELOW) >= 0) {
selStart = selEnd = buffer.length();
}
}
if (selStart > last)
selStart = selEnd = Integer.MAX_VALUE;
if (selEnd < first)
selStart = selEnd = -1;
switch (what) {
case CLICK:
if (selStart == selEnd) {
return false;
}
ClickableSpan[] link = buffer.getSpans(selStart, selEnd, ClickableSpan.class);
if (link.length != 1)
return false;
link[0].onClick(widget);
break;
case UP:
int beststart, bestend;
beststart = -1;
bestend = -1;
for (int i = 0; i < candidates.length; i++) {
int end = buffer.getSpanEnd(candidates[i]);
if (end < selEnd || selStart == selEnd) {
if (end > bestend) {
beststart = buffer.getSpanStart(candidates[i]);
bestend = end;
}
}
}
if (beststart >= 0) {
Selection.setSelection(buffer, bestend, beststart);
return true;
}
break;
case DOWN:
beststart = Integer.MAX_VALUE;
bestend = Integer.MAX_VALUE;
for (int i = 0; i < candidates.length; i++) {
int start = buffer.getSpanStart(candidates[i]);
if (start > selStart || selStart == selEnd) {
if (start < beststart) {
beststart = start;
bestend = buffer.getSpanEnd(candidates[i]);
}
}
}
if (bestend < Integer.MAX_VALUE) {
Selection.setSelection(buffer, beststart, bestend);
return true;
}
break;
}
return false;
}
private boolean action(int what, TextView widget, Spannable buffer) {
Layout layout = widget.getLayout();
int padding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom();
int areatop = widget.getScrollY();
int areabot = areatop + widget.getHeight() - padding;
int linetop = layout.getLineForVertical(areatop);
int linebot = layout.getLineForVertical(areabot);
int first = layout.getLineStart(linetop);
int last = layout.getLineEnd(linebot);
MyURLSpan[] candidates = buffer.getSpans(first, last, MyURLSpan.class);
int a = Selection.getSelectionStart(buffer);
int b = Selection.getSelectionEnd(buffer);
int selStart = Math.min(a, b);
int selEnd = Math.max(a, b);
if (selStart < 0) {
if (buffer.getSpanStart(FROM_BELOW) >= 0) {
selStart = selEnd = buffer.length();
}
}
if (selStart > last) {
selStart = selEnd = Integer.MAX_VALUE;
}
if (selEnd < first) {
selStart = selEnd = -1;
}
switch (what) {
case CLICK:
if (selStart == selEnd) {
return false;
}
MyURLSpan[] link = buffer.getSpans(selStart, selEnd, MyURLSpan.class);
if (link.length != 1) {
return false;
}
link[0].onClick(widget);
break;
case UP:
int best_start = -1;
int best_end = -1;
for (MyURLSpan candidate1 : candidates) {
int end = buffer.getSpanEnd(candidate1);
if (end < selEnd || selStart == selEnd) {
if (end > best_end) {
best_start = buffer.getSpanStart(candidate1);
best_end = end;
}
}
}
if (best_start >= 0) {
Selection.setSelection(buffer, best_end, best_start);
return true;
}
break;
case DOWN:
best_start = Integer.MAX_VALUE;
best_end = Integer.MAX_VALUE;
for (MyURLSpan candidate : candidates) {
int start = buffer.getSpanStart(candidate);
if (start > selStart || selStart == selEnd) {
if (start < best_start) {
best_start = start;
best_end = buffer.getSpanEnd(candidate);
}
}
}
if (best_end < Integer.MAX_VALUE) {
Selection.setSelection(buffer, best_start, best_end);
return true;
}
break;
}
return false;
}
@Override
public boolean onTouchEvent(@NonNull TextView widget, @NonNull Spannable buffer, @NonNull MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
Layout layout = widget.getLayout();
if (layout == null) {
return super.onTouchEvent(widget, buffer, event);
}
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
MyURLSpan[] link = buffer.getSpans(off, off, MyURLSpan.class);
if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
if (!mHasPerformedLongPress) {
link[0].onClick(widget);
}
pressed = false;
lastEvent = new float[2];
} else {
pressed = true;
lastEvent[0] = event.getX();
lastEvent[1] = event.getY();
checkForLongClick(link, widget);
}
return true;
}
} else if (action == MotionEvent.ACTION_MOVE) {
float[] position = {
event.getX(), event.getY()
};
// int slop =
// ViewConfiguration.get(widget.getContext()).getScaledTouchSlop();
int slop = 6;
float xInstance = Math.abs(lastEvent[0] - position[0]);
float yInstance = Math.abs(lastEvent[1] - position[1]);
double instance = Math.sqrt(Math.hypot(xInstance, yInstance));
if (instance > slop) {
pressed = false;
}
} else if (action == MotionEvent.ACTION_CANCEL) {
pressed = false;
lastEvent = new float[2];
} else {
pressed = false;
lastEvent = new float[2];
}
return super.onTouchEvent(widget, buffer, event);
}
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
try {
link[0].onClick(widget);
} catch (Exception e) {
e.printStackTrace();
ToastUtil.e(R.string.not_foud_qq);
}
if (((URLSpan) link[0]).getURL() != null) {
if (DonateActivity.zhifubao.startsWith(((URLSpan) link[0]).getURL())) {
UrlCountUtil.onEvent(UrlCountUtil.CLICK_SETTINGS_ABOUT_DONATE);
} else if (DonateActivity.qqJump.startsWith(((URLSpan) link[0]).getURL())) {
UrlCountUtil.onEvent(UrlCountUtil.CLICK_SETTINGS_JOIN_QQ);
}
}
} else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer, buffer.getSpanStart(link[0]), buffer.getSpanEnd(link[0]));
}
return true;
} else {
Selection.removeSelection(buffer);
}
}
return super.onTouchEvent(widget, buffer, event);
}