下面列出了android.text.Layout#getLineForOffset ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
private boolean deleteLine(View view, Editable content) {
if (view instanceof TextView) {
final Layout layout = ((TextView) view).getLayout();
if (layout != null) {
final int line = layout.getLineForOffset(Selection.getSelectionStart(content));
final int start = layout.getLineStart(line);
final int end = layout.getLineEnd(line);
if (end != start) {
content.delete(start, end);
return true;
}
}
}
return false;
}
@Override
public void onPopupChangePosition() {
try {
Layout layout = getLayout();
if (layout != null) {
int pos = getSelectionStart();
int line = layout.getLineForOffset(pos);
int baseline = layout.getLineBaseline(line);
int ascent = layout.getLineAscent(line);
float x = layout.getPrimaryHorizontal(pos);
float y = baseline + ascent;
int offsetHorizontal = (int) x + mLinePadding;
setDropDownHorizontalOffset(offsetHorizontal);
int heightVisible = getHeightVisible();
int offsetVertical = 0;
if (verticalScroll != null) {
offsetVertical = (int) ((y + mCharHeight) - verticalScroll.getScrollY());
} else {
offsetVertical = (int) ((y + mCharHeight) - getScrollY());
}
int tmp = offsetVertical + getDropDownHeight() + mCharHeight;
if (tmp < heightVisible) {
tmp = offsetVertical + mCharHeight / 2;
setDropDownVerticalOffset(tmp);
} else {
tmp = offsetVertical - getDropDownHeight() - mCharHeight;
setDropDownVerticalOffset(tmp);
}
}
} catch (Exception ignored) {
}
}
public int getSelectLine() {
Layout layout = getLayout();
if (getSelectionStart() != getSelectionEnd()) {
return -1;
}
return layout.getLineForOffset(getSelectionStart());
}
Rect getCursorRect() {
Layout layout = getLayout();
final int offset = getSelectionStart();
final int line = layout.getLineForOffset(offset);
final int top = layout.getLineTop(line);
final int bottom = layout.getLineTop(line + 1);
float horizontal = layout.getSecondaryHorizontal(offset);
// horizontal = Math.max(0.5f, horizontal - 0.5f);
int left = (int) (horizontal);
return new Rect(left, top, left + DpiUtils.dip2px(getContext(), 1), bottom);
}
private void jumpToOccurrence() {
Layout layout = getLayout();
if (layout == null) {
Log.w(TAG, "getLayout() is null");
} else if (getContent() == null || getContent().isEmpty()) {
Log.w(TAG, "getContent is null or empty");
} else if (currentOccurrence < 1) {
// if currentOccurrence is lower than 1, jump to last occurrence
currentOccurrence = occurrenceCount;
jumpToOccurrence();
} else if (searchQuery != null && !searchQuery.isEmpty()) {
String currentContent = getContent().toLowerCase();
int indexOfNewText = indexOfNth(currentContent, searchQuery.toLowerCase(), 0, currentOccurrence);
if (indexOfNewText <= 0) {
// Search term is not n times in text
// Go back to first search result
if (currentOccurrence != 1) {
currentOccurrence = 1;
jumpToOccurrence();
}
return;
}
String textUntilFirstOccurrence = currentContent.substring(0, indexOfNewText);
int numberLine = layout.getLineForOffset(textUntilFirstOccurrence.length());
if (numberLine >= 0) {
ScrollView scrollView = getScrollView();
if (scrollView != null) {
scrollView.post(() -> scrollView.smoothScrollTo(0, layout.getLineTop(numberLine)));
}
}
}
}
/**
* @return current line number of the cursor, or -1 if no cursor
*/
public int getCurrentCursorLine() {
int selectionStart = mMentionsEditText.getSelectionStart();
Layout layout = mMentionsEditText.getLayout();
if (layout != null && !(selectionStart == -1)) {
return layout.getLineForOffset(selectionStart);
}
return -1;
}
public void drawLeadingMargin(Canvas canvas, Paint paint, int x, int dir,
int top, int baseline, int bottom, CharSequence text,
int start, int end, boolean first, Layout layout) {
if (((Spanned)text).getSpanStart(this) == start) {
Style style = paint.getStyle();
paint.setStyle(Style.FILL);
float yPosition;
if (layout != null) {
int line = layout.getLineForOffset(start);
yPosition = (float)layout.getLineBaseline(line) - (float)this.bulletRadius * 2.0F;
} else {
yPosition = (float)(top + bottom) / 2.0F;
}
float xPosition = (float)(x + dir * this.bulletRadius);
if (canvas.isHardwareAccelerated()) {
if (this.mBulletPath == null) {
this.mBulletPath = new Path();
this.mBulletPath.addCircle(0.0F, 0.0F, (float)this.bulletRadius, Direction.CW);
}
canvas.save();
canvas.translate(xPosition, yPosition);
canvas.drawPath(this.mBulletPath, paint);
canvas.restore();
} else {
canvas.drawCircle(xPosition, yPosition, (float)this.bulletRadius, paint);
}
paint.setStyle(style);
}
}
public static void draw(TextView textView, Canvas canvas) {
Layout layout = textView.getLayout();
if (layout != null) {
CharSequence text = textView.getText();
if (text instanceof Spanned) {
Spanned spanned = (Spanned) text;
OverlineSpan[] spans = spanned.getSpans(0, spanned.length(), OverlineSpan.class);
if (spans != null && spans.length > 0) {
int paddingTop = textView.getTotalPaddingTop();
int paddingLeft = textView.getPaddingLeft();
int shift = (int) (textView.getTextSize() * 8f / 9f);
float thickness = textView.getTextSize() / 15f - 0.25f;
int color = textView.getCurrentTextColor();
PAINT.setColor(color);
PAINT.setStrokeWidth(thickness);
for (OverlineSpan span : spans) {
int start = spanned.getSpanStart(span);
int end = spanned.getSpanEnd(span);
int lineStart = layout.getLineForOffset(start);
int lineEnd = layout.getLineForOffset(end);
for (int i = lineStart; i <= lineEnd; i++) {
float left = i == lineStart ? layout.getPrimaryHorizontal(start) : layout.getLineLeft(i);
float right = i == lineEnd ? layout.getPrimaryHorizontal(end) : layout.getLineRight(i);
float top = layout.getLineBaseline(i) - shift + 0.5f;
canvas.drawLine(paddingLeft + left, paddingTop + top, paddingLeft + right,
paddingTop + top, PAINT);
}
}
}
}
}
}
@Override
public void drawLeadingMargin(Canvas canvas, Paint paint, int x, int dir,
int top, int baseline, int bottom,
CharSequence text, int start, int end,
boolean first, Layout layout) {
if (((Spanned) text).getSpanStart(this) == start) {
Paint.Style style = paint.getStyle();
int oldcolor = 0;
if (mWantColor) {
oldcolor = paint.getColor();
paint.setColor(mColor);
}
paint.setStyle(Paint.Style.FILL);
if (layout != null) {
// "bottom" position might include extra space as a result of line spacing
// configuration. Subtract extra space in order to show bullet in the vertical
// center of characters.
final int line = layout.getLineForOffset(start);
int spacing = line != layout.getLineCount() - 1 ? (int) layout.getSpacingAdd() : 0;
bottom = bottom - spacing;
}
final float yPosition = (top + bottom) / 2f;
final float xPosition = x + dir * mBulletRadius;
canvas.drawCircle(xPosition, yPosition, mBulletRadius, paint);
if (mWantColor) {
paint.setColor(oldcolor);
}
paint.setStyle(style);
}
}
private boolean updateCursorPosition() {
final Layout layout = getLayout();
final int offset = getSelectionStart();
final int line = layout.getLineForOffset(offset);
final int top = layout.getLineTop(line);
final int bottom = layout.getLineTop(line + 1);
updateCursorPosition(top, bottom, layout.getPrimaryHorizontal(offset));
return true;
}
@Override
public void drawLeadingMargin(Canvas canvas, Paint paint, int x, int dir,
int top, int baseline, int bottom,
CharSequence text, int start, int end,
boolean first, Layout layout) {
if (((Spanned) text).getSpanStart(this) == start) {
Paint.Style style = paint.getStyle();
int oldcolor = 0;
if (mWantColor) {
oldcolor = paint.getColor();
paint.setColor(mColor);
}
paint.setStyle(Paint.Style.FILL);
if (layout != null) {
// "bottom" position might include extra space as a result of line spacing
// configuration. Subtract extra space in order to show bullet in the vertical
// center of characters.
final int line = layout.getLineForOffset(start);
int spacing = line != layout.getLineCount() - 1 ? (int) layout.getSpacingAdd() : 0;
bottom = bottom - spacing;
}
final float yPosition = (top + bottom) / 2f;
final float xPosition = x + dir * mBulletRadius;
canvas.drawCircle(xPosition, yPosition, mBulletRadius, paint);
if (mWantColor) {
paint.setColor(oldcolor);
}
paint.setStyle(style);
}
}
private boolean updateCursorPosition() {
final Layout layout = getLayout();
final int offset = getSelectionStart();
final int line = layout.getLineForOffset(offset);
final int top = layout.getLineTop(line);
final int bottom = layout.getLineTop(line + 1);
updateCursorPosition(top, bottom, layout.getPrimaryHorizontal(offset));
return true;
}
protected void onPopupChangePosition() {
try {
Layout layout = getLayout();
if (layout != null) {
int pos = getSelectionStart();
int line = layout.getLineForOffset(pos);
int baseline = layout.getLineBaseline(line);
int ascent = layout.getLineAscent(line);
Rect bounds = new Rect();
Paint textPaint = getPaint();
String sample="A";
textPaint.getTextBounds(sample, 0, sample.length(), bounds);
int width = bounds.width()/sample.length();
float x = layout.getPrimaryHorizontal(pos);
float y = baseline + ascent;
int offsetHorizontal = (int) x + mGutterWidth;
setDropDownHorizontalOffset(offsetHorizontal);
int heightVisible = getHeightVisible();
int offsetVertical = (int) ((y + mCharHeight) - getScrollY());
int tmp = offsetVertical + getDropDownHeight() + mCharHeight;
//if (tmp < heightVisible) {
tmp = -h + ((offsetVertical*2 / (mCharHeight)) * (mCharHeight / 2))+(mCharHeight/2);
setDropDownVerticalOffset(tmp);
//((Activity)(mContext)).setTitle("ov :"+offsetVertical +" ch "+mCharHeight+" tmp"+tmp +"h "+h+"p:"+pos);
// } else {
// tmp = offsetVertical - getDropDownHeight() - mCharHeight;
// setDropDownVerticalOffset(tmp);
// ((Activity)(mContext)).setTitle(" 2 tmp :"+tmp);
// }
// int pos = getSelectionStart();
// int line = layout.getLineForOffset(pos);
// int baseline = layout.getLineBaseline(line);
// int ascent = layout.getLineAscent(line);
//
// float x = layout.getPrimaryHorizontal(pos);
// float y = baseline + ascent;
//
// int offsetHorizontal = (int) x + mGutterWidth;
// setDropDownHorizontalOffset(offsetHorizontal);
//
// // int heightVisible = getHeightVisible();
// int offsetVertical = (int) ((y + mCharHeight) - getScrollY());
//
// int tmp = offsetVertical + getDropDownHeight() + mCharHeight;
//// if (tmp < heightVisible) {
// tmp = -(offsetVertical + mCharHeight) + ((offsetVertical / mCharHeight) * (mCharHeight / 2));
// setDropDownVerticalOffset(tmp);
//// } else {
//// tmp = offsetVertical - getDropDownHeight() - mCharHeight;
//// setDropDownVerticalOffset(tmp);
//// }
}
} catch (Exception e) {
Logger.error(TAG, e);
}
}
@Override
public void onDraw(@NonNull Canvas canvas) {
if (!isInEditMode()) {
int top;
Layout layout = getLayout();
if (layout != null && codeEditor != null && mHighlightCurrentLine) {
int currentLineStart = codeEditor.getLineForIndex(getSelectionStart());
if (currentLineStart == codeEditor.getLineForIndex(getSelectionEnd())) {
int selectedLineStartIndex = codeEditor.getIndexForStartOfLine(currentLineStart);
int selectedLineEndIndex = codeEditor.getIndexForEndOfLine(currentLineStart);
int topVisualLine = layout.getLineForOffset(selectedLineStartIndex);
int bottomVisualLine = layout.getLineForOffset(selectedLineEndIndex);
int left = mGutterWidth;
if (!mShowLineNumbers)
left = 0; //убираем отступ для Paint'а если номера строк выключены
top = layout.getLineTop(topVisualLine) + getPaddingTop();
int right = (layout.getWidth() + getPaddingLeft()) + getPaddingRight();
int bottom = layout.getLineBottom(bottomVisualLine) + getPaddingTop();
canvas.drawRect(left, top, right, bottom, mSelectedLinePaint);
}
}
super.onDraw(canvas);
if (layout != null && mShowLineNumbers) {
int prevLineNumber = -1;
canvas.drawRect(getScrollX(), getScrollY(),
mGutterWidth + getScrollX(),
getScrollY() + getHeight(), mGutterBackgroundPaint);
int paddingTop = getPaddingTop();
int max = mLineUtils.getBottomVisibleLine(this);
int textRight = (mGutterWidth - mIdealMargin / 2) + getScrollX();
if (codeEditor != null) {
int i = mLineUtils.getTopVisibleLine(this);
if (i >= 2) {
i -= 2;
} else {
i = 0;
}
while (i <= max) {
int number = codeEditor.getLineForIndex(getLayout().getLineStart(i));
if (number != prevLineNumber) {
canvas.drawText(Integer.toString(number + 1), textRight,
layout.getLineBaseline(i) + paddingTop, mLineNumberPaint);
}
prevLineNumber = number;
i++;
}
top = getScrollY();
canvas.drawLine(mGutterWidth + getScrollX(), top,
mGutterWidth + getScrollX(), top + getHeight(), mLinePaint);
}
}
}
}
RNLinearTextGradientSpan(
float[] locations,
int[] colors,
float[] start,
float[] end,
boolean useViewFrame,
Layout layout,
int textStart,
int textEnd,
float maxWidth,
float maxHeight,
String text
) {
if (
start != null &&
end != null &&
colors != null &&
locations != null &&
text != null &&
!YogaConstants.isUndefined(maxWidth) &&
maxWidth != 0 &&
maxHeight != 0 &&
layout != null
) {
Path path = new Path();
RectF bounds = new RectF();
layout.getSelectionPath(textStart, textEnd, path);
path.computeBounds(bounds, true);
if (layout.getLineForOffset(textEnd - 1) != layout.getLineForOffset(textEnd)) {
RectF lastSymbolBounds = new RectF();
layout.getSelectionPath(textEnd - 1, textEnd, path);
path.computeBounds(lastSymbolBounds, true);
if (lastSymbolBounds.contains(bounds) && bounds.contains(lastSymbolBounds)) {
layout.getSelectionPath(textStart, textEnd - 1, path);
path.computeBounds(bounds, true);
}
}
float width = useViewFrame ? maxWidth : bounds.width();
float height = useViewFrame ? maxHeight : bounds.height();
float x0 = (useViewFrame ? 0 : bounds.left) + start[0] * width;
float y0 = (useViewFrame ? 0 : bounds.top) + start[1] * height;
float x1 = (useViewFrame ? 0 : bounds.left) + end[0] * width;
float y1 = (useViewFrame ? 0 : bounds.top) + end[1] * height;
mGradient = new LinearGradient(x0, y0, x1, y1, colors, locations, Shader.TileMode.CLAMP);
}
}
@OnPopulateExtraAccessibilityNode
static void onPopulateExtraAccessibilityNode(
AccessibilityNodeInfoCompat node,
int extraNodeIndex,
int componentBoundsLeft,
int componentBoundsTop,
@Prop(resType = ResType.STRING) CharSequence text,
@FromBoundsDefined Layout textLayout,
@FromBoundsDefined ClickableSpan[] clickableSpans) {
if (!(text instanceof Spanned)) {
return;
}
final Spanned spanned = (Spanned) text;
final ClickableSpan span = clickableSpans[extraNodeIndex];
final int start = spanned.getSpanStart(span);
final int end = spanned.getSpanEnd(span);
final int startLine = textLayout.getLineForOffset(start);
final int endLine = textLayout.getLineForOffset(end);
// The bounds for multi-line strings should *only* include the first line. This is because
// Talkback triggers its click at the center point of these bounds, and if that center point
// is outside the spannable, it will click on something else. There is no harm in not outlining
// the wrapped part of the string, as the text for the whole string will be read regardless of
// the bounding box.
final int selectionPathEnd =
startLine == endLine ? end : textLayout.getLineVisibleEnd(startLine);
textLayout.getSelectionPath(start, selectionPathEnd, sTempPath);
sTempPath.computeBounds(sTempRectF, /* unused */ true);
sTempRect.set(
componentBoundsLeft + (int) sTempRectF.left,
componentBoundsTop + (int) sTempRectF.top,
componentBoundsLeft + (int) sTempRectF.right,
componentBoundsTop + (int) sTempRectF.bottom);
if (sTempRect.isEmpty()) {
// Text is not actually visible.
// Override bounds so it doesn't crash ExploreByTouchHelper.java
sTempRect.set(0, 0, 1, 1);
node.setBoundsInParent(sTempRect);
node.setContentDescription(""); // make node non-focusable
return;
}
node.setBoundsInParent(sTempRect);
node.setClickable(true);
node.setFocusable(true);
node.setEnabled(true);
node.setVisibleToUser(true);
node.setText(spanned.subSequence(start, end));
if (span instanceof AccessibleClickableSpan) {
AccessibleClickableSpan accessibleClickableSpan = (AccessibleClickableSpan) span;
String contentDescription = accessibleClickableSpan.getAccessibilityDescription();
String role = accessibleClickableSpan.getAccessibilityRole();
if (contentDescription != null) {
node.setContentDescription(contentDescription);
}
if (role != null) {
node.setClassName(role);
} else {
node.setClassName(AccessibilityRole.BUTTON);
}
} else {
node.setClassName(AccessibilityRole.BUTTON);
}
}
private void draw(
@NonNull View view,
@NonNull Canvas canvas,
@NonNull Layout layout
) {
final CharSequence cs = layout.getText();
if (!(cs instanceof Spanned)) {
return;
}
final Spanned spanned = (Spanned) cs;
final int save = canvas.save();
try {
canvas.translate(view.getPaddingLeft(), view.getPaddingTop());
// TODO: block?
// TODO: we must remove _original_ spans
// TODO: cache (attach a listener?)
// TODO: editor?
final CodeSpan[] spans = spanned.getSpans(0, spanned.length(), CodeSpan.class);
if (spans != null && spans.length > 0) {
for (CodeSpan span : spans) {
final int startOffset = spanned.getSpanStart(span);
final int endOffset = spanned.getSpanEnd(span);
final int startLine = layout.getLineForOffset(startOffset);
final int endLine = layout.getLineForOffset(endOffset);
// do we need to round them?
final float left = layout.getPrimaryHorizontal(startOffset)
+ (-1 * layout.getParagraphDirection(startLine) * paddingHorizontal);
final float right = layout.getPrimaryHorizontal(endOffset)
+ (layout.getParagraphDirection(endLine) * paddingHorizontal);
final float top = getLineTop(layout, startLine, paddingVertical);
final float bottom = getLineBottom(layout, endLine, paddingVertical);
Debug.i(new RectF(left, top, right, bottom).toShortString());
if (startLine == endLine) {
canvas.drawRect(left, top, right, bottom, paint);
} else {
// draw first line (start until the lineEnd)
// draw everything in-between (startLine - endLine)
// draw last line (lineStart until the end
canvas.drawRect(
left,
top,
layout.getLineRight(startLine),
getLineBottom(layout, startLine, paddingVertical),
paint
);
for (int line = startLine + 1; line < endLine; line++) {
canvas.drawRect(
layout.getLineLeft(line),
getLineTop(layout, line, paddingVertical),
layout.getLineRight(line),
getLineBottom(layout, line, paddingVertical),
paint
);
}
canvas.drawRect(
layout.getLineLeft(endLine),
getLineTop(layout, endLine, paddingVertical),
right,
getLineBottom(layout, endLine, paddingVertical),
paint
);
}
}
}
} finally {
canvas.restoreToCount(save);
}
}