有時候咱們須要根據關鍵字截斷文本內容,省略多餘部分,好比微信搜索聊天記錄時會在關鍵字的先後進行截斷.java
處理方式和邏輯見以下代碼和註釋:android
private static void ellipsizeByKeyword(final TextView textView, String content, String keyword, boolean ignoreCase) {
TextPaint paint = textView.getPaint();
String compareContent = ignoreCase ? content.toLowerCase(Locale.ENGLISH) : content;
String compareKeyword = ignoreCase ? keyword.toLowerCase(Locale.ENGLISH) : keyword;
final int keywordStart = compareContent.indexOf(compareKeyword);
if (keywordStart < 0) { // 找不到關鍵字
textView.setText(null);
return;
}
int maxLine = TextViewCompat.getMaxLines(textView);
if (maxLine <= 0) { // 沒有行數限制
textView.setText(content);
return;
}
// 每行文字的最大顯示寬度
int availableWidth = textView.getWidth() - textView.getPaddingLeft() - textView.getPaddingRight();
// 區分單行和多行作不一樣的處理
if (maxLine < 2) { // 單行
int availableCount = 0; // 一行可顯示的字符數
// 若是關鍵字可在截斷尾部後的內容中找到,則直接截斷尾部
String newCharSeq = TextUtils.ellipsize(compareContent, paint, availableWidth, TextUtils.TruncateAt.END).toString();
availableCount = newCharSeq.length();
if (newCharSeq.contains(compareKeyword)) {
textView.setEllipsize(TextUtils.TruncateAt.END);
textView.setText(content);
return;
}
// 若是關鍵字可在截斷首部後的內容中找到,則直接截斷首部
newCharSeq = TextUtils.ellipsize(compareContent, paint, availableWidth, TextUtils.TruncateAt.START).toString();
availableCount = Math.max(newCharSeq.length(), availableCount);
if (newCharSeq.contains(compareKeyword)) {
textView.setEllipsize(TextUtils.TruncateAt.START);
textView.setText(content);
return;
}
// 關鍵字太長了,一行不夠顯示
if (availableCount <= keyword.length()) {
// display: ELLIPSIS_NORMAL + keyword + ...
textView.setEllipsize(TextUtils.TruncateAt.END);
textView.setText(ELLIPSIS_NORMAL + keyword);
return;
}
// 關鍵字在內容中間位置,則首尾都要加上省略號
// display: ELLIPSIS_NORMAL + xxx + keyword + xxx +...
textView.setEllipsize(TextUtils.TruncateAt.END);
int start = keywordStart - (availableCount - keyword.length()) / 2;
String text = ELLIPSIS_NORMAL + content.substring(start >= 0 ? start : 0);
newCharSeq = TextUtils.ellipsize(text, paint, availableWidth, TextUtils.TruncateAt.END).toString();
if (newCharSeq.contains(compareKeyword)) {
textView.setText(text);
} else {
textView.setText(ELLIPSIS_NORMAL + content.substring(keywordStart));
}
} else { // multi line
List<Point> linesStart = getLineStartAndEnd(textView.getPaint(), compareContent, availableWidth);
int keywordLineStart = getKeywordLine(keywordStart, linesStart); // 在原始內容中,關鍵字第一個字符的行位置
int keywordLineEnd = getKeywordLine(keywordStart + compareKeyword.length() + 1, linesStart); // 在原始內容中,關鍵字最後一個字符的行位置
if (keywordLineEnd - keywordLineStart < maxLine) {
int endLine = Math.min(keywordLineStart + maxLine / 2, linesStart.size() - 1); // linesStart.size() - 1 = lastLines
int startLine = Math.max(endLine - (maxLine - 1) + maxLine % 2, 0); // if maxline is odd, starline+1
textView.setEllipsize(TextUtils.TruncateAt.END);
if (startLine == 0) {
// display: xxx + keyword + xxx +...
textView.setText(content);
} else {
// display: ELLIPSIS_NORMAL + xxx + keyword + xxx +...
int start = linesStart.get(startLine).x;
textView.setText(ELLIPSIS_NORMAL + content.substring(start));
}
} else { // // 關鍵字太長了
// display: ELLIPSIS_NORMAL + keyword + xx ...
textView.setEllipsize(TextUtils.TruncateAt.END);
textView.setText(ELLIPSIS_NORMAL + content.substring(keywordStart));
}
}
}
/** * 計算每一行的開始字符位置和結束字符位置 * @return List.size()爲總行數.point.x 爲當前行的開始字符位置, point.y 爲當前行的結束字符位置 */
private static List<Point> getLineStartAndEnd(TextPaint tp, CharSequence cs, int lineWidth) {
// StaticLayout是android中處理文字換行的一個工具類,StaticLayout已經實現了文本繪製換行處理
StaticLayout layout = new StaticLayout(cs, tp, lineWidth, Layout.Alignment.ALIGN_NORMAL,
1.0f, 0.0f, true);
int count = layout.getLineCount();
List<Point> list = new ArrayList<>();
for (int i = 0; i < count; i++) {
list.add(new Point(layout.getLineStart(i), layout.getLineEnd(i)));
}
return list;
}
複製代碼
效果:git
完整代碼封裝在github上的Androids項目中的EllipsizeUtils類中.github
Androids項目是本人根據平時的項目實踐經驗,爲了提升Android開發效率而寫的一個工具SDK;裏面提供了一些工具類以及自定義View,可在實際項目開發時直接使用。微信
謝謝你們支持!工具