android ellipsize textilmiline


163

ฉันต้องทำมุมมองข้อความแบบหลายบรรทัดใหม่ องค์ประกอบของฉันมีขนาดใหญ่พอที่จะแสดงอย่างน้อย 4 บรรทัดด้วยวงรี แต่แสดงเพียง 2 บรรทัด ฉันพยายามที่จะเปลี่ยนจำนวนแถวขั้นต่ำและจำนวนสูงสุดขององค์ประกอบ แต่มันไม่มีอะไรเปลี่ยนแปลง


คุณมีสไตล์หรือธีมที่ใช้กับ TextView ของคุณซึ่งสามารถระบุขนาดสูงสุดได้หรือไม่?
Cheryl Simon

คุณหาทางแก้ปัญหานี้หรือไม่?
UMAR-MOBITSOLUTIONS

สวัสดีหลังจากต่อสู้กับปัญหาการมีข้อความ 2 บรรทัด (maxLines = 2) และจุดสามจุดที่ส่วนท้ายของข้อความ (ellipsize = end) ฉันพบว่ามันใช้งานได้กับอุปกรณ์บางอย่างและในบางอัน (ฉันมี ~ 15 อุปกรณ์ในการทดสอบ) มันทำงานมักจะอยู่บนอุปกรณ์ที่มีความละเอียดสูงขึ้นแล้ว HVGA (320x480 พิกเซล) แต่ยังบน HTC บางคนที่มี 240x320px ... ทางออกเดียวคือการมี TextView ที่กำหนดเองตามที่แสดงด้านล่าง ...
สเตน

Mine ทำงานได้ดีหลังจากลบ "android: textIsSelectable = true"
HannahMitt

ดังที่ Robert Nekic กล่าวว่ามีข้อบกพร่องของ Android ซึ่งได้รับการแก้ไขแล้วในขณะนี้: code.google.com/p/android/issues/detail?id=2254คงจะดีถ้ารู้ว่ารุ่นแรกของ Android ที่ได้รับการแก้ไข
aleb

คำตอบ:


140

นี่คือวิธีแก้ไขปัญหา มันเป็น subclass ของ 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
    }
}

4
ขอบคุณสำหรับรหัสนี้มันทำงานได้ดีมาก หนึ่งริ้วรอยสำหรับผู้ใช้รายอื่น: คุณจะต้องเรียกใช้ setMaxLines (int) แทนการตั้งค่าคุณสมบัติใน XML อย่างชัดเจน
pents90

2
ที่จริงแล้วมันควรจะง่ายมากที่จะจับคู่ตัวแปรเหล่านี้กับแอตทริบิวต์ XML ผ่านทาง attrs.xml
Diego Tori

