TextView
文字列がレンダリングされる前に文字列が占める行数を取得するにはどうすればよいですか。
ViewTreeObserver
これらはレンダリング後にのみ起動されるため、Aは機能しません。
TextView
文字列がレンダリングされる前に文字列が占める行数を取得するにはどうすればよいですか。
ViewTreeObserver
これらはレンダリング後にのみ起動されるため、Aは機能しません。
受け入れられた答えは、単語を壊さないように次の行に単語全体を配置すると機能しません。
|hello |
|world! |
行数を 100% 確実にする唯一の方法は、TextView が使用するのと同じテキスト フロー エンジンを使用することです。TextView はリフロー ロジックを共有していないため、テキストを複数の行に分割するカスタム文字列プロセッサを次に示します。各行は指定された幅に収まります。また、単語全体が収まらない場合を除き、単語を分割しないように最善を尽くします。
public List<String> splitWordsIntoStringsThatFit(String source, float maxWidthPx, Paint paint) {
ArrayList<String> result = new ArrayList<>();
ArrayList<String> currentLine = new ArrayList<>();
String[] sources = source.split("\\s");
for(String chunk : sources) {
if(paint.measureText(chunk) < maxWidthPx) {
processFitChunk(maxWidthPx, paint, result, currentLine, chunk);
} else {
//the chunk is too big, split it.
List<String> splitChunk = splitIntoStringsThatFit(chunk, maxWidthPx, paint);
for(String chunkChunk : splitChunk) {
processFitChunk(maxWidthPx, paint, result, currentLine, chunkChunk);
}
}
}
if(! currentLine.isEmpty()) {
result.add(TextUtils.join(" ", currentLine));
}
return result;
}
/**
* Splits a string to multiple strings each of which does not exceed the width
* of maxWidthPx.
*/
private List<String> splitIntoStringsThatFit(String source, float maxWidthPx, Paint paint) {
if(TextUtils.isEmpty(source) || paint.measureText(source) <= maxWidthPx) {
return Arrays.asList(source);
}
ArrayList<String> result = new ArrayList<>();
int start = 0;
for(int i = 1; i <= source.length(); i++) {
String substr = source.substring(start, i);
if(paint.measureText(substr) >= maxWidthPx) {
//this one doesn't fit, take the previous one which fits
String fits = source.substring(start, i - 1);
result.add(fits);
start = i - 1;
}
if (i == source.length()) {
String fits = source.substring(start, i);
result.add(fits);
}
}
return result;
}
/**
* Processes the chunk which does not exceed maxWidth.
*/
private void processFitChunk(float maxWidth, Paint paint, ArrayList<String> result, ArrayList<String> currentLine, String chunk) {
currentLine.add(chunk);
String currentLineStr = TextUtils.join(" ", currentLine);
if (paint.measureText(currentLineStr) >= maxWidth) {
//remove chunk
currentLine.remove(currentLine.size() - 1);
result.add(TextUtils.join(" ", currentLine));
currentLine.clear();
//ok because chunk fits
currentLine.add(chunk);
}
}
単体テストの一部を次に示します。
String text = "Hello this is a very long and meanless chunk: abcdefghijkonetuhosnahrc.pgraoneuhnotehurc.pgansohtunsaohtu. Hope you like it!";
Paint paint = new Paint();
paint.setTextSize(30);
paint.setTypeface(Typeface.DEFAULT_BOLD);
List<String> strings = splitWordsIntoStringsThatFit(text, 50, paint);
assertEquals(3, strings.size());
assertEquals("Hello this is a very long and meanless chunk:", strings.get(0));
assertEquals("abcdefghijkonetuhosnahrc.pgraoneuhnotehurc.pganso", strings.get(1));
assertEquals("htunsaohtu. Hope you like it!", strings.get(2));
これで、レンダリングする必要なく、TextView の行数について 100% 確信できるようになりました。
TextView textView = ... //text view must be of fixed width
Paint paint = new Paint();
paint.setTextSize(yourTextViewTextSizePx);
paint.setTypeface(yourTextViewTypeface);
float textViewWidthPx = ...;
List<String> strings = splitWordsIntoStringsThatFit(yourText, textViewWidthPx, paint);
textView.setText(TextUtils.join("\n", strings);
int lineCount = strings.size(); //will be the same as textView.getLineCount()
final Rect bounds = new Rect();
final Paint paint = new Paint();
paint.setTextSize(currentTextSize);
paint.getTextBounds(testString, 0, testString.length(), bounds);
テキストの幅を TextView の幅で割って、合計行数を取得します。
final int numLines = (int) Math.ceil((float) bounds.width() / currentSize);
currentSize : テキストがレンダリングされるビューの予想サイズ。サイズは画面幅を超えてはなりません。