여러 줄의 텍스트 뷰를 줄임표로 만들어야합니다. 내 구성 요소는 타원으로 4 줄 이상을 표시 할만큼 충분히 크지 만 2 줄만 표시됩니다. 구성 요소의 최소 및 최대 행 수를 변경하려고했지만 아무것도 변경하지 않았습니다.
여러 줄의 텍스트 뷰를 줄임표로 만들어야합니다. 내 구성 요소는 타원으로 4 줄 이상을 표시 할만큼 충분히 크지 만 2 줄만 표시됩니다. 구성 요소의 최소 및 최대 행 수를 변경하려고했지만 아무것도 변경하지 않았습니다.
답변:
다음은 문제에 대한 해결책입니다. 실제로 줄임표에 사용되는 TextView의 하위 클래스입니다. 이전 답변에 나열된 android-textview-multiline-ellipse 코드는 특정 상황에서 버그가 있고 GPL하에있는 것으로 나타났습니다 .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"
setText(..., TextView.BufferType.SPANNABLE);
.
이것을 시도하십시오, 그것은 나를 위해 작동합니다, 나는 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 의견을 바탕으로 Spanned 텍스트와 함께 작동하는 다음과 같은 접근 방식을 사용했습니다 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입니다. Micah의 멋진 솔루션의 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
{
}
}
}
활동에 코드를 추가하십시오.
Textview.setEllipsize(TextUtils.TruncateAt.END)
이것은 텍스트보기의 끝에 줄임표를 추가합니다
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();
}
}
이것이 나의 해결책이다. 내 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;
}
}
코드가 잘 작동했습니다! 텍스트 만 변경해야하는 경우가 아니라면 onSizeChanged 메서드를 오버로드 할 수 있습니다.
@Override
protected void onSizeChanged (int w, int h, int oldw, int oldh) {
isStale = true;
super.onSizeChanged(w, h, oldw, oldh);
}
이것도 내 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
Micah Hainline의 최고 답변은 훌륭하지만 Mieve의 답변 아래 의견에 게시 된 사용자 aleb가 라이브러리에서 작성한 라이브러리가 더 좋습니다.
이 구성 요소로 Android 라이브러리를 만들고 가능한 한 많은 텍스트 줄을 표시하고 마지막 줄을 줄임표로 변경했습니다. github.com/triposo/barone 참조
TextView 만 필요한 경우 여기에 추가 기능이 있습니다 .
어쩌면 이것은 다른 사람들이 내가했던 것보다 빨리 찾는 데 도움이 될 것입니다 :-)
이것은 늦었지만 재고 메일 앱에서 사용되는 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;
}
}
android : lines, android : minLines, android : maxLines와 같은 몇 가지 속성을 확인해야합니다. 최대 4 줄을 표시하고 줄임표를 만들려면 android : maxLines 및 android : ellipsize 만 있으면됩니다.
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:maxLines="4"
android:ellipsize="marquee"
android:text="Hai!"
/>
이것을 당신의 textview에 포함시켜야합니다 :
android:singleLine="false"
기본적으로 true입니다. 명시 적으로 false로 설정해야합니다.
code <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:maxLines="4" android:ellipsize="marquee" android:singleLine="false" android:text="Hai make this a very long string that wraps at least 4 lines!" />