http://topic.csdn.net/u/20110909/18/7734814e-caa2-4687-8aea-27d404d34f97.html

textview中有很多行,我只让它显示三行,最后显示...,我设置了android:maxLines="3"和android:ellipsize="end",但出现的问题是只显示两行就显示“...” :


android:id="@+id/jgli_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/jgli_pic"
android:layout_alignParentTop="true"
android:layout_marginTop="5dip"
android:layout_alignParentRight="true"
android:layout_marginRight="5dip"
android:maxLines="3"
android:ellipsize="end"
android:text="@+id/bb_price"
style="@style/listview_item_text"
/>

可以把android:ellipsize="end" 去掉,代码控制,也可以:

这个好像是sdk的一个老bug,我上网查过的,可以自己写个继承TextView的类,再实现其中几个方法解决
(注意得在java文件中设置maxLines):
package com.jebeljing.TextViewTest;import java.util.ArrayList;  import java.util.List;   import android.content.Context; [code=Java][/code]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 ellipsizeListeners = new ArrayList();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) {   System.out.println(layout.getLineCount()+"\t"+maxLines);workingText = fullText.substring(0, layout.getLineEnd(maxLines - 1)).trim();   Layout layout2=createWorkingLayout(workingText + ELLIPSIS);while (layout2.getLineCount() > maxLines) {   System.out.println(layout2.getLineCount()+"\t"+maxLines);int lastSpace = workingText.lastIndexOf(' ');   System.out.println(lastSpace);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 } }  }}

这是一个android 的bug ,如果你只是检查英文可以用这种方法,如果你是检查的中文,日文这种需要分词滴,就复杂了

同时可以参考:

http://code.google.com/p/android/issues/detail?id=2254

http://code.google.com/p/android-textview-multiline-ellipse/source/checkout