9
ฉันพบปัญหาหาก workingText เป็นภาษาจีน เนื่องจากชาวจีนไม่มี SPACE ดังนั้นรหัสนี้ใช้งานไม่ได้อย่างสมบูรณ์ฉันได้แก้ไขโค้ดด้านล่างหวังว่าจะช่วยได้ ในขณะที่ (createWorkingLayout (workingText + ELLIPSIS) .getLineCount ()> maxLines) {// int lastSpace = workingText.lastIndexOf (''); // ถ้า (lastSpace == -1) {// break; //} // workingText = workingText.substring (0, lastSpace); // 由于我们大多数情况下 workingText 为中文, 所以之前之前的的的的的的的的的的字符的 T T T T T T T T T T T T T T T T (1) ); }
chengbo

21
การเพิ่มสิ่งต่อไปนี้ลงในตัวสร้างจะช่วยให้คุณสามารถตั้งค่าสูงสุดผ่าน XML: TypedArray a = context.obtainStyledAttributes (attrs, int ใหม่ [] {android.R.attr.maxLines}); setMaxLines (a.getInt (0, 2));
stealthcopter

29
ฉันสร้างห้องสมุด Android ด้วยองค์ประกอบนี้และเปลี่ยนให้สามารถแสดงข้อความได้มากที่สุดเท่าที่จะเป็นไปได้ ดูgithub.com/triposo/barone คุณสามารถดูได้ในคู่มือการเดินทางของเราเมื่อแสดงคำแนะนำ: play.google.com/store/apps/…
aleb

55

ในแอพของฉันฉันมีปัญหาที่คล้ายกัน: 2 สายของสตริงและในที่สุดเพิ่ม "... " ถ้าสตริงยาวเกินไป ฉันใช้รหัสนี้ในไฟล์ xml ลงในแท็ก textview:

android:maxLines="2"
android:ellipsize="end"
android:singleLine="false"

4
และถ้ามันใช้ไม่ได้กับโค้ดนี้ (ในบางเวอร์ชั่น) เพิ่มแอนดรอยด์ "ซอสพิเศษ": scrollHorizontally = "true"
Bart Burg

7
setText(..., TextView.BufferType.SPANNABLE);ไม่ทำงานถ้าชุดข้อความด้วย
Simas

1
มีข่าวเรื่อง spannable บ้างไหม? @Simas ฉันพบปัญหาเดียวกัน
Alexey Strakh

17

ฉันพบปัญหานี้เช่นกัน มีข้อผิดพลาดค่อนข้างเก่าเกี่ยวกับเรื่องนี้ที่ยังไม่ได้ตอบ: Bug 2254


Eeeks คุณพูดถูก ฉันลองและไม่สามารถทำจุดไข่ปลา 4 บรรทัด มันหยุดเสมอที่บรรทัดที่สอง
มอร่า

3
ข้อผิดพลาดที่ดูเหมือนจะได้รับการแก้ไขใน 4.0.4 มันได้รับการแก้ไขใน Jelly Bean อย่างน้อย
greg7gkb

1
คุณไม่ควรตอบคำถามพร้อมคำตอบ
Bart Burg

17

ลองสิ่งนี้มันใช้งานได้สำหรับฉันฉันมี 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!" />

16
Lysogen โซลูชันนี้ใช้งานไม่ได้จริง ๆ อย่างน้อยก็ไม่เหมาะสำหรับคนอื่นที่นี่ บางทีคุณกำลังใช้อุปกรณ์ที่แตกต่างจากคนอื่นมาก แต่เป็นวิธีแก้ไขปัญหาทั่วไปนี่อาจไม่ได้ผล
คาห์ Hainline

3
ใช่ไม่ทำงาน ยังคงถูกตัดเหลือสองบรรทัด นี้น่าผิดหวัง!
Matt K

2
ฉันคัดลอกรหัสนี้ไปยังโครงการตัวอย่างและ tesxtview เพิ่งแสดง 2 บรรทัด
เหงียนมินห์บินห์

1
ใช้งานได้สำหรับฉันบนแท็บเล็ต Ice Cream Sandwich!
Cel

1
ทำงานให้ฉันกับขนมปังขิง SII มันน่าสนใจที่จะรู้เพิ่มเติมเกี่ยวกับอุปกรณ์ที่ใช้งานไม่ได้
PCoder

14

เมื่อได้รับปัญหานี้และสุดท้ายฉันก็สร้างวิธีแก้ปัญหาสั้น ๆ คุณเพียงแค่รีวงด้วยตนเองตามที่คุณต้องการแอตทริบิวต์ 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);
                }

            }
        });

12

ฉันรวมการแก้ปัญหาโดย Micah Hainline, Alex Băluț และ Paul Imhoff เพื่อสร้าง multiline รูปไข่TextViewที่ยังรองรับSpannedข้อความด้วย

คุณจะต้องตั้งค่าและandroid:ellipsizeandroid: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


2
ขอขอบคุณทางออกเดียวที่ทำงานได้ (ผ่านการทดสอบหลายอย่าง) สงสัยว่าการให้คะแนนของคุณคือ 1
CoolMind

1
ฉันสามารถยืนยันได้ว่ามันเป็นโซลูชันที่สะอาดและใช้งานได้เพียงใช้คลาส EllipsizingTextView และลืมแฮ็ก
javaxian

7

ในกรณีของฉันไม่จำเป็นต้องใช้รหัสใน 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" />

แต่ดูเหมือนว่าจะมีข้อผิดพลาดในการแสดงตัวอย่างเค้าโครงของ Android Studio (v3.0): แสดงตัวอย่างเค้าโครง

ให้ Android 7.1.1 บนอุปกรณ์ของฉันสิ่งนี้ทำงาน: ภาพหน้าจอของอุปกรณ์


ฉันก็มีปัญหานี้เช่นกัน ดูตัวอย่าง Android ผิด ในอุปกรณ์ที่ดี
AndEngine

4

ขึ้นอยู่กับการแก้ปัญหาโดยมีคาห์ 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
      }
}

4

สำหรับผู้ที่สนใจนี่คือ C # Xamarin.Android port ของ Micah solution ที่น่ารัก:

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 
        { 
        }
    }
}

เยี่ยมมากฉันเพิ่งจะได้พบกับห้องสมุดดั้งเดิมและแปลมันด้วยตัวเองขอบคุณ!
Stephane Mathis

2

