下面列出了android.text.Layout#getLineEnd ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
/**
* 裁剪文本至固定行数
* @param text 源文本
* @return 字符串
*/
private String tailorText(String text){
String destStr = text + ELLIPSIS + getUnfoldText();
Layout layout = makeTextLayout(destStr);
//如果行数大于固定行数
if(layout.getLineCount() > getFoldLine()){
int index = layout.getLineEnd(getFoldLine());
if(text.length() < index){
index = text.length();
}
//从最后一位逐渐试错至固定行数
String subText = text.substring(0, index-1);
return tailorText(subText);
}else{
return destStr;
}
}
/**
* 按行分割文本
*/
private void spliteLineData(Layout layout,String text){
mLineDataList.clear();
int lineCount = layout.getLineCount();
Log.d("hyh", "AutomaticEditText: spliteLineData: 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);
//去除掉换行符
if(rowStr.contains(SYM_CHANGE_LINE)){
rowStr = rowStr.replace(SYM_CHANGE_LINE,"");
}
CustomSpanData customTextSpanData = new CustomSpanData.Builder(start,end)
.setSpanType(TYPE_ABS_SIZE_SPAN)
.setTextSize(UNIT_PX,mLayoutHelper.getFontSize())
.build();
LineData lineData = new LineData(rowStr,customTextSpanData);
Log.d("hyh", "AutomaticEditText: spliteLineData: lineData="+lineData.toString());
mLineDataList.add(lineData);
}
}
@Override
protected void drawFrame(Canvas canvas) {
Layout layout = mHTextView.getLayout();
int gapIndex = 0;
for (int i = 0; i < layout.getLineCount(); i++) {
int lineStart = layout.getLineStart(i);
int lineEnd = layout.getLineEnd(i);
float lineLeft = layout.getLineLeft(i);
float lineBaseline = layout.getLineBaseline(i);
String lineText = mText.subSequence(lineStart, lineEnd).toString();
for (int c = 0; c < lineText.length(); c++) {
int alpha = alphaList.get(gapIndex);
mPaint.setAlpha((int) ((255 - alpha) * progress + alpha));
canvas.drawText(String.valueOf(lineText.charAt(c)), lineLeft, lineBaseline, mPaint);
lineLeft += gapList.get(gapIndex++);
}
}
}
/**
* Find the number of lines of text which must be shown in order to display the character at
* a given index.
*/
private int getLineForIndex(int index) {
Layout layout = getLayout();
int endLine = 0;
while (endLine < layout.getLineCount() && layout.getLineEnd(endLine) < index) {
endLine++;
}
// Since endLine is an index, add 1 to get the number of lines.
return endLine + 1;
}
/**
* 重绘文字,两端对齐
*
* @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();
}
}
/**
* 计算字符距离控件左侧的位移
*
* @param line 字符所在行
* @param charOffset 字符偏移量
*/
private float calculatorCharPositionToLeft(int line, int charOffset) {
String text_str = getText().toString();
Layout layout = getLayout();
int lineStart = layout.getLineStart(line);
int lineEnd = layout.getLineEnd(line);
String line_str = text_str.substring(lineStart, lineEnd);
if (line_str.equals("\n"))
return getPaddingLeft();
// 最左侧
if (lineStart == charOffset)
return getPaddingLeft();
// 最右侧
if (charOffset == lineEnd - 1)
return mViewTextWidth + getPaddingLeft();
float desiredWidth = StaticLayout.getDesiredWidth(text_str, lineStart, lineEnd, getPaint());
// 中间位置
// 计算相邻字符之间需要填充的宽度
// (TextView内容的实际宽度 - 该行字符串的宽度)/(字符个数-1)
float insert_blank = (mViewTextWidth - desiredWidth) / (line_str.length() - 1);
// 计算当前字符左侧所有字符的宽度
float allLeftCharWidth = StaticLayout.getDesiredWidth(text_str.substring(lineStart, charOffset), getPaint());
// 相邻字符之间需要填充的宽度 + 当前字符左侧所有字符的宽度
return insert_blank * (charOffset - lineStart) + allLeftCharWidth + getPaddingLeft();
}
@Nullable
protected CharSequence getNextLine(Editable editable, Layout layout, int currentLine) {
if (currentLine + 1 > layout.getLineCount() - 1) return null;
int lineStart = layout.getLineStart(currentLine + 1);
int lineEnd = layout.getLineEnd(currentLine + 1);
return editable.subSequence(lineStart, lineEnd);
}
/**
* 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;
}
/**
* move cursor to lineInfo
*
* @param line - lineInfo in editor, start at 0
*/
public void goToLine(int line) {
Layout layout = getLayout();
line = Math.min(line - 1, getLineCount() - 1);
line = Math.max(0, line);
if (layout != null) {
int index = layout.getLineEnd(line);
setSelection(index);
}
}
/**
* 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;
}
/**
* Find the number of lines of text which must be shown in order to display the character at
* a given index.
*/
private int getLineForIndex(int index) {
Layout layout = getLayout();
int endLine = 0;
while (endLine < layout.getLineCount() && layout.getLineEnd(endLine) < index) {
endLine++;
}
// Since endLine is an index, add 1 to get the number of lines.
return endLine + 1;
}
/**
* 得到最多行那个 最后一个字符的位置,与第一个位置 测出宽度 与 扩展字符 相加 如果大于行宽, 就把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 static int getOffsetForHorizontal(View view, Layout layout, int x, int line) {
if (view.getWidth() > layout.getWidth()) {
if (view instanceof FastTextView) {
int gravity = ((FastTextView) view).getGravity();
int translateX;
int horizontalGravity = gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
switch (horizontalGravity) {
default:
case Gravity.LEFT:
translateX = view.getPaddingLeft();
break;
case Gravity.CENTER_HORIZONTAL:
translateX = view.getPaddingLeft() + (((FastTextView) view).getInnerWidth() - layout
.getWidth()) / 2;
break;
case Gravity.RIGHT:
translateX = view.getPaddingLeft() + ((FastTextView) view).getInnerWidth() - layout
.getWidth();
break;
}
x -= translateX;
}
}
try {
return layout.getOffsetForHorizontal(line, x);
} catch (Throwable e) {
e.printStackTrace();
}
return layout.getLineEnd(line);
}
private void translateText(Layout layout, BufferType type) {
originalLineCount = layout.getLineCount();
if (layout.getLineCount() > mShowMaxLine) {
isOverMaxLine = true;
SpannableStringBuilder span = new SpannableStringBuilder();
int start = layout.getLineStart(mShowMaxLine - 1);
int end = layout.getLineEnd(mShowMaxLine - 1);
if (mTipGravity == END) {
TextPaint paint = getPaint();
StringBuilder builder = new StringBuilder(ELLIPSIZE_END).append(mFoldText);
end -= paint.breakText(mOriginalText, start, end, false, paint.measureText(builder.toString()), null);
float x = getWidth() - getPaddingLeft() - getPaddingRight() - getTextWidth(mFoldText);
while (layout.getPrimaryHorizontal(end - 1) + getTextWidth(mOriginalText.subSequence(end - 1, end).toString()) < x) {
end++;
}
end--;
} else {
end--;
}
CharSequence ellipsize = mOriginalText.subSequence(0, end);
span.append(ellipsize);
span.append(ELLIPSIZE_END);
if (mTipGravity != END) {
span.append("\n");
}
super.setText(span, type);
} else {
isOverMaxLine = false;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (needCheckDevice && isVivoApi22()) {
int width = getMeasuredWidth();
CharSequence charSequence = getText();
Layout layout = createWorkingLayout(charSequence, getPaint(), width);
float dotWidth = getPaint().measureText(DOT_SUFFIX);
int lineCount = layout.getLineCount();
Log.d(TAG, "onMeasure , lineCount : " + lineCount);
// 多行且第一行未占满
if (lineCount > 1 && Math.abs(width - layout.getLineWidth(0)) > dotWidth) {
Log.d(TAG, "onMeasure , firstLineEndIndex : " + layout.getLineEnd(0));
int index = layout.getLineEnd(1);
while (true) {
index--;
charSequence = charSequence.subSequence(0, index);
charSequence = SpannableStringBuilder.valueOf(charSequence).append(DOT_SUFFIX);
layout = createWorkingLayout(charSequence, getPaint(), width);
if (layout.getLineCount() == 1 && (width - layout.getLineWidth(0) <= dotWidth || index <= 10)) {
mLayout = layout;
break;
}
}
}
needCheckDevice = false;
}
}
/**
* Find the number of lines of text which must be shown in order to display the character at
* a given index.
*/
private int getLineForIndex(int index) {
Layout layout = getLayout();
int endLine = 0;
while (endLine < layout.getLineCount() && layout.getLineEnd(endLine) < index) {
endLine++;
}
// Since endLine is an index, add 1 to get the number of lines.
return endLine + 1;
}
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 (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;
}