下面列出了android.text.Layout#getLineTop ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Override
public void drawLeadingMargin(@NonNull Canvas c, @NonNull Paint p, int x, int dir,
int top, int baseline, int bottom,
@NonNull CharSequence text, int start, int end,
boolean first, @NonNull Layout layout) {
int st = ((Spanned) text).getSpanStart(this);
int ix = (int) x;
int itop = (int) layout.getLineTop(layout.getLineForOffset(st));
int dw = mDrawable.getIntrinsicWidth();
int dh = mDrawable.getIntrinsicHeight();
// XXX What to do about Paint?
mDrawable.setBounds(ix, itop, ix + dw, itop + dh);
mDrawable.draw(c);
}
/**
* 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;
}
/**
* Performs a scroll down action.
* Scrolls down 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 scrollDown(TextView widget, Spannable buffer, int amount) {
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;
}
final int limit = layout.getLineCount() - 1;
if (bottomLine <= limit) {
bottomLine = Math.min(bottomLine + amount - 1, limit);
Touch.scrollTo(widget, layout, widget.getScrollX(),
layout.getLineTop(bottomLine + 1) - innerHeight);
return true;
}
return false;
}
private BetterLinkMovementExtended.ClickableSpanWithText findClickableSpanUnderTouch(TextView textView, Spannable text, MotionEvent event) {
int touchX = (int) event.getX();
int touchY = (int) event.getY();
touchX -= textView.getTotalPaddingLeft();
touchY -= textView.getTotalPaddingTop();
touchX += textView.getScrollX();
touchY += textView.getScrollY();
Layout layout = textView.getLayout();
int touchedLine = layout.getLineForVertical(touchY);
int touchOffset = layout.getOffsetForHorizontal(touchedLine, (float) touchX);
this.touchedLineBounds.left = layout.getLineLeft(touchedLine);
this.touchedLineBounds.top = (float) layout.getLineTop(touchedLine);
this.touchedLineBounds.right = layout.getLineWidth(touchedLine) + this.touchedLineBounds.left;
this.touchedLineBounds.bottom = (float) layout.getLineBottom(touchedLine);
if (this.touchedLineBounds.contains((float) touchX, (float) touchY)) {
Object[] spans = text.getSpans(touchOffset, touchOffset, SPAN_CLASS);
for (Object span : spans) {
if (span instanceof ClickableSpan) {
return ClickableSpanWithText.ofSpan(textView, (ClickableSpan) span);
}
}
return null;
} else {
return null;
}
}
@Override
protected void onDraw(Canvas canvas) {
int height = getHeight();
Layout layout = getLayout();
int maxHeight = 0;
for (int i = 0; i < layout.getLineCount(); i++) {
int bottom = layout.getLineBottom(i);
if (bottom > height) {
maxHeight = layout.getLineTop(i);
break;
}
}
if (maxHeight > 0) {
canvas.save();
canvas.clipRect(0, 0, getWidth(), maxHeight);
}
super.onDraw(canvas);
OverlineSpan.draw(this, canvas);
if (maxHeight > 0) {
canvas.restore();
}
}
@Override
public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
int top, int baseline, int bottom,
CharSequence text, int start, int end,
boolean first, Layout layout) {
int st = ((Spanned) text).getSpanStart(this);
int itop = layout.getLineTop(layout.getLineForOffset(st));
if (dir < 0) {
x -= mBitmap.getWidth();
}
c.drawBitmap(mBitmap, x, itop, p);
}
/**
* @param line - current line
* @param column - column of line
* @return Position (in pixels) for edittext at line and column
*/
public Point getDebugPosition(int line, int column, int gravity) {
Layout layout = getLayout();
if (layout != null) {
int pos = layout.getLineStart(line) + column;
int baseline = layout.getLineBaseline(line);
int ascent = layout.getLineAscent(line);
int offsetHorizontal = (int) layout.getPrimaryHorizontal(pos) + mLinePadding; //x
float y;
int offsetVertical = 0;
if (gravity == Gravity.BOTTOM) {
y = baseline + ascent;
if (verticalScroll != null) {
offsetVertical = (int) ((y + mCharHeight) - verticalScroll.getScrollY());
} else {
offsetVertical = (int) ((y + mCharHeight) - getScrollY());
}
return new Point(offsetHorizontal, offsetVertical);
} else if (gravity == Gravity.TOP) {
y = layout.getLineTop(line);
if (verticalScroll != null) {
offsetVertical = (int) (y - verticalScroll.getScrollY());
} else {
offsetVertical = (int) (y - getScrollY());
}
return new Point(offsetHorizontal, offsetVertical);
}
return new Point(offsetHorizontal, offsetVertical);
}
return new Point();
}
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);
}
/**
* Gets the span that was touched.
* @param tv {@link TextView}
* @param span {@link Spannable}
* @param e {@link MotionEvent}
* @return {@link TouchableSpan}
*/
private TouchableSpan getTouchedSpan(TextView tv, Spannable span, MotionEvent e) {
// Find the location in which the touch was made
int x = (int)e.getX();
int y = (int)e.getY();
// Ignore padding
x -= tv.getTotalPaddingLeft();
y -= tv.getTotalPaddingTop();
// Account for scrollable text
x += tv.getScrollX();
y += tv.getScrollY();
final Layout layout = tv.getLayout();
final int touchedLine = layout.getLineForVertical(y);
final int touchOffset = layout.getOffsetForHorizontal(touchedLine, x);
// Set bounds of the touched line
touchBounds.left = layout.getLineLeft(touchedLine);
touchBounds.top = layout.getLineTop(touchedLine);
touchBounds.right = layout.getLineRight(touchedLine);
touchBounds.bottom = layout.getLineBottom(touchedLine);
// Ensure the span falls within the bounds of the touch
TouchableSpan touchSpan = null;
if (touchBounds.contains(x, y)) {
// Find clickable spans that lie under the touched area
TouchableSpan[] spans = span.getSpans(touchOffset, touchOffset, TouchableSpan.class);
touchSpan = (spans.length > 0) ? spans[0] : null;
}
return touchSpan;
}
public static int getLineTopWithoutPadding(@NonNull Layout layout, int line) {
final int top = layout.getLineTop(line);
if (line == 0) {
return top - layout.getTopPadding();
}
return top;
}
private static float getLineTop(@NonNull Layout layout, int line, float padding) {
float value = layout.getLineTop(line) - padding;
if (line == 0) {
value -= layout.getTopPadding();
}
return value;
}
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;
}
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;
}
private static int getCurrentLineTop(Spannable buffer, Layout layout) {
return layout.getLineTop(layout.getLineForOffset(Selection.getSelectionEnd(buffer)));
}
@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);
}
}
}
}
private void drawText(Canvas canvas) {
Map<Integer, String> lineNumbles = getLineNumbers();
Layout layout = getLayout();
int selectLine = getSelectLine();
int range[] = new int[4];
{// 计算需要绘制的行号所需要的范围
int clipLeft = 0;
int clipTop = (getScrollView().getScrollY() == 0) ? 0
: getExtendedPaddingTop() + getScrollView().getScrollY()
- getScrollView().getPaddingTop();
int clipRight = getWidth();
int clipBottom = clipTop + getScrollView().getHeight();
Rect rect = new Rect(clipLeft, clipTop, clipRight, clipBottom);
getLineRangeForDraw(rect, range);
}
int firstLine = range[0];
int lastLine = range[1];
if (firstLine < 0) {
return;
}
int previousLineBottom = layout.getLineTop(firstLine);
int previousLineEnd = layout.getLineStart(firstLine);
int lineCount = getLineCount();
Paint paint = getPaint();
for (int lineNum = firstLine; lineNum <= lastLine
&& lineNum < lineCount; lineNum++) {
int start = previousLineEnd;
previousLineEnd = layout.getLineStart(lineNum + 1);
int end = layout.getLineVisibleEnd(lineNum);
int ltop = previousLineBottom;
int lbottom = layout.getLineTop(lineNum + 1);
previousLineBottom = lbottom;
int lbaseline = lbottom - layout.getLineDescent(lineNum);
int left = getPaddingLeft();
// 绘制选择行背景
if (lineNum == selectLine) {
paint.setColor(lineNumberBackgroundColor);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(mLineNumberBgStrokeWidth);
canvas.drawRect(getPaddingLeft() - mLineNumberBgStrokeWidth, ltop, getRight() - getPaddingRight() + mLineNumberBgStrokeWidth, lbottom, paint);
paint.setStyle(Paint.Style.FILL);
}
// 绘制行号
String lineNumberText = lineNumbles.get(lineNum);
if (lineNumberText != null) {
paint.setColor(lineNumberColor);
canvas.drawText(lineNumberText, 0, lineNumberText.length(), mNumberPadding,
lbaseline, paint);
}
int textLength = getText().length();
// 绘制文字
if (start < textLength) {
//计算需要绘制的文字位置
//获取改行所有文字宽度
float[] widths = new float[end - start + 1];
paint.getTextWidths(getText(), start, end, widths);
//计算获取看到的文字第一个位置,和对应的偏移x
float firstNeedDrawPos[] = getLineFirstCharPosForDraw(widths);
int firstPos = (int) firstNeedDrawPos[0];
float offsetX = firstNeedDrawPos[1];
int maxOffX = getViewScrollX() + getVisibleWidth();
// 文字着色
for (int i = start + firstPos; i < end && i < textLength; ) {
if (offsetX > maxOffX) {
break;
}
int color = getCodeColor(i);
{
float fontWidths = widths[i - start];
int fontCount = 1;
for (int j = i + 1; j < end && j < textLength; j++) {
if (color == getCodeColor(j)) {
fontCount++;
fontWidths += widths[j - start];
} else {
break;
}
}
if (color == 0) {
color = defaultTextColor;
}
paint.setColor(color);
canvas.drawText(getText(), i, i + fontCount, left + offsetX, lbaseline, paint);
i += fontCount;
offsetX += fontWidths;
}
}
}
}
}
public static int getLineHeight(@NonNull Layout layout, int line) {
return layout.getLineTop(line + 1) - layout.getLineTop(line);
}
private static float getLineHeight(@NonNull Layout layout, int line) {
return layout.getLineTop(line + 1) - layout.getLineTop(line);
}
/**
* {@inheritDoc}
*/
public void displaySuggestions(boolean display) {
// If nothing to change, return early
if (display == isDisplayingSuggestions() || mMentionsEditText == null) {
return;
}
// Change view depending on whether suggestions are being shown or not
if (display) {
disableSpellingSuggestions(true);
mTextCounterView.setVisibility(View.GONE);
mSuggestionsList.setVisibility(View.VISIBLE);
mPrevEditTextParams = mMentionsEditText.getLayoutParams();
mPrevEditTextBottomPadding = mMentionsEditText.getPaddingBottom();
mMentionsEditText.setPadding(mMentionsEditText.getPaddingLeft(), mMentionsEditText.getPaddingTop(), mMentionsEditText.getPaddingRight(), mMentionsEditText.getPaddingTop());
int height = mMentionsEditText.getPaddingTop() + mMentionsEditText.getLineHeight() + mMentionsEditText.getPaddingBottom();
mMentionsEditText.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, height));
mMentionsEditText.setVerticalScrollBarEnabled(false);
int cursorLine = getCurrentCursorLine();
Layout layout = mMentionsEditText.getLayout();
if (layout != null) {
int lineTop = layout.getLineTop(cursorLine);
mMentionsEditText.scrollTo(0, lineTop);
}
// Notify action listener that list was shown
if (mActionListener != null) {
mActionListener.onSuggestionsDisplayed();
}
} else {
disableSpellingSuggestions(false);
mTextCounterView.setVisibility(mDisplayTextCount ? View.VISIBLE : View.GONE);
mSuggestionsList.setVisibility(View.GONE);
mMentionsEditText.setPadding(mMentionsEditText.getPaddingLeft(), mMentionsEditText.getPaddingTop(), mMentionsEditText.getPaddingRight(), mPrevEditTextBottomPadding);
if (mPrevEditTextParams == null) {
mPrevEditTextParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
mMentionsEditText.setLayoutParams(mPrevEditTextParams);
mMentionsEditText.setVerticalScrollBarEnabled(true);
// Notify action listener that list was hidden
if (mActionListener != null) {
mActionListener.onSuggestionsHidden();
}
}
requestLayout();
invalidate();
}