package com.test;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint.Align;import android.text.TextPaint;import android.util.AttributeSet;import android.util.Log;import android.view.View;import android.view.View.MeasureSpec;import java.util.ArrayList;import java.util.List;public class MyClipTextView extends View{    private TextPaint mTextPaint;    private String mText;    private int mAscent;    private String mStrEllipsis;    private String mStrEllipsisMore;    private int mMaxLines;    private boolean mDrawEllipsizeMoreString;    private int mColorEllipsizeMore;    private boolean mRightAlignEllipsizeMoreString;    private boolean mExpanded;    private LineBreaker mBreakerExpanded;    private LineBreaker mBreakerCollapsed;        public MyClipTextView(Context context) {        super(context);        // TODO Auto-generated constructor stub        mExpanded = false;        mDrawEllipsizeMoreString = true;        mRightAlignEllipsizeMoreString = false;        mMaxLines = -1;        mStrEllipsis = "...";        mStrEllipsisMore = "";        mColorEllipsizeMore = 0xFF0000FF;                mBreakerExpanded = new LineBreaker();                mBreakerCollapsed = new LineBreaker();                // Default font size and color.        mTextPaint = new TextPaint();        mTextPaint.setAntiAlias(true);        mTextPaint.setTextSize(13);        mTextPaint.setColor(0xFF000000);        mTextPaint.setTextAlign(Align.LEFT);    }        /**     * Sets the text to display in this widget.     * @param text The text to display.     */    public void setText(String text) {        mText = text;        requestLayout();        invalidate();    }    /**     * Sets the text size for this widget.     * @param size Font size.     */    public void setTextSize(int size) {        mTextPaint.setTextSize(size);        requestLayout();        invalidate();    }    /**     * Sets the text color for this widget.     * @param color ARGB value for the text.     */    public void setTextColor(int color) {        mTextPaint.setColor(color);        invalidate();    }    /**     * The string to append when ellipsizing. Must be shorter than the available     * width for a single line!     * @param ellipsis The ellipsis string to use, like "...", or "-----".     */    public void setEllipsis(String ellipsis) {        mStrEllipsis = ellipsis;    }        /**     * Optional extra ellipsize string. This     * @param ellipsisMore     */    public void setEllipsisMore(String ellipsisMore) {        mStrEllipsisMore = ellipsisMore;    }        /**     * The maximum number of lines to allow, height-wise.     * @param maxLines     */    public void setMaxLines(int maxLines) {        mMaxLines = maxLines;    }        /**     * Turn drawing of the optional ellipsizeMore string on or off.     * @param drawEllipsizeMoreString Yes or no.     */    public void setDrawEllipsizeMoreString(boolean drawEllipsizeMoreString) {        mDrawEllipsizeMoreString = drawEllipsizeMoreString;    }        /**     * Font color to use for the optional ellipsizeMore string.     * @param color ARGB color.     */    public void setColorEllpsizeMore(int color) {        mColorEllipsizeMore = color;    }        /**     * When drawing the ellipsizeMore string, either draw it wherever ellipsizing on the last     * line occurs, or always right align it. On by default.     * @param rightAlignEllipsizeMoreString Yes or no.     */    public void setRightAlignEllipsizeMoreString(boolean rightAlignEllipsizeMoreString) {        mRightAlignEllipsizeMoreString = rightAlignEllipsizeMoreString;    }        /**     * @see android.view.View#measure(int, int)     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(            measureWidth(widthMeasureSpec),            measureHeight(heightMeasureSpec));    }    /**     * Determines the width of this view     * @param measureSpec A measureSpec packed into an int     * @return The width of the view, honoring constraints from measureSpec     */    private int measureWidth(int measureSpec) {        int result = 0;        int specMode = MeasureSpec.getMode(measureSpec);        int specSize = MeasureSpec.getSize(measureSpec);        if (specMode == MeasureSpec.EXACTLY) {            // We were told how big to be.            result = specSize;                        // Format the text using this exact width, and the current mode.            breakWidth(specSize);        }         else {            if (specMode == MeasureSpec.AT_MOST) {                // Use the AT_MOST size - if we had very short text, we may need even less                // than the AT_MOST value, so return the minimum.                result = breakWidth(specSize);                result = Math.min(result, specSize);            }            else {                // We're not given any width - so in this case we assume we have an unlimited                // width?                breakWidth(specSize);            }        }        return result;    }    /**     * Determines the height of this view     * @param measureSpec A measureSpec packed into an int     * @return The height of the view, honoring constraints from measureSpec     */    private int measureHeight(int measureSpec) {        int result = 0;        int specMode = MeasureSpec.getMode(measureSpec);        int specSize = MeasureSpec.getSize(measureSpec);        mAscent = (int) mTextPaint.ascent();        if (specMode == MeasureSpec.EXACTLY) {            // We were told how big to be, so nothing to do.            result = specSize;        }         else {            // The lines should already be broken up. Calculate our max desired height            // for our current mode.            int numLines;            if (mExpanded) {                numLines = mBreakerExpanded.getLines().size();            }            else {                numLines = mBreakerCollapsed.getLines().size();            }            result = numLines * (int) (-mAscent + mTextPaint.descent())                   + getPaddingTop()                   + getPaddingBottom();            // Respect AT_MOST value if that was what is called for by measureSpec.            if (specMode == MeasureSpec.AT_MOST) {                result = Math.min(result, specSize);            }        }        return result;    }    /**     * Render the text     *      * @see android.view.View#onDraw(android.graphics.Canvas)     */    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);                List lines;        LineBreaker breaker;        if (mExpanded) {            breaker = mBreakerExpanded;            lines = mBreakerExpanded.getLines();        }        else {            breaker = mBreakerCollapsed;            lines = mBreakerCollapsed.getLines();        }                float x = getPaddingLeft();        float y = getPaddingTop() + (-mAscent);        for (int i = 0; i < lines.size(); i++) {            // Draw the current line.            int[] pair = lines.get(i);            canvas.drawText(mText, pair[0], pair[1]+1, x, y, mTextPaint);                        // Draw the ellipsis if necessary.            if (i == lines.size() - 1) {                if (breaker.getRequiredEllipsis()) {                    canvas.drawText(mStrEllipsis, x + breaker.getLengthLastEllipsizedLine(), y, mTextPaint);                    if (mDrawEllipsizeMoreString) {                        int lastColor = mTextPaint.getColor();                        mTextPaint.setColor(mColorEllipsizeMore);                        if (mRightAlignEllipsizeMoreString) {                            // Seems to not be right...                            canvas.drawText(mStrEllipsisMore, canvas.getWidth()-(breaker.getLengthEllipsisMore()+getPaddingRight()+getPaddingLeft()), y, mTextPaint);                        }                        else {                            canvas.drawText(mStrEllipsisMore, x + breaker.getLengthLastEllipsizedLinePlusEllipsis(), y, mTextPaint);                        }                        mTextPaint.setColor(lastColor);                    }                }            }                        y += (-mAscent + mTextPaint.descent());            if (y > canvas.getHeight()) {                break;            }        }    }        public boolean getIsExpanded() {        return mExpanded;    }        public void expand() {        mExpanded = true;        requestLayout();        invalidate();    }        public void collapse() {        mExpanded = false;        requestLayout();        invalidate();    }        private int breakWidth(int availableWidth) {        int widthUsed = 0;        if (mExpanded) {            widthUsed =              mBreakerExpanded.breakText(                 mText,                 availableWidth - getPaddingLeft() - getPaddingRight(),                 mTextPaint);        }        else {            widthUsed =              mBreakerCollapsed.breakText(                mText,                 mStrEllipsis,                 mStrEllipsisMore,                 mMaxLines,                 availableWidth - getPaddingLeft() - getPaddingRight(),                 mTextPaint);        }                return widthUsed + getPaddingLeft() + getPaddingRight();    }            /**     * Used internally to break a string into a list of integer pairs. The pairs are     * start and end locations for lines given the current available layout width.     */    private static class LineBreaker    {        /** Was the input text long enough to need an ellipsis? */        private boolean mRequiredEllipsis;                /** Beginning and end indices for the input string. */        private ArrayList mLines;                /** The width in pixels of the last line, used to draw the ellipsis if necessary. */        private float mLengthLastLine;                /** The width of the ellipsis string, so we know where to draw the ellipsisMore string         *  if necessary.         */        private float mLengthEllipsis;                /** The width of the ellipsizeMore string, same use as above. */        private float mLengthEllipsisMore;                        public LineBreaker() {            mRequiredEllipsis = false;            mLines = new ArrayList();        }        /**         * Used for breaking text in 'expanded' mode, which needs no ellipse.         * Uses as many lines as is necessary to accomodate the entire input         * string.         * @param input String to be broken.         * @param maxWidth Available layout width.         * @param tp Current paint object with styles applied to it.         */        public int breakText(String input,                                 int maxWidth,                              TextPaint tp)         {            return breakText(input, null, null, -1, maxWidth, tp);        }        /**         * Used for breaking text, honors ellipsizing. The string will be broken into lines using         * the available width. The last line will subtract the physical width of the ellipsis         * string from maxWidth to reserve room for the ellipsis. If the ellpsisMore string is set,         * then space will also be reserved for its length as well.         * @param input String to be broken.         * @param ellipsis Ellipsis string, like "..."         * @param ellipsisMore Optional space reservation after the ellipsis, like " Read More!"         * @param maxLines Max number of lines to allow before ellipsizing.         * @param maxWidth Available layout width.         * @param tp Current paint object with styles applied to it.         */        public int breakText(String input,                                 String ellipsis,                                String ellipsisMore,                             int maxLines,                              int maxWidth,                              TextPaint tp)         {            mLines.clear();            mRequiredEllipsis = false;            mLengthLastLine = 0.0f;            mLengthEllipsis = 0.0f;            mLengthEllipsisMore = 0.0f;                        // If maxWidth is -1, interpret that as meaning to render the string on a single            // line. Skip everything.            if (maxWidth == -1) {                mLines.add(new int[]{ 0, input.length() });                return (int)(tp.measureText(input) + 0.5f);            }            // Measure the ellipsis string, and the ellipsisMore string if valid.            if (ellipsis != null) {                mLengthEllipsis = tp.measureText(ellipsis);            }            if (ellipsisMore != null) {                mLengthEllipsisMore = tp.measureText(ellipsisMore);            }            // Start breaking.            int posStartThisLine = -1;            float lengthThisLine = 0.0f;            boolean breakWords = true;            int pos = 0;            while (pos < input.length()) {                                if (posStartThisLine == -1) {                    posStartThisLine = pos;                }                                if (mLines.size() == maxLines) {                    mRequiredEllipsis = true;                    break;                }                                float widthOfChar = tp.measureText(input.charAt(pos) + "");                boolean newLineRequired = false;                                if(!hasChinese(input)){/**english*/                    // Check for a new line character or if we've run over max width.                    if (input.charAt(pos) == '\n') {                        newLineRequired = true;                                                // We want the current line to go up to the character right before the                        // new line char, and we want the next line to start at the char after                        // this new line char.                        mLines.add(new int[] { posStartThisLine, pos-1 });                    }else if (lengthThisLine + widthOfChar >= maxWidth) {                        newLineRequired = true;                        // We need to backup if we are in the middle of a word.                        if (input.charAt(pos) == ' ' || breakWords == false) {                            // Backup one character, because it doesn't fit on this line.                            pos--;                                                        // So this line includes up to the character before the space.                            mLines.add(new int[] { posStartThisLine, pos });                        }else {                            // Backup until we are at a space.                            Log.v("*******", "*********************************now char = " + input.charAt(pos));                            while (input.charAt(pos) != ' ') {                                pos--;                            }                                                        // This line includes up to the space.                            mLines.add(new int[] { posStartThisLine, pos });                        }                    }                }else{/**chinese*/                    // Check for a new line character or if we've run over max width.                    if (input.charAt(pos) == '\n') {                        newLineRequired = true;                                                // We want the current line to go up to the character right before the                        // new line char, and we want the next line to start at the char after                        // this new line char.                        mLines.add(new int[] { posStartThisLine, pos-1 });                    }else if (lengthThisLine + widthOfChar >= maxWidth) {                        newLineRequired = true;                            // This line includes up to the space.                            mLines.add(new int[] { posStartThisLine, pos });                    }                }                                                if (newLineRequired) {                    // The next cycle should reset the position if it sees it's -1 (to whatever i is).                    posStartThisLine = -1;                                        // Reset line length for next iteration.                    lengthThisLine = 0.0f;                                        // When we get to the last line, subtract the width of the ellipsis.                    if (mLines.size() == maxLines - 1) {                        maxWidth -= (mLengthEllipsis + mLengthEllipsisMore);                        // We also don't need to break on a full word, it'll look a little                        // cleaner if all breaks on the final lines break in the middle of                        // the last word.                        breakWords = false;                    }                }else {                    if(!hasChinese(input)){/**english*/                        lengthThisLine += widthOfChar;                    }else{/**chinese*/                        lengthThisLine += (widthOfChar + 0.5f);                    }                                        // If we're on the last character of the input string, add on whatever we have leftover.                    if (pos == input.length() - 1) {                        mLines.add(new int[] { posStartThisLine, pos });                    }                }                                pos++;            }                        // If we ellipsized, then add the ellipsis string to the end.            if (mRequiredEllipsis) {                int[] pairLast = mLines.get(mLines.size()-1);                mLengthLastLine = tp.measureText(input.substring(pairLast[0], pairLast[1] + 1));            }                        // If we required only one line, return its length, otherwise we used            // whatever the maxWidth supplied was.            if (mLines.size() == 0) {                return 0;            }            else if (mLines.size() == 1) {                return (int)(tp.measureText(input) + 0.5f);            }            else {                return maxWidth;            }        }                public boolean getRequiredEllipsis() {            return mRequiredEllipsis;        }                public List getLines() {            return mLines;        }                public float getLengthLastEllipsizedLine() {            return mLengthLastLine;        }                public float getLengthLastEllipsizedLinePlusEllipsis() {            return mLengthLastLine + mLengthEllipsis;        }                public float getLengthEllipsis() {            return mLengthEllipsis;        }                public float getLengthEllipsisMore() {            return mLengthEllipsisMore;        }                /**         * ÅжÏÎı¾ÖÐÊÇ·ñº¬ÓÐÖÐÎÄ         */        private boolean hasChinese(String input){            return input.getBytes().length != input.length();        }    }}

package com.test;import android.app.Activity;import android.os.Bundle;import android.view.ViewGroup.LayoutParams;import android.widget.LinearLayout;public class MyClipActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {                super.onCreate(savedInstanceState);        setContentView(R.layout.main);                LinearLayout llContent = (LinearLayout) findViewById(R.id.llvg);        MyClipTextView tv2 = new MyClipTextView(this);        tv2.setLayoutParams(new LayoutParams(100, 100));        tv2.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));        tv2.setEllipsis("...");        tv2.setEllipsisMore("");        tv2.setMaxLines(5);        tv2.setText(getResources().getString(R.string.testcn1));        tv2.setPadding(10, 10, 10, 10);        tv2.setBackgroundColor(0xFFFCDFB2);                llContent.addView(tv2);    }}

