Android文字過長時根據關鍵字省略內容Ellipsize
有時候我們需要根據關鍵字截斷文字內容,省略多餘部分,比如微信搜尋聊天記錄時會在關鍵字的前後進行截斷.

image
處理方式和邏輯見如下程式碼和註釋:
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; }
效果:

image
github
完整程式碼封裝在github上的 Androids專案 中的 EllipsizeUtils類 中.
Androids專案 是本人根據平時的專案實踐經驗,為了提高Android開發效率而寫的一個工具SDK;裡面提供了一些工具類以及自定義View,可在實際專案開發時直接使用。
謝謝大家支援!