複数行のテキストビューを楕円化する必要があります。私のコンポーネントは、楕円で少なくとも4行を表示するのに十分な大きさですが、2行しか表示されません。コンポーネントの行の最小数と最大数を変更しようとしましたが、何も変わりません。
これが問題の解決策です。それは実際に省略のために動作するTextViewのサブクラスです。以前の回答にリストされたAndroid-textview-multiline-ellipseコードは、特定の状況でバグがあり、GPLの下にあることがわかりましたが、これはほとんどの人にとってはうまくいきません。このコードは、帰属なしで自由に使用できます。または、必要に応じてApacheライセンスの下で使用してください。テキストが省略されたときに通知するリスナーがあることに注意してください。
import Java.util.ArrayList;
import Java.util.List;
import Android.content.Context;
import Android.graphics.Canvas;
import Android.text.Layout;
import Android.text.Layout.Alignment;
import Android.text.StaticLayout;
import Android.text.TextUtils.TruncateAt;
import Android.util.AttributeSet;
import Android.widget.TextView;
public class EllipsizingTextView extends TextView {
private static final String Ellipsis = "...";
public interface EllipsizeListener {
void ellipsizeStateChanged(boolean ellipsized);
}
private final List<EllipsizeListener> ellipsizeListeners = new ArrayList<EllipsizeListener>();
private boolean isEllipsized;
private boolean isStale;
private boolean programmaticChange;
private String fullText;
private int maxLines = -1;
private float lineSpacingMultiplier = 1.0f;
private float lineAdditionalVerticalPadding = 0.0f;
public EllipsizingTextView(Context context) {
super(context);
}
public EllipsizingTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EllipsizingTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void addEllipsizeListener(EllipsizeListener listener) {
if (listener == null) {
throw new NullPointerException();
}
ellipsizeListeners.add(listener);
}
public void removeEllipsizeListener(EllipsizeListener listener) {
ellipsizeListeners.remove(listener);
}
public boolean isEllipsized() {
return isEllipsized;
}
@Override
public void setMaxLines(int maxLines) {
super.setMaxLines(maxLines);
this.maxLines = maxLines;
isStale = true;
}
public int getMaxLines() {
return maxLines;
}
@Override
public void setLineSpacing(float add, float mult) {
this.lineAdditionalVerticalPadding = add;
this.lineSpacingMultiplier = mult;
super.setLineSpacing(add, mult);
}
@Override
protected void onTextChanged(CharSequence text, int start, int before, int after) {
super.onTextChanged(text, start, before, after);
if (!programmaticChange) {
fullText = text.toString();
isStale = true;
}
}
@Override
protected void onDraw(Canvas canvas) {
if (isStale) {
super.setEllipsize(null);
resetText();
}
super.onDraw(canvas);
}
private void resetText() {
int maxLines = getMaxLines();
String workingText = fullText;
boolean ellipsized = false;
if (maxLines != -1) {
Layout layout = createWorkingLayout(workingText);
if (layout.getLineCount() > maxLines) {
workingText = fullText.substring(0, layout.getLineEnd(maxLines - 1)).trim();
while (createWorkingLayout(workingText + Ellipsis).getLineCount() > maxLines) {
int lastSpace = workingText.lastIndexOf(' ');
if (lastSpace == -1) {
break;
}
workingText = workingText.substring(0, lastSpace);
}
workingText = workingText + Ellipsis;
ellipsized = true;
}
}
if (!workingText.equals(getText())) {
programmaticChange = true;
try {
setText(workingText);
} finally {
programmaticChange = false;
}
}
isStale = false;
if (ellipsized != isEllipsized) {
isEllipsized = ellipsized;
for (EllipsizeListener listener : ellipsizeListeners) {
listener.ellipsizeStateChanged(ellipsized);
}
}
}
private Layout createWorkingLayout(String workingText) {
return new StaticLayout(workingText, getPaint(), getWidth() - getPaddingLeft() - getPaddingRight(),
Alignment.ALIGN_NORMAL, lineSpacingMultiplier, lineAdditionalVerticalPadding, false);
}
@Override
public void setEllipsize(TruncateAt where) {
// Ellipsize settings are not respected
}
}
私のアプリでも同様の問題がありました。2行の文字列で、文字列が長すぎる場合は最終的に「...」を追加します。 xmlファイルの次のコードをtextviewタグに使用しました。
Android:maxLines="2"
Android:ellipsize="end"
Android:singleLine="false"
私もこの問題に遭遇しました。未解決のままである古いバグがあります: Bug 2254
これを試してください、それは私のために動作します、私は4行を持っています、そしてそれは最後/ 4行目の終わりに「...」を追加します。士気の答えと同じですが、そこにsingeLine = "false"があります。
<TextView
Android:layout_width="fill_parent"
Android:layout_height="wrap_content"
Android:maxLines="4"
Android:ellipsize="Marquee"
Android:singleLine="false"
Android:text="Hi make this a very long string that wraps at least 4 lines, seriously make it really really long so it gets cut off at the fourth line not joke. Just do it!" />
この問題を解決し、最後に、簡単な解決策を自分で構築します。必要な行を手動で楕円化するだけで、maxLine属性によってテキストが切り取られます。
この例では、テキストを最大3行にカットします
final TextView title = (TextView)findViewById(R.id.text);
title.setText("A really long text");
ViewTreeObserver vto = title.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
ViewTreeObserver obs = title.getViewTreeObserver();
obs.removeGlobalOnLayoutListener(this);
if(title.getLineCount() > 3){
Log.d("","Line["+title.getLineCount()+"]"+title.getText());
int lineEndIndex = title.getLayout().getLineEnd(2);
String text = title.getText().subSequence(0, lineEndIndex-3)+"...";
title.setText(text);
Log.d("","NewText:"+text);
}
}
});
Micah Hainline、Alex Băluț、Paul Imhoffのソリューションを組み合わせて、TextView
テキストもサポートする楕円形のマルチラインSpanned
を作成しました。
Android:ellipsize
とAndroid:maxLines
のみを設定する必要があります。
/*
* Copyright (C) 2011 Micah Hainline
* Copyright (C) 2012 Triposo
* Copyright (C) 2013 Paul Imhoff
* Copyright (C) 2014 Shahin Yousefi
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.Apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Android.annotation.SuppressLint;
import Android.content.Context;
import Android.content.res.TypedArray;
import Android.graphics.Canvas;
import Android.support.annotation.NonNull;
import Android.text.Layout;
import Android.text.Layout.Alignment;
import Android.text.SpannableStringBuilder;
import Android.text.Spanned;
import Android.text.StaticLayout;
import Android.text.TextUtils;
import Android.text.TextUtils.TruncateAt;
import Android.util.AttributeSet;
import Android.widget.TextView;
import Java.util.ArrayList;
import Java.util.List;
import Java.util.regex.Pattern;
public class EllipsizingTextView extends TextView {
private static final CharSequence Ellipsis = "\u2026";
private static final Pattern DEFAULT_END_PUNCTUATION
= Pattern.compile("[\\.!?,;:\u2026]*$", Pattern.DOTALL);
private final List<EllipsizeListener> mEllipsizeListeners = new ArrayList<>();
private EllipsizeStrategy mEllipsizeStrategy;
private boolean isEllipsized;
private boolean isStale;
private boolean programmaticChange;
private CharSequence mFullText;
private int mMaxLines;
private float mLineSpacingMult = 1.0f;
private float mLineAddVertPad = 0.0f;
private Pattern mEndPunctPattern;
public EllipsizingTextView(Context context) {
this(context, null);
}
public EllipsizingTextView(Context context, AttributeSet attrs) {
this(context, attrs, Android.R.attr.textViewStyle);
}
public EllipsizingTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs,
new int[]{ Android.R.attr.maxLines }, defStyle, 0);
setMaxLines(a.getInt(0, Integer.MAX_VALUE));
a.recycle();
setEndPunctuationPattern(DEFAULT_END_PUNCTUATION);
}
public void setEndPunctuationPattern(Pattern pattern) {
mEndPunctPattern = pattern;
}
public void addEllipsizeListener(@NonNull EllipsizeListener listener) {
mEllipsizeListeners.add(listener);
}
public void removeEllipsizeListener(EllipsizeListener listener) {
mEllipsizeListeners.remove(listener);
}
public boolean isEllipsized() {
return isEllipsized;
}
@SuppressLint("Override")
public int getMaxLines() {
return mMaxLines;
}
@Override
public void setMaxLines(int maxLines) {
super.setMaxLines(maxLines);
mMaxLines = maxLines;
isStale = true;
}
public boolean ellipsizingLastFullyVisibleLine() {
return mMaxLines == Integer.MAX_VALUE;
}
@Override
public void setLineSpacing(float add, float mult) {
mLineAddVertPad = add;
mLineSpacingMult = mult;
super.setLineSpacing(add, mult);
}
@Override
public void setText(CharSequence text, BufferType type) {
if (!programmaticChange) {
mFullText = text;
isStale = true;
}
super.setText(text, type);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (ellipsizingLastFullyVisibleLine()) isStale = true;
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
super.setPadding(left, top, right, bottom);
if (ellipsizingLastFullyVisibleLine()) isStale = true;
}
@Override
protected void onDraw(@NonNull Canvas canvas) {
if (isStale) resetText();
super.onDraw(canvas);
}
private void resetText() {
int maxLines = getMaxLines();
CharSequence workingText = mFullText;
boolean ellipsized = false;
if (maxLines != -1) {
if (mEllipsizeStrategy == null) setEllipsize(null);
workingText = mEllipsizeStrategy.processText(mFullText);
ellipsized = !mEllipsizeStrategy.isInLayout(mFullText);
}
if (!workingText.equals(getText())) {
programmaticChange = true;
try {
setText(workingText);
} finally {
programmaticChange = false;
}
}
isStale = false;
if (ellipsized != isEllipsized) {
isEllipsized = ellipsized;
for (EllipsizeListener listener : mEllipsizeListeners) {
listener.ellipsizeStateChanged(ellipsized);
}
}
}
@Override
public void setEllipsize(TruncateAt where) {
if (where == null) {
mEllipsizeStrategy = new EllipsizeNoneStrategy();
return;
}
switch (where) {
case END:
mEllipsizeStrategy = new EllipsizeEndStrategy();
break;
case START:
mEllipsizeStrategy = new EllipsizeStartStrategy();
break;
case MIDDLE:
mEllipsizeStrategy = new EllipsizeMiddleStrategy();
break;
case Marquee:
super.setEllipsize(where);
isStale = false;
default:
mEllipsizeStrategy = new EllipsizeNoneStrategy();
break;
}
}
public interface EllipsizeListener {
void ellipsizeStateChanged(boolean ellipsized);
}
private abstract class EllipsizeStrategy {
public CharSequence processText(CharSequence text) {
return !isInLayout(text) ? createEllipsizedText(text) : text;
}
public boolean isInLayout(CharSequence text) {
Layout layout = createWorkingLayout(text);
return layout.getLineCount() <= getLinesCount();
}
protected Layout createWorkingLayout(CharSequence workingText) {
return new StaticLayout(workingText, getPaint(),
getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
Alignment.ALIGN_NORMAL, mLineSpacingMult,
mLineAddVertPad, false /* includepad */);
}
protected int getLinesCount() {
if (ellipsizingLastFullyVisibleLine()) {
int fullyVisibleLinesCount = getFullyVisibleLinesCount();
return fullyVisibleLinesCount == -1 ? 1 : fullyVisibleLinesCount;
} else {
return mMaxLines;
}
}
protected int getFullyVisibleLinesCount() {
Layout layout = createWorkingLayout("");
int height = getHeight() - getCompoundPaddingTop() - getCompoundPaddingBottom();
int lineHeight = layout.getLineBottom(0);
return height / lineHeight;
}
protected abstract CharSequence createEllipsizedText(CharSequence fullText);
}
private class EllipsizeNoneStrategy extends EllipsizeStrategy {
@Override
protected CharSequence createEllipsizedText(CharSequence fullText) {
return fullText;
}
}
private class EllipsizeEndStrategy extends EllipsizeStrategy {
@Override
protected CharSequence createEllipsizedText(CharSequence fullText) {
Layout layout = createWorkingLayout(fullText);
int cutOffIndex = layout.getLineEnd(mMaxLines - 1);
int textLength = fullText.length();
int cutOffLength = textLength - cutOffIndex;
if (cutOffLength < Ellipsis.length()) cutOffLength = Ellipsis.length();
String workingText = TextUtils.substring(fullText, 0, textLength - cutOffLength).trim();
String strippedText = stripEndPunctuation(workingText);
while (!isInLayout(strippedText + Ellipsis)) {
int lastSpace = workingText.lastIndexOf(' ');
if (lastSpace == -1) break;
workingText = workingText.substring(0, lastSpace).trim();
strippedText = stripEndPunctuation(workingText);
}
workingText = strippedText + Ellipsis;
SpannableStringBuilder dest = new SpannableStringBuilder(workingText);
if (fullText instanceof Spanned) {
TextUtils.copySpansFrom((Spanned) fullText, 0, workingText.length(), null, dest, 0);
}
return dest;
}
public String stripEndPunctuation(CharSequence workingText) {
return mEndPunctPattern.matcher(workingText).replaceFirst("");
}
}
private class EllipsizeStartStrategy extends EllipsizeStrategy {
@Override
protected CharSequence createEllipsizedText(CharSequence fullText) {
Layout layout = createWorkingLayout(fullText);
int cutOffIndex = layout.getLineEnd(mMaxLines - 1);
int textLength = fullText.length();
int cutOffLength = textLength - cutOffIndex;
if (cutOffLength < Ellipsis.length()) cutOffLength = Ellipsis.length();
String workingText = TextUtils.substring(fullText, cutOffLength, textLength).trim();
while (!isInLayout(Ellipsis + workingText)) {
int firstSpace = workingText.indexOf(' ');
if (firstSpace == -1) break;
workingText = workingText.substring(firstSpace, workingText.length()).trim();
}
workingText = Ellipsis + workingText;
SpannableStringBuilder dest = new SpannableStringBuilder(workingText);
if (fullText instanceof Spanned) {
TextUtils.copySpansFrom((Spanned) fullText, textLength - workingText.length(),
textLength, null, dest, 0);
}
return dest;
}
}
private class EllipsizeMiddleStrategy extends EllipsizeStrategy {
@Override
protected CharSequence createEllipsizedText(CharSequence fullText) {
Layout layout = createWorkingLayout(fullText);
int cutOffIndex = layout.getLineEnd(mMaxLines - 1);
int textLength = fullText.length();
int cutOffLength = textLength - cutOffIndex;
if (cutOffLength < Ellipsis.length()) cutOffLength = Ellipsis.length();
cutOffLength += cutOffIndex % 2; // Make it even.
String firstPart = TextUtils.substring(
fullText, 0, textLength / 2 - cutOffLength / 2).trim();
String secondPart = TextUtils.substring(
fullText, textLength / 2 + cutOffLength / 2, textLength).trim();
while (!isInLayout(firstPart + Ellipsis + secondPart)) {
int lastSpaceFirstPart = firstPart.lastIndexOf(' ');
int firstSpaceSecondPart = secondPart.indexOf(' ');
if (lastSpaceFirstPart == -1 || firstSpaceSecondPart == -1) break;
firstPart = firstPart.substring(0, lastSpaceFirstPart).trim();
secondPart = secondPart.substring(firstSpaceSecondPart, secondPart.length()).trim();
}
SpannableStringBuilder firstDest = new SpannableStringBuilder(firstPart);
SpannableStringBuilder secondDest = new SpannableStringBuilder(secondPart);
if (fullText instanceof Spanned) {
TextUtils.copySpansFrom((Spanned) fullText, 0, firstPart.length(),
null, firstDest, 0);
TextUtils.copySpansFrom((Spanned) fullText, textLength - secondPart.length(),
textLength, null, secondDest, 0);
}
return TextUtils.concat(firstDest, Ellipsis, secondDest);
}
}
}
完全なソース: EllipsizingTextView.Java
私の場合、これをJavaでコーディングする必要はありません。すべてが期待どおりに機能します。 Android:singleLine="false"
のようなものは必要ありません。
<TextView
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:ellipsize="end"
Android:maxLines="4"
Android:text="@string/very_long_text" />
Micah Hainlineとalebsのコメントによるソリューションに基づいて、私はスパンドテキストで動作する次のアプローチを思い付きました。 myTextView.setText(Html.fromHtml("<b>Testheader</b> - Testcontent"));
動作します!これは、現在Spanned
でのみ機能することに注意してください。 String
とSpanned
のどちらの方法でも動作するように変更できます。
public class EllipsizingTextView extends TextView {
private static final Spanned Ellipsis = new SpannedString("…");
public interface EllipsizeListener {
void ellipsizeStateChanged(boolean ellipsized);
}
private final List<EllipsizeListener> ellipsizeListeners = new ArrayList<EllipsizeListener>();
private boolean isEllipsized;
private boolean isStale;
private boolean programmaticChange;
private Spanned fullText;
private int maxLines;
private float lineSpacingMultiplier = 1.0f;
private float lineAdditionalVerticalPadding = 0.0f;
public EllipsizingTextView(Context context) {
this(context, null);
}
public EllipsizingTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public EllipsizingTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
super.setEllipsize(null);
TypedArray a = context.obtainStyledAttributes(attrs, new int[] { Android.R.attr.maxLines });
setMaxLines(a.getInt(0, Integer.MAX_VALUE));
}
public void addEllipsizeListener(EllipsizeListener listener) {
if (listener == null) {
throw new NullPointerException();
}
ellipsizeListeners.add(listener);
}
public void removeEllipsizeListener(EllipsizeListener listener) {
ellipsizeListeners.remove(listener);
}
public boolean isEllipsized() {
return isEllipsized;
}
@Override
public void setMaxLines(int maxLines) {
super.setMaxLines(maxLines);
this.maxLines = maxLines;
isStale = true;
}
public int getMaxLines() {
return maxLines;
}
public boolean ellipsizingLastFullyVisibleLine() {
return maxLines == Integer.MAX_VALUE;
}
@Override
public void setLineSpacing(float add, float mult) {
this.lineAdditionalVerticalPadding = add;
this.lineSpacingMultiplier = mult;
super.setLineSpacing(add, mult);
}
@Override
public void setText(CharSequence text, BufferType type) {
if (!programmaticChange && text instanceof Spanned) {
fullText = (Spanned) text;
isStale = true;
}
super.setText(text, type);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (ellipsizingLastFullyVisibleLine()) {
isStale = true;
}
}
public void setPadding(int left, int top, int right, int bottom) {
super.setPadding(left, top, right, bottom);
if (ellipsizingLastFullyVisibleLine()) {
isStale = true;
}
}
@Override
protected void onDraw(Canvas canvas) {
if (isStale) {
resetText();
}
super.onDraw(canvas);
}
private void resetText() {
Spanned workingText = fullText;
boolean ellipsized = false;
Layout layout = createWorkingLayout(workingText);
int linesCount = getLinesCount();
if (layout.getLineCount() > linesCount) {
// We have more lines of text than we are allowed to display.
workingText = (Spanned) fullText.subSequence(0, layout.getLineEnd(linesCount - 1));
while (createWorkingLayout((Spanned) TextUtils.concat(workingText, Ellipsis)).getLineCount() > linesCount) {
int lastSpace = workingText.toString().lastIndexOf(' ');
if (lastSpace == -1) {
break;
}
workingText = (Spanned) workingText.subSequence(0, lastSpace);
}
workingText = (Spanned) TextUtils.concat(workingText, Ellipsis);
ellipsized = true;
}
if (!workingText.equals(getText())) {
programmaticChange = true;
try {
setText(workingText);
} finally {
programmaticChange = false;
}
}
isStale = false;
if (ellipsized != isEllipsized) {
isEllipsized = ellipsized;
for (EllipsizeListener listener : ellipsizeListeners) {
listener.ellipsizeStateChanged(ellipsized);
}
}
}
/**
* Get how many lines of text we are allowed to display.
*/
private int getLinesCount() {
if (ellipsizingLastFullyVisibleLine()) {
int fullyVisibleLinesCount = getFullyVisibleLinesCount();
if (fullyVisibleLinesCount == -1) {
return 1;
} else {
return fullyVisibleLinesCount;
}
} else {
return maxLines;
}
}
/**
* Get how many lines of text we can display so their full height is visible.
*/
private int getFullyVisibleLinesCount() {
Layout layout = createWorkingLayout(new SpannedString(""));
int height = getHeight() - getPaddingTop() - getPaddingBottom();
int lineHeight = layout.getLineBottom(0);
return height / lineHeight;
}
private Layout createWorkingLayout(Spanned workingText) {
return new StaticLayout(workingText, getPaint(),
getWidth() - getPaddingLeft() - getPaddingRight(),
Alignment.ALIGN_NORMAL, lineSpacingMultiplier,
lineAdditionalVerticalPadding, false /* includepad */);
}
@Override
public void setEllipsize(TruncateAt where) {
// Ellipsize settings are not respected
}
}
興味のある方のために、ミカの素敵なソリューションのC#Xamarin.Androidへの移植を以下に示します。
public delegate void EllipsizeEvent(bool ellipsized);
public class EllipsizingTextView : TextView
{
private const string Ellipsis = "...";
public event EllipsizeEvent EllipsizeStateChanged;
private bool isEllipsized;
private bool isStale;
private bool programmaticChange;
private string fullText;
private int maxLines = -1;
private float lineSpacingMultiplier = 1.0f;
private float lineAdditionalVerticalPadding;
public EllipsizingTextView(Context context) : base(context)
{
}
public EllipsizingTextView(Context context, IAttributeSet attrs) : base(context, attrs)
{
}
public EllipsizingTextView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
}
public EllipsizingTextView(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public bool IsEllipsized
{
get { return isEllipsized; }
}
public override void SetMaxLines(int maxLines) {
base.SetMaxLines(maxLines);
this.maxLines = maxLines;
isStale = true;
}
public int GetMaxLines()
{
return maxLines;
}
public override void SetLineSpacing(float add, float mult)
{
lineAdditionalVerticalPadding = add;
lineSpacingMultiplier = mult;
base.SetLineSpacing(add, mult);
}
protected override void OnTextChanged(ICharSequence text, int start, int before, int after)
{
base.OnTextChanged(text, start, before, after);
if (!programmaticChange)
{
fullText = text.ToString();
isStale = true;
}
}
protected override void OnDraw(Canvas canvas)
{
if (isStale)
{
base.Ellipsize = null;
ResetText();
}
base.OnDraw(canvas);
}
private void ResetText()
{
int maxLines = GetMaxLines();
string workingText = fullText;
bool ellipsized = false;
if (maxLines != -1)
{
Layout layout = CreateWorkingLayout(workingText);
if (layout.LineCount > maxLines)
{
workingText = fullText.Substring(0, layout.GetLineEnd(maxLines - 1)).Trim();
while (CreateWorkingLayout(workingText + Ellipsis).LineCount > maxLines)
{
int lastSpace = workingText.LastIndexOf(' ');
if (lastSpace == -1)
{
break;
}
workingText = workingText.Substring(0, lastSpace);
}
workingText = workingText + Ellipsis;
ellipsized = true;
}
}
if (workingText != Text)
{
programmaticChange = true;
try
{
Text = workingText;
}
finally
{
programmaticChange = false;
}
}
isStale = false;
if (ellipsized != isEllipsized)
{
isEllipsized = ellipsized;
if (EllipsizeStateChanged != null)
EllipsizeStateChanged(ellipsized);
}
}
private Layout CreateWorkingLayout(string workingText)
{
return new StaticLayout(workingText, Paint, Width - PaddingLeft - PaddingRight, Layout.Alignment.AlignNormal, lineSpacingMultiplier, lineAdditionalVerticalPadding, false);
}
public override TextUtils.TruncateAt Ellipsize
{
get
{
return base.Ellipsize;
}
set
{
}
}
}
2行目の最後に...
を追加するには、テキストが短い場合に1行を保存します。
Android:maxLines="2"
Android:ellipsize="end"
これが私の解決策です。 githubでデモをダウンロードできます。 https://github.com/krossford/KrossLib/tree/master/Android-project
このスクリーンショットはmaxLines = 4
というデモでしたが、うまくいくと思います。
package com.krosshuang.krosslib.lib.view;
import Android.content.Context;
import Android.graphics.Canvas;
import Android.util.AttributeSet;
import Android.widget.TextView;
import Java.util.ArrayList;
/*
如何使用?
How to use it?
> 1.在xml或者Java代码中常规使用
> 1.use it like other views on xml and Java code.
> 2.[必须] setMaxLines 方法替代在xml中的 "Android:maxLines" 属性
> 2.[must] call the setMaxLines method to instead of the xml property Android:maxLines.
> 3.[可选] 注意调用 setMultilineEllipsizeMode() 方法,具体请查看注释
> 3.[option] you can invoke setMultilineEllipsizeMode method, but I have not implement it.
*/
/**
* Android自己的TextView对多行ellipsize处理的不好
* Created by krosshuang on 2015/12/17.
*/
public class EllipsizeEndTextView extends TextView {
private static final String LOG_TAG = "EllipsizeTextView";
/** 每一行都有省略号 */
//TODO 该特性待完成
public static final int MODE_EACH_LINE = 1;
/** 最后一行才有省略号 */
public static final int MODE_LAST_LINE = 2;
private static final String ELLIPSIZE = "...";
private ArrayList<String> mTextLines = new ArrayList<String>();
private CharSequence mSrcText = null;
private int mMultilineEllipsizeMode = MODE_LAST_LINE;
private int mMaxLines = 1;
private boolean mNeedIgnoreTextChangeAndSelfInvoke = false;
public EllipsizeEndTextView(Context context) {
super(context);
}
public EllipsizeEndTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EllipsizeEndTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
if (!mNeedIgnoreTextChangeAndSelfInvoke) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
mSrcText = text;
}
}
@Override
public void setMaxLines(int maxlines) {
super.setMaxLines(maxlines);
mMaxLines = maxlines;
}
public int getSupportedMaxLines() {
return mMaxLines;
}
@Override
protected void onDraw(Canvas canvas) {
setVisibleText();
super.onDraw(canvas);
mNeedIgnoreTextChangeAndSelfInvoke = false;
}
private void setVisibleText() {
if (mSrcText == null) {
return;
}
//获得可使用的width get available width
final int aw = getWidth() - getPaddingLeft() - getPaddingRight();
String srcText = mSrcText.toString();
//将原始的字符串先按原始数据中存在的换行符弄成多行字符串
String[] lines = srcText.split("\n");
//Log.i(LOG_TAG, "原始数据有: " + lines.length + " 行 " + Arrays.toString(lines));
int maxLines = getSupportedMaxLines();
//将原始文本分成几行后加入list
mTextLines.clear();
for (int i = 0; i < lines.length; i++) {
mTextLines.add(lines[i]);
}
switch (mMultilineEllipsizeMode) {
case MODE_EACH_LINE:
break;
default:
case MODE_LAST_LINE:
//开始遍历
String eachLine = null;
for (int i = 0; i < mTextLines.size() && i < maxLines - 1; i++) {
eachLine = mTextLines.get(i);
if (getPaint().measureText(eachLine, 0, eachLine.length()) > aw) {
//当前行超过可用宽度
boolean isOut = true;
int end = eachLine.length() - 1;
while (isOut) {
if (getPaint().measureText(eachLine.substring(0, end), 0, end) > aw) {
end--;
} else {
isOut = false;
}
}
mTextLines.set(i, eachLine.substring(0, end)); //当前行设置为裁剪后的
mTextLines.add(i + 1, eachLine.substring(end, eachLine.length())); //将裁剪剩余的部分,加入下一行,刚好接下来发生的遍历就可以处理它,相当于一个递归
}
}
//遍历处理结束,所有的行都是在可用宽度以内的
break;
}
//根据 maxLines 和 结果的行数,决定最小需要多少行
int resultSize = Math.min(maxLines, mTextLines.size());
//对最后一行做处理
String lastLine = mTextLines.get(resultSize - 1);
//最后一行有两种情况需要加...
//1.最后一行数据本身很长,超过了可用宽度,那么裁剪后尾部加上...
//2.最后一行不是很长,并没有超过可用宽度,但是它底下还有行没有显示,因此加上...
if (getPaint().measureText(lastLine, 0, lastLine.length()) > aw || resultSize < mTextLines.size()) {
boolean isOut = true;
int end = lastLine.length();
while (isOut) {
if (getPaint().measureText(lastLine.substring(0, end) + ELLIPSIZE, 0, end + 3) > aw) {
end--;
} else {
isOut = false;
}
}
mTextLines.set(resultSize - 1, lastLine.substring(0, end) + ELLIPSIZE);
}
//开始构建结果
StringBuilder sb = new StringBuilder();
for (int i = 0; i < resultSize ; i++) {
sb.append(mTextLines.get(i));
if (i != resultSize - 1) {
sb.append('\n');
}
}
//构建完成,set
if (sb.toString().equals(getText())) {
return;
} else {
mNeedIgnoreTextChangeAndSelfInvoke = true;
setText(sb.toString());
}
}
/**
* 设置ellipsize mode,暂时不支持
* @deprecated
* */
public void setMultilineEllipsizeMode(int mode) {
mMultilineEllipsizeMode = mode;
}
}
すぐに使えるものは何もありません。しかし、あなたはこれを見ることができます
http://code.google.com/p/Android-textview-multiline-ellipse/
ニーズに合わせて調整します。
アクティビティにコードを追加するだけです
Textview.setEllipsize(TextUtils.TruncateAt.END)
これにより、textviewの最後に省略記号が追加されます
TextViewを拡張し、これらのメソッドをオーバーライドします。
CharSequence origText = "";
int maxLines = 2;
@Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, type);
origText = text;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
CharSequence text = origText;
onPreDraw();
while(getLineCount() > maxLines) {
text = text.subSequence(0, text.length()-1);
super.setText(text + "...");
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
onPreDraw();
}
}
この方法で実装してみてください
Android:maxLines="1"
Android:ellipsize="end"
Android:scrollHorizontally="true"
scrollHorizontallyは...
のキーです
ここでのMicah Hainlineからの一番の答えは素晴らしく機能しますが、Micahs answerの下のコメントで投稿したユーザーalebによって構築されたライブラリはさらに良いです:
このコンポーネントでAndroidライブラリを作成し、可能な限り多くのテキスト行を表示し、最後の行を省略できるように変更しました。 github.com/triposo/barone を参照してください
TextViewだけが必要な場合は、さらにいくつかの機能があります。これは here です。
たぶん、これは他の人が私よりも早く見つけるのに役立つでしょう:-)
コードは非常にうまくいきました! Textだけを変更する必要がない場合は、onSizeChangedメソッドをオーバーロードできます。
@Override
protected void onSizeChanged (int w, int h, int oldw, int oldh) {
isStale = true;
super.onSizeChanged(w, h, oldw, oldh);
}
これは遅くなりましたが、ストックメールアプリで使用されているAndroidのApacheライセンスクラスを見つけました: https://Android.googlesource.com/platform/packages/apps/UnifiedEmail/+/184ec73/src/ com/Android/mail/ui/EllipsizedMultilineTextView.Java
/*
* Copyright (C) 2013 Google Inc.
* Licensed to The Android Open Source Project.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.Apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.Android.mail.ui;
import Android.content.Context;
import Android.text.Layout;
import Android.text.Layout.Alignment;
import Android.text.SpannableStringBuilder;
import Android.text.Spanned;
import Android.text.StaticLayout;
import Android.text.TextUtils;
import Android.util.AttributeSet;
import Android.widget.TextView;
/**
* A special MultiLine TextView that will apply ellipsize logic to only the last
* line of text, such that the last line may be shorter than any previous lines.
*/
public class EllipsizedMultilineTextView extends TextView {
public static final int ALL_AVAILABLE = -1;
private int mMaxLines;
public EllipsizedMultilineTextView(Context context) {
this(context, null);
}
public EllipsizedMultilineTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setMaxLines(int maxlines) {
super.setMaxLines(maxlines);
mMaxLines = maxlines;
}
/**
* Ellipsize just the last line of text in this view and set the text to the
* new ellipsized value.
* @param text Text to set and ellipsize
* @param avail available width in pixels for the last line
* @param Paint Paint that has the proper properties set to measure the text
* for this view
* @return the {@link CharSequence} that was set on the {@link TextView}
*/
public CharSequence setText(final CharSequence text, int avail) {
if (text == null || text.length() == 0) {
return text;
}
setEllipsize(null);
setText(text);
if (avail == ALL_AVAILABLE) {
return text;
}
Layout layout = getLayout();
if (layout == null) {
final int w = getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight();
layout = new StaticLayout(text, 0, text.length(), getPaint(), w, Alignment.ALIGN_NORMAL,
1.0f, 0f, false);
}
// find the last line of text and chop it according to available space
final int lastLineStart = layout.getLineStart(mMaxLines - 1);
final CharSequence remainder = TextUtils.ellipsize(text.subSequence(lastLineStart,
text.length()), getPaint(), avail, TextUtils.TruncateAt.END);
// assemble just the text portion, without spans
final SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append(text.toString(), 0, lastLineStart);
if (!TextUtils.isEmpty(remainder)) {
builder.append(remainder.toString());
}
// Now copy the original spans into the assembled string, modified for any ellipsizing.
//
// Merely assembling the Spanned pieces together would result in duplicate CharacterStyle
// spans in the assembled version if a CharacterStyle spanned across the lastLineStart
// offset.
if (text instanceof Spanned) {
final Spanned s = (Spanned) text;
final Object[] spans = s.getSpans(0, s.length(), Object.class);
final int destLen = builder.length();
for (int i = 0; i < spans.length; i++) {
final Object span = spans[i];
final int start = s.getSpanStart(span);
final int end = s.getSpanEnd(span);
final int flags = s.getSpanFlags(span);
if (start <= destLen) {
builder.setSpan(span, start, Math.min(end, destLen), flags);
}
}
}
setText(builder);
return builder;
}
}
これも私のhtmlを処理しました。
/*
* Copyright (C) 2013 Google Inc.
* Licensed to The Android Open Source Project.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.Apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.Android.mail.ui;
import Android.content.Context;
import Android.content.res.TypedArray;
import Android.text.Layout;
import Android.text.Layout.Alignment;
import Android.text.SpannableStringBuilder;
import Android.text.Spanned;
import Android.text.StaticLayout;
import Android.text.TextUtils;
import Android.util.AttributeSet;
import Android.widget.TextView;
/**
* A special MultiLine TextView that will apply ellipsize logic to only the last
* line of text, such that the last line may be shorter than any previous lines.
*/
public class EllipsizedMultilineTextView extends TextView {
public static final int ALL_AVAILABLE = -1;
private int mMaxLines;
public EllipsizedMultilineTextView(Context context) {
super(context);
}
public EllipsizedMultilineTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public EllipsizedMultilineTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private final void init(Context context, AttributeSet attrs) {
final TypedArray a = context.obtainStyledAttributes(attrs,
new int[] { Android.R.attr.maxLines });
setMaxLines(a.getInt(0, 2));
}
@Override
public void setMaxLines(int maxlines) {
super.setMaxLines(maxlines);
mMaxLines = maxlines;
}
/**
* Ellipsize just the last line of text in this view and set the text to the
* new ellipsized value.
* @param text Text to set and ellipsize
* @param avail available width in pixels for the last line
* @param Paint Paint that has the proper properties set to measure the text
* for this view
* @return the {@link CharSequence} that was set on the {@link TextView}
*/
public CharSequence setText(final CharSequence text, int avail) {
if (text == null || text.length() == 0) {
return text;
}
setEllipsize(null);
setText(text);
if (avail == ALL_AVAILABLE) {
return text;
}
Layout layout = getLayout();
if (layout == null) {
final int w = getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight();
layout = new StaticLayout(text, 0, text.length(), getPaint(), w, Alignment.ALIGN_NORMAL,
1.0f, 0f, false);
}
// find the last line of text and chop it according to available space
final int lastLineStart = layout.getLineStart(mMaxLines - 1);
final CharSequence remainder = TextUtils.ellipsize(text.subSequence(lastLineStart,
text.length()), getPaint(), avail, TextUtils.TruncateAt.END);
// assemble just the text portion, without spans
final SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append(text.toString(), 0, lastLineStart);
if (!TextUtils.isEmpty(remainder)) {
builder.append(remainder.toString());
}
// Now copy the original spans into the assembled string, modified for any ellipsizing.
//
// Merely assembling the Spanned pieces together would result in duplicate CharacterStyle
// spans in the assembled version if a CharacterStyle spanned across the lastLineStart
// offset.
if (text instanceof Spanned) {
final Spanned s = (Spanned) text;
final Object[] spans = s.getSpans(0, s.length(), Object.class);
final int destLen = builder.length();
for (int i = 0; i < spans.length; i++) {
final Object span = spans[i];
final int start = s.getSpanStart(span);
final int end = s.getSpanEnd(span);
final int flags = s.getSpanFlags(span);
if (start <= destLen) {
builder.setSpan(span, start, Math.min(end, destLen), flags);
}
}
}
setText(builder);
return builder;
}
}
元のソース LINK
私も同じ問題を抱えています。 Android:ellipsize = "Marquee"を削除するだけで修正しました