下面列出了android.text.Layout#getLineStart ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Override
protected void onDraw(Canvas canvas) {
int count = getLineCount();
final Layout layout = getLayout();
float xStart, xStop, xDiff;
int firstCharInLine, lastCharInLine;
for (int i = 0; i < count; i++) {
int baseline = getLineBounds(i, mRect);
firstCharInLine = layout.getLineStart(i);
lastCharInLine = layout.getLineEnd(i);
xStart = layout.getPrimaryHorizontal(firstCharInLine);
xDiff = layout.getPrimaryHorizontal(firstCharInLine + 1) - xStart;
xStop = layout.getPrimaryHorizontal(lastCharInLine - 1) + xDiff;
canvas.drawLine(xStart,
baseline + mStrokeWidth,
xStop,
baseline + mStrokeWidth,
mPaint);
}
super.onDraw(canvas);
}
@Override
protected void onDraw(Canvas canvas) {
TextPaint paint = getPaint();
paint.setColor(getCurrentTextColor());
paint.drawableState = getDrawableState();
mViewWidth = getMeasuredWidth();
String text = (String) getText();
mLineY = 0;
mLineY += getTextSize();
Layout layout = getLayout();
for (int i = 0; i < layout.getLineCount(); i++) {
int lineStart = layout.getLineStart(i);
int lineEnd = layout.getLineEnd(i);
String line = text.substring(lineStart, lineEnd);
float width = StaticLayout.getDesiredWidth(text, lineStart, lineEnd, getPaint());
if (needScale(line)) {
drawScaledText(canvas, lineStart, line, width);
} else {
canvas.drawText(line, 0, mLineY, paint);
}
mLineY += getLineHeight();
}
}
private void drawStrokeBorder(Canvas canvas){
Layout layout = getLayout();
if(layout!=null){
String text = getText().toString();
int lineCount = getLineCount();
//第一行文字顶部与控件顶部的距离
int totalPaddingTop = getTotalPaddingTop();
for (int i = 0; i < lineCount; i++) {
int start = layout.getLineStart(i);
int end = layout.getLineEnd(i);
String rowStr = text.substring(start, end);
float x = layout.getLineLeft(i)+getPaddingLeft();
int y = totalPaddingTop + getBaseLine(i);
Path textPath = new Path();
getPaint().getTextPath(rowStr,0,rowStr.length(),x,y,textPath);
mBorderPaint.setColor(mBorderColor);
canvas.drawPath(textPath,mBorderPaint);
}
}
}
private void translateText(Layout layout, BufferType type) {
if (layout.getLineCount() > mShowMaxLine) {
SpannableStringBuilder span = new SpannableStringBuilder();
int start = layout.getLineStart(mShowMaxLine - 1);
int end = layout.getLineVisibleEnd(mShowMaxLine - 1);
TextPaint paint = getPaint();
StringBuilder builder = new StringBuilder(ELLIPSIZE_END);
if (mTipGravity == END) {
builder.append(" ").append(mFoldText);
end -= paint.breakText(mOriginalText, start, end, false, paint.measureText(builder.toString()), null) + 1;
float x = getWidth() - getPaddingLeft() - getPaddingRight() - getTextWidth(ELLIPSIZE_END.concat(mFoldText));
while (layout.getPrimaryHorizontal(end - 1) + getTextWidth(mOriginalText.subSequence(end - 1, end).toString()) < x) {
end++;
}
end -= 2;
} else {
end -= paint.breakText(mOriginalText, start, end, false, paint.measureText(builder.toString()), null) + 1;
}
CharSequence ellipsize = mOriginalText.subSequence(0, end);
span.append(ellipsize);
span.append(ELLIPSIZE_END);
addTip(span, type);
}
}
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;
}
/**
* 重绘文字,两端对齐
*
* @param canvas
*/
private void drawTextWithJustify(Canvas canvas) {
// 文字画笔
TextPaint textPaint = getPaint();
textPaint.setColor(getCurrentTextColor());
textPaint.drawableState = getDrawableState();
String text_str = getText().toString();
// 当前所在行的Y向偏移
int currentLineOffsetY = getPaddingTop();
currentLineOffsetY += getTextSize();
Layout layout = getLayout();
//循环每一行,绘制文字
for (int i = 0; i < layout.getLineCount(); i++) {
int lineStart = layout.getLineStart(i);
int lineEnd = layout.getLineEnd(i);
//获取到TextView每行中的内容
String line_str = text_str.substring(lineStart, lineEnd);
// 获取每行字符串的宽度(不包括字符间距)
float desiredWidth = StaticLayout.getDesiredWidth(text_str, lineStart, lineEnd, getPaint());
if (isLineNeedJustify(line_str)) {
//最后一行不需要重绘
if (i == layout.getLineCount() - 1) {
canvas.drawText(line_str, getPaddingLeft(), currentLineOffsetY, textPaint);
} else {
drawJustifyTextForLine(canvas, line_str, desiredWidth, currentLineOffsetY);
}
} else {
canvas.drawText(line_str, getPaddingLeft(), currentLineOffsetY, textPaint);
}
//更新行Y向偏移
currentLineOffsetY += getLineHeight();
}
}
@Override
protected void onDraw(Canvas canvas) {
TextPaint paint = getPaint();
paint.setColor(getCurrentTextColor());
paint.drawableState = getDrawableState();
mViewWidth = getMeasuredWidth();
String text = getText().toString();
mLineY = 0;
mLineY += getTextSize();
Layout layout = getLayout();
// layout.getLayout()在4.4.3出现NullPointerException
if (layout == null) {
return;
}
Paint.FontMetrics fm = paint.getFontMetrics();
int textHeight = (int) (Math.ceil(fm.descent - fm.ascent));
textHeight = (int) (textHeight * layout.getSpacingMultiplier() + layout
.getSpacingAdd());
for (int i = 0; i < layout.getLineCount(); i++) {
int lineStart = layout.getLineStart(i);
int lineEnd = layout.getLineEnd(i);
float width = StaticLayout.getDesiredWidth(text, lineStart,
lineEnd, getPaint());
String line = text.substring(lineStart, lineEnd);
if (needScale(line)) {
drawScaledText(canvas, lineStart, line, width);
} else {
canvas.drawText(line, 0, mLineY, paint);
}
mLineY += textHeight;
}
}
/**
* Truncates text which is too long and appends the given custom ellipsis CharSequence to the end
* of the visible text.
*
* @param text Text to truncate
* @param customEllipsisText Text to append to the end to indicate truncation happened
* @param newLayout A Layout object populated with measurement information for this text
* @param ellipsizedLineNumber The line number within the text at which truncation occurs (i.e.
* the last visible line).
* @return The provided text truncated in such a way that the 'customEllipsisText' can appear at
* the end.
*/
private static CharSequence truncateText(
CharSequence text,
CharSequence customEllipsisText,
Layout newLayout,
int ellipsizedLineNumber,
float layoutWidth) {
Rect bounds = new Rect();
newLayout
.getPaint()
.getTextBounds(customEllipsisText.toString(), 0, customEllipsisText.length(), bounds);
// Identify the X position at which to truncate the final line:
// Note: The left position of the line is needed for the case of RTL text.
final float ellipsisTarget =
layoutWidth - bounds.width() + newLayout.getLineLeft(ellipsizedLineNumber);
// Get character offset number corresponding to that X position:
int ellipsisOffset = newLayout.getOffsetForHorizontal(ellipsizedLineNumber, ellipsisTarget);
if (ellipsisOffset > 0) {
// getOffsetForHorizontal returns the closest character, but we need to guarantee no
// truncation, so subtract 1 from the result:
ellipsisOffset -= 1;
// Ensure that we haven't chosen an ellipsisOffset that's past the end of the ellipsis start.
// This can occur in several cases, including when the width of the customEllipsisText is less
// than the width of the default ellipsis character, and when in RTL mode and there is
// whitespace to the left of the text. In these cases, getOffsetForHorizontal will return the
// end of the string because our ellipsisTarget was in the middle of the ellipsis character.
if (newLayout.getEllipsisCount(ellipsizedLineNumber) > 0) {
final int ellipsisStart =
newLayout.getLineStart(ellipsizedLineNumber)
+ newLayout.getEllipsisStart(ellipsizedLineNumber);
if (ellipsisOffset > ellipsisStart) {
ellipsisOffset = ellipsisStart;
}
}
return TextUtils.concat(text.subSequence(0, ellipsisOffset), customEllipsisText);
} else {
return text;
}
}
/**
* @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();
}
/**
* Gets the lineInfo from the index of the letter in the text
*/
public static int getLineFromIndex(int index, int lineCount, Layout layout) {
int line;
int currentIndex = 0;
for (line = 0; line < lineCount; line++) {
currentIndex += layout.getLineEnd(line) - layout.getLineStart(line);
if (currentIndex >= index) {
break;
}
}
return line;
}
public static int getStartIndexAtLine(EditText editable, int line) {
Layout layout = editable.getLayout();
if (layout != null) {
return layout.getLineStart(line);
}
return 0;
}
/**
* 得到最多行那个 最后一个字符的位置,与第一个位置 测出宽度 与 扩展字符 相加 如果大于行宽, 就把endPos--,知道小于。
* 最后 截取字符 0-endPos 然后拼接 扩展字符 去显示即可!
*/
private void setExpand() {
calExpand = true;
Layout layout = getLayout();
int endPos = layout.getLineEnd(maxLine - 1);
int startPos = layout.getLineStart(maxLine - 1);
TextPaint pt = getPaint();
//如果宽度>测量宽度 就继续 --
while (Layout.getDesiredWidth(getText(), startPos, endPos, pt) + Layout.getDesiredWidth(new SpannedString(EXPAND), pt) > layout.getWidth()
&& --endPos > startPos) {
}
SpannableStringBuilder nowSb = new SpannableStringBuilder(getText().subSequence(0, endPos)).append(EXPAND);
setText(nowSb);
}
/**
* 判断是否需要更新文本
* @return
*/
private boolean isUpdateText(Layout layout,String text){
boolean update = false;
if(!mLastText.equals(text)) {
update = true;
}else{
int lineCount = layout.getLineCount();
int size = mLineDataList.size();
if(lineCount != size){
update = true;
}else{
for (int i = 0; i < lineCount; i++) {
int start = layout.getLineStart(i);
int end =layout.getLineEnd(i);
String rowStr = text.substring(start, end);
//去除掉换行符
if(rowStr.contains(SYM_CHANGE_LINE)){
rowStr = rowStr.replace(SYM_CHANGE_LINE,"");
}
LineData lineData = mLineDataList.get(i);
String lineText = lineData.getLineText();
if (!rowStr.equals(lineText)) {
//原本的每行文字跟现在的每行文字不相同,说明排版变了,需要重新更新文本
update = true;
break;
}
}
}
}
return update;
}
private void refresh(){
Layout layout = getLayout();
if(layout != null){
List<CustomSpanData> copySpanData = new ArrayList<>();
copySpanData.addAll(mCustomTextSpanDataList);
mCustomTextSpanDataList.clear();
int lineCount = layout.getLineCount();
String text = layout.getText().toString();
int index = text.indexOf("\n");
Log.d("hyh", "SpanLineEditText: refresh: index="+index);
Log.d("hyh", "SpanLineEditText: refresh: text="+text+" ,lineCount="+lineCount);
for (int i = 0; i < lineCount; i++) {
int start = layout.getLineStart(i);
int end = layout.getLineEnd(i);
String rowStr = text.substring(start,end);
Log.d("hyh", "SpanLineEditText: refresh: rowStr="+rowStr+" ,start="+start+" ,end="+end);
CustomSpanData customTextSpanData = new CustomSpanData.Builder(text,start,end)
.setTextSize(getRangeRandomInt())
.setSpanType(TYPE_ABS_SIZE_SPAN)
.build();
mCustomTextSpanDataList.add(customTextSpanData);
}
if(!mLastText.equals(text)) {
updateText(text);
}
mLastText = text;
}
}
@Override
protected void onDraw(Canvas canvas) {
TextPaint paint = getPaint();
paint.setColor(getCurrentTextColor());
paint.drawableState = getDrawableState();
String text = getText().toString();
float lineY = 0;
lineY += getTextSize() * 1f;
final Layout layout = getLayout();
final float desiredLineWidth = getMeasuredWidth();
for (int i = 0, lineCount = layout.getLineCount(); i < lineCount; i++) {
int lineStart = layout.getLineStart(i);
int lineEnd = layout.getLineEnd(i);
String line = text.substring(lineStart, lineEnd);
if (needScale(line) && i < lineCount - 1) {
drawScaledText(line, canvas, lineY, desiredLineWidth, paint);
} else {
canvas.drawText(line, 0, lineY, paint);
}
lineY += getLineHeight();
}
}
/**
* Gets the lineInfo from the index of the letter in the text
*/
public static int getLineFromIndex(int index, int lineCount, Layout layout) {
int line;
int currentIndex = 0;
for (line = 0; line < lineCount; line++) {
currentIndex += layout.getLineEnd(line) - layout.getLineStart(line);
if (currentIndex >= index) {
break;
}
}
return line;
}
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);
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;
}
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 (ClickableSpan candidate1 : candidates) {
int end = buffer.getSpanEnd(candidate1);
if (end < selEnd || selStart == selEnd) {
if (end > bestend) {
beststart = buffer.getSpanStart(candidate1);
bestend = end;
}
}
}
if (beststart >= 0) {
Selection.setSelection(buffer, bestend, beststart);
return true;
}
break;
case DOWN:
beststart = Integer.MAX_VALUE;
bestend = Integer.MAX_VALUE;
for (ClickableSpan candidate : candidates) {
int start = buffer.getSpanStart(candidate);
if (start > selStart || selStart == selEnd) {
if (start < beststart) {
beststart = start;
bestend = buffer.getSpanEnd(candidate);
}
}
}
if (bestend < Integer.MAX_VALUE) {
Selection.setSelection(buffer, beststart, bestend);
return true;
}
break;
}
return false;
}