ในการเพิ่ม...ในตอนท้ายของบรรทัดที่สองให้บันทึก 1 บรรทัดหากข้อความสั้น:

android:maxLines="2"
android:ellipsize="end"

1

เพียงเพิ่มรหัสในกิจกรรมของคุณ

Textview.setEllipsize(TextUtils.TruncateAt.END)

สิ่งนี้จะเพิ่มจุดไข่ปลาในตอนท้ายของมุมมองข้อความ


ดูเหมือนง่ายกว่าการขยายคลาส TextView
Malachiasz

สิ่งนี้ใช้ได้กับฉันและเป็นคำตอบที่ง่ายที่สุดของทั้งหมด
Ricardo

สิ่งนี้เหมือนกับการตั้งค่า ellipsize attr ใน xml
Shubham Naik

1

ขยาย 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();
    }

}

1

นี่คือทางออกของฉัน คุณสามารถดาวน์โหลดตัวอย่างบน 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;
    }
}

0

ฉันมีปัญหาเดียวกัน ฉันแก้ไขได้โดยเพียงแค่ลบ Android: ellipsize = "marquee"


มันใช้งานได้แม้ว่ามันควรจะกล่าวว่าสิ่งนี้จะไม่เพิ่มจุดไข่ปลาในตอนท้ายของบรรทัดสุดท้าย แต่ในความคิดของฉันนี้ยังคงเป็นทางออกที่ดีในการแก้ไขปัญหา นอกจากนี้คุณจะต้องใช้ android: maxLines = "4" เพื่อ จำกัด 4 บรรทัด
Pzanno

คำถามของ OP นั้นเป็นจุดเฉพาะสำหรับจุดไข่ปลาในตอนท้ายดังนั้นมันจะไม่ทำงาน
Matt K

0

รหัสทำงานได้ดีมาก! คุณสามารถโอเวอร์โหลดวิธี onSizeChanged ได้หากไม่เพียง แต่ต้องเปลี่ยนข้อความ

@Override
protected void onSizeChanged (int w, int h, int oldw, int oldh) {
    isStale = true;
    super.onSizeChanged(w, h, oldw, oldh);
}

0

อันนี้จัดการ 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;
    }
}

ลิงค์แหล่งดั้งเดิม


0

คำตอบที่ดีที่สุดจาก Micah Hainline ใช้งานได้ดี แต่ดีกว่าคือห้องสมุดที่สร้างขึ้นโดยผู้ใช้ aleb ขณะที่เขาโพสต์ในความคิดเห็นภายใต้คำตอบของ Micahs:

ฉันสร้างห้องสมุด Android ด้วยองค์ประกอบนี้และเปลี่ยนให้สามารถแสดงข้อความได้มากที่สุดเท่าที่จะเป็นไปได้ ดูgithub.com/triposo/barone

มีคุณสมบัติบางอย่างขึ้นไปที่ถ้าคุณต้องการเพียง TextView นั้นมันเป็นที่นี่

อาจจะช่วยให้ผู้อื่นค้นหาเร็วกว่าที่ฉันเคย :-)


0

นี่สาย แต่ฉันพบ Apache คลาสที่ได้รับลิขสิทธิ์จาก Android ที่ใช้ในแอพอีเมลหุ้น: 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;
    }
}

-2

มีคุณสมบัติบางอย่างที่คุณควรตรวจสอบ: 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!"
    />

วิธีการใช้งานเหล่านี้? ข้อความยังถูกปัดเศษเป็นสองบรรทัดโดยไม่คำนึงถึงมูลค่าที่ฉันให้แก่พวกเขา
Arutha

คุณเป็นอย่างไรบ้าง ฉันคิดว่าคุณขาดอะไรไป แต่ก็เดายาก
moraes

1
ฉันใช้การตั้งค่าเดียวกัน แต่ข้อความของฉันมักจะถูกลากเป็นสองบรรทัด
Arutha

คำตอบนี้ไม่ได้ผล ... คุณควรลองคำตอบก่อนโพสต์
Kevin Parker

4
@ เควิน: คำตอบนี้เก่ามาก คุณควรลองกับรุ่น Android ตั้งแต่วันที่ 29 มกราคม 2010 ก่อนที่จะบอกว่ามันใช้งานไม่ได้และไม่ได้ลงคะแนน
มอร่า

-14

คุณต้องรวมสิ่งนี้ไว้ในมุมมองข้อความของคุณ:

android:singleLine="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!" />
Lysogen

3
android: singleLineเป็นเท็จอย่างแน่นอนโดยค่าเริ่มต้น
Jason Robinson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.