package ru.gzt.newsreader.widgets;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint.Align;import android.text.TextPaint;import android.util.Log;import android.view.View;import java.util.ArrayList;import java.util.List;public class TextViewMultilineEllipse extends View {  private TextPaint mTextPaint;  private String mText;  private int mAscent;  private String mStrEllipsis;  private String mStrEllipsisMore;  private int mMaxLines;  private boolean mDrawEllipsizeMoreString;  private int mColorEllipsizeMore;  private boolean mRightAlignEllipsizeMoreString;  private boolean mExpanded;  private LineBreaker mBreakerExpanded;  private LineBreaker mBreakerCollapsed;  public TextViewMultilineEllipse(Context context) {    super(context);    // TODO Auto-generated constructor stub    mExpanded = false;    mDrawEllipsizeMoreString = true;    mRightAlignEllipsizeMoreString = false;    mMaxLines = -1;    mStrEllipsis = "...";    mStrEllipsisMore = "";    mColorEllipsizeMore = 0xFF0000FF;    mBreakerExpanded = new LineBreaker();    mBreakerCollapsed = new LineBreaker();    // Default font size and color.    mTextPaint = new TextPaint();    mTextPaint.setAntiAlias(true);    mTextPaint.setTextSize(13);    mTextPaint.setColor(0xFF000000);    mTextPaint.setTextAlign(Align.LEFT);  }  /**   * Sets the text to display in this widget.   *    * @param text   *          The text to display.   */  public void setText(String text) {    mText = text;    requestLayout();    invalidate();  }  /**   * Sets the text size for this widget.   *    * @param size   *          Font size.   */  public void setTextSize(int size) {    mTextPaint.setTextSize(size);    requestLayout();    invalidate();  }  /**   * Sets the text color for this widget.   *    * @param color   *          ARGB value for the text.   */  public void setTextColor(int color) {    mTextPaint.setColor(color);    invalidate();  }  /**   * The string to append when ellipsizing. Must be shorter than the available   * width for a single line!   *    * @param ellipsis   *          The ellipsis string to use, like "...", or "-----".   */  public void setEllipsis(String ellipsis) {    mStrEllipsis = ellipsis;  }  /**   * Optional extra ellipsize string. This   *    * @param ellipsisMore   */  public void setEllipsisMore(String ellipsisMore) {    mStrEllipsisMore = ellipsisMore;  }  /**   * The maximum number of lines to allow, height-wise.   *    * @param maxLines   */  public void setMaxLines(int maxLines) {    mMaxLines = maxLines;  }  /**   * Turn drawing of the optional ellipsizeMore string on or off.   *    * @param drawEllipsizeMoreString   *          Yes or no.   */  public void setDrawEllipsizeMoreString(boolean drawEllipsizeMoreString) {    mDrawEllipsizeMoreString = drawEllipsizeMoreString;  }  /**   * Font color to use for the optional ellipsizeMore string.   *    * @param color   *          ARGB color.   */  public void setColorEllpsizeMore(int color) {    mColorEllipsizeMore = color;  }  /**   * When drawing the ellipsizeMore string, either draw it wherever ellipsizing   * on the last line occurs, or always right align it. On by default.   *    * @param rightAlignEllipsizeMoreString   *          Yes or no.   */  public void setRightAlignEllipsizeMoreString(boolean rightAlignEllipsizeMoreString) {    mRightAlignEllipsizeMoreString = rightAlignEllipsizeMoreString;  }  /**   * @see android.view.View#measure(int, int)   */  @Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));  }  /**   * Determines the width of this view   *    * @param measureSpec   *          A measureSpec packed into an int   * @return The width of the view, honoring constraints from measureSpec   */  private int measureWidth(int measureSpec) {    int result = 0;    int specMode = MeasureSpec.getMode(measureSpec);    int specSize = MeasureSpec.getSize(measureSpec);    if (specMode == MeasureSpec.EXACTLY) {      // We were told how big to be.      result = specSize;      // Format the text using this exact width, and the current mode.      breakWidth(specSize);    } else {      if (specMode == MeasureSpec.AT_MOST) {        // Use the AT_MOST size - if we had very short text, we may need even        // less        // than the AT_MOST value, so return the minimum.        result = breakWidth(specSize);        result = Math.min(result, specSize);      } else {        // We're not given any width - so in this case we assume we have an        // unlimited        // width?        breakWidth(specSize);      }    }    return result;  }  /**   * Determines the height of this view   *    * @param measureSpec   *          A measureSpec packed into an int   * @return The height of the view, honoring constraints from measureSpec   */  private int measureHeight(int measureSpec) {    int result = 0;    int specMode = MeasureSpec.getMode(measureSpec);    int specSize = MeasureSpec.getSize(measureSpec);    mAscent = (int) mTextPaint.ascent();    if (specMode == MeasureSpec.EXACTLY) {      // We were told how big to be, so nothing to do.      result = specSize;    } else {      // The lines should already be broken up. Calculate our max desired height      // for our current mode.      int numLines;      if (mExpanded) {        numLines = mBreakerExpanded.getLines().size();      } else {        numLines = mBreakerCollapsed.getLines().size();      }      result = numLines * (int) (-mAscent + mTextPaint.descent()) + getPaddingTop() + getPaddingBottom();      // Respect AT_MOST value if that was what is called for by measureSpec.      if (specMode == MeasureSpec.AT_MOST) {        result = Math.min(result, specSize);      }    }    return result;  }  /**   * Render the text   *    * @see android.view.View#onDraw(android.graphics.Canvas)   */  @Override  protected void onDraw(Canvas canvas) {    super.onDraw(canvas);    List lines;    LineBreaker breaker;    if (mExpanded) {      breaker = mBreakerExpanded;      lines = mBreakerExpanded.getLines();    } else {      breaker = mBreakerCollapsed;      lines = mBreakerCollapsed.getLines();    }    float x = getPaddingLeft();    float y = getPaddingTop() + (-mAscent);    for (int i = 0; i < lines.size(); i++) {      // Draw the current line.      int[] pair = lines.get(i);      canvas.drawText(mText, pair[0], pair[1] + 1, x, y, mTextPaint);      // Draw the ellipsis if necessary.      if (i == lines.size() - 1) {        if (breaker.getRequiredEllipsis()) {          canvas.drawText(mStrEllipsis, x + breaker.getLengthLastEllipsizedLine(), y, mTextPaint);          if (mDrawEllipsizeMoreString) {            int lastColor = mTextPaint.getColor();            mTextPaint.setColor(mColorEllipsizeMore);            if (mRightAlignEllipsizeMoreString) {              // Seems to not be right...              canvas.drawText(mStrEllipsisMore, canvas.getWidth() - (breaker.getLengthEllipsisMore() + getPaddingRight() + getPaddingLeft()), y, mTextPaint);            } else {              canvas.drawText(mStrEllipsisMore, x + breaker.getLengthLastEllipsizedLinePlusEllipsis(), y, mTextPaint);            }            mTextPaint.setColor(lastColor);          }        }      }      y += (-mAscent + mTextPaint.descent());      if (y > canvas.getHeight()) {        break;      }    }  }  public boolean getIsExpanded() {    return mExpanded;  }  public void expand() {    mExpanded = true;    requestLayout();    invalidate();  }  public void collapse() {    mExpanded = false;    requestLayout();    invalidate();  }  private int breakWidth(int availableWidth) {    int widthUsed = 0;    if (mExpanded) {      widthUsed = mBreakerExpanded.breakText(mText, availableWidth - getPaddingLeft() - getPaddingRight(), mTextPaint);    } else {      widthUsed = mBreakerCollapsed.breakTextFast(mText, mStrEllipsis, mStrEllipsisMore, mMaxLines, availableWidth - getPaddingLeft() - getPaddingRight(),          mTextPaint);    }    return widthUsed + getPaddingLeft() + getPaddingRight();  }  /**   * Used internally to break a string into a list of integer pairs. The pairs   * are start and end locations for lines given the current available layout   * width.   */  private static class LineBreaker {    /** Was the input text long enough to need an ellipsis? */    private boolean mRequiredEllipsis;    /** Beginning and end indices for the input string. */    private ArrayList mLines;    /**     * The width in pixels of the last line, used to draw the ellipsis if     * necessary.     */    private float mLengthLastLine;    /**     * The width of the ellipsis string, so we know where to draw the     * ellipsisMore string if necessary.     */    private float mLengthEllipsis;    /** The width of the ellipsizeMore string, same use as above. */    private float mLengthEllipsisMore;    public LineBreaker() {      mRequiredEllipsis = false;      mLines = new ArrayList();    }    /**     * Used for breaking text in 'expanded' mode, which needs no ellipse. Uses     * as many lines as is necessary to accomodate the entire input string.     *      * @param input     *          String to be broken.     * @param maxWidth     *          Available layout width.     * @param tp     *          Current paint object with styles applied to it.     */    public int breakText(String input, int maxWidth, TextPaint tp) {      // return breakText(input, null, null, -1, maxWidth, tp);      return breakTextFast(input, maxWidth, tp);    }    public final int breakTextFast(String input, int maxWidth, TextPaint tp) {      CharSequence textCharArray = input.subSequence(0, input.length());      int inputLength = textCharArray.length();      mLines.clear();      int offset = 0;      while (offset < inputLength) {        int numOfChars = tp.breakText(textCharArray, offset, textCharArray.length(), true, maxWidth, null);        mLines.add(new int[] { offset, (offset += numOfChars) - 1 });      }      return maxWidth;    }    public final int breakTextFast(String input, String ellipsis, String ellipsisMore, int maxLines, int maxWidth, TextPaint tp) {      CharSequence textCharArray = input.subSequence(0, input.length());      int inputLength = textCharArray.length();      mRequiredEllipsis = false;      mLengthLastLine = 0.0f;      mLengthEllipsis = 0.0f;      mLengthEllipsisMore = 0.0f;      if (ellipsis != null) {        mLengthEllipsis = tp.measureText(ellipsis);      }      if (ellipsisMore != null) {        mLengthEllipsis = tp.measureText(ellipsisMore);      }      float maxLineWidth = 0;      float[] measuredWidth = new float[1];      measuredWidth[0] = 0;      mLines.clear();      int offset = 0, k = 0;      while (k++ < maxLines && offset < inputLength) {        int numOfChars = tp.breakText(textCharArray, offset, textCharArray.length(), true, maxWidth, measuredWidth);        maxLineWidth = maxLineWidth > measuredWidth[0] ? maxLineWidth : measuredWidth[0];        mLines.add(new int[] { offset, (offset += numOfChars) - 1 });      }      int[] location = mLines.get(mLines.size() - 1);      if (k >= maxLines && location[1] != inputLength - 1) {        mRequiredEllipsis = true;        location[1] = location[0] + tp.breakText(textCharArray, location[0], location[1], true, maxWidth - (mLengthEllipsis + mLengthEllipsis), measuredWidth)            - 1;        maxLineWidth = maxLineWidth > measuredWidth[0] ? maxLineWidth : measuredWidth[0];      }      mLengthLastLine = measuredWidth[0];      return (int) maxLineWidth;    }    public boolean getRequiredEllipsis() {      return mRequiredEllipsis;    }    public List getLines() {      return mLines;    }    public float getLengthLastEllipsizedLine() {      return mLengthLastLine;    }    public float getLengthLastEllipsizedLinePlusEllipsis() {      return mLengthLastLine + mLengthEllipsis;    }    public float getLengthEllipsis() {      return mLengthEllipsis;    }    public float getLengthEllipsisMore() {      return mLengthEllipsisMore;    }    /**     * ÅжÏÎı¾ÖÐÊÇ·ñº¬ÓÐÖÐÎÄ     */    private boolean hasChinese(String input) {      return input.getBytes().length != input.length();    }  }}

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 ellipsizeListeners = new ArrayList();        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        }}



更多相关文章

  1. android之Menu
  2. Android(安卓)的网络编程(17)-android显示网络图片
  3. [原]如何在Android用FFmpeg+SDL2.0解码显示图像
  4. 『ANDROID』android:layout_gravity和android:gravity属性的区别
  5. Android(安卓)面试题总结之Android(安卓)基础(五)
  6. android设置Activity背景色为透明的2种方法
  7. 【Android(安卓)TextView/EditText 不允许换行】android:lines
  8. android实现密码框右侧显示小眼睛
  9. android:screenOrientation属性(设置横屏竖屏)

随机推荐

  1. Android图形显示系统——下层显示4:图层合
  2. android OOM
  3. 如何检索Android设备的唯一ID
  4. 【幻灯片分享】凡客移动应用之Android(安
  5. 关于Android(安卓)的内存泄露及分析
  6. Android进阶-Android(安卓)Handler消息机
  7. android UI进阶之android中隐藏的layout
  8. Android(安卓)中的 Service 全面总结
  9. android活动的生存期
  10. Android(安卓)View 的刷新机制