textview中有很多行,我只让它显示三行,最后显示...,我设置了android:maxLines="3"和android:ellipsize="end",
16lz
2021-12-04
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" 去掉,代码控制,也可以:
(注意得在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 }}
更多相关文章
- android之Menu
- Android(安卓)的网络编程(17)-android显示网络图片
- [原]如何在Android用FFmpeg+SDL2.0解码显示图像
- 『ANDROID』android:layout_gravity和android:gravity属性的区别
- Android(安卓)面试题总结之Android(安卓)基础(五)
- android设置Activity背景色为透明的2种方法
- 【Android(安卓)TextView/EditText 不允许换行】android:lines
- android实现密码框右侧显示小眼睛
- android:screenOrientation属性(设置横屏竖屏)