編輯:Android開發實例
大家經常會用到系統默認的TextView,TextView可以很好地適應單行長文本(尾部自動打上省略號),以及可以完整顯示多行文本(TextView的寬高足夠大)。但如果是很多行的文本而TextView又足夠大的時候,則會出現以下這種情況.......超出的文本受TextView大小限制,不能完全顯示。
本文主要實現一個能夠適應多行長文本的TextView,自動縮減長文本並在結尾補上省略號。如下圖:紅色部分為普通的TextView,綠色部分為本文實現的TextView。
本文的源碼可以到 http://download.csdn.net/detail/hellogv/4298631 下載,本文的TextViewMultilineEllipse.java改自http://code.google.com/p/android-textview-multiline-ellipse/以及http://code.google.com/p/android/的MyClipTextView.java,相對於前面2者,本文使用哈希表來保存每次onMeasure()計算所得的寬高,大幅減少重復計算寬高。
本文的主Activity的源碼如下:
- public class AutoFixTextViewActivity extends Activity {
- private LinearLayout linearLayout1;
- private TextViewMultilineEllipse tvMultilineEllipse;
- private TextView tvNormal;
- //水調歌頭,大家懂的
- private final String text="明月幾時有?把酒問青天。不知天上宮阙,今夕是何年。\n"
- +"我欲乘風歸去,又恐瓊樓玉宇,高處不勝寒。\n"
- +"起舞弄清影,何似在人間。\n"
- +"轉朱閣,低绮戶,照無眠。不應有恨,何事長向別時圓?\n"
- +"人有悲歡離合,月有陰晴圓缺,此事古難全。\n"
- +"但願人長久,千裡共婵娟。";
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- this.setTitle("適應多行文本的Android TextView---hellogv");
- //共同的寬高
- LayoutParams lp=new LayoutParams(LayoutParams.FILL_PARENT,100);
- //----用TextView來顯示換行長文本----//
- tvNormal=(TextView)this.findViewById(R.id.tvNormal);
- tvNormal.setLayoutParams(lp); //限制TextView的寬高
- tvNormal.setEllipsize(TextUtils.TruncateAt.END);
- tvNormal.setSingleLine(false);
- tvNormal.setMaxLines(5);
- tvNormal.setText(text);
- //----用TextViewMultilineEllipse來顯示換行長文本----//
- linearLayout1=(LinearLayout) this.findViewById(R.id.linearLayout1);
- tvMultilineEllipse = new TextViewMultilineEllipse(this);
- tvMultilineEllipse.setLayoutParams(lp);//限制TextView的寬高
- tvMultilineEllipse.setEllipsis("...");//...替換剩余字符串
- tvMultilineEllipse.setMaxLines(5);
- tvMultilineEllipse.setTextSize((int)tvNormal.getTextSize());//設置文字大小
- tvMultilineEllipse.setTextColor(Color.WHITE);
- tvMultilineEllipse.setText(text);//設置文本
- //在代碼裡添加tvMultilineEllipse,暫時不支持Layout裡直接添加
- linearLayout1.addView(tvMultilineEllipse);
- }
- }
TextViewMultilineEllipse.java源碼如下,有點多,讀者可以直接忽略:
- public class TextViewMultilineEllipse extends TextView{
- 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;
- /**hashMapW是優化的關鍵點,通過哈希表來減少計算次數*/
- private HashMap<Integer,Integer> hashMapW=new HashMap<Integer,Integer>();
- 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);
- setDrawingCacheEnabled(true);
- }
- /**
- * 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<int[]> 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) {
- if(hashMapW.containsKey(availableWidth))
- return hashMapW.get(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);
- }
- hashMapW.put(availableWidth, widthUsed + getPaddingLeft() + getPaddingRight());
- 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<int[]> 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<int[]>();
- }
- /**
- * 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<int[]> 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();
- }
- }
- }
本文實例講述了Android實現模仿UCweb菜單效果的方法。分享給大家供大家參考。具體如下: UCWeb的菜單看起來不錯,自己模仿做一個,思路實現如下: 1、保
•android-support-v4.jar,這是谷歌官方給我們提供的一個兼容低版本Android設備的軟件包,裡面包囊了只有在Android3.0
JSON代表JavaScript對象符號。它是一個獨立的數據交換格式,是XML的最佳替代品。本章介紹了如何解析JSON文件,並從中提取所需的信息。Android提供了四個
Android應用程序可以在許多不同地區的許多設備上運行。為了使應用程序更具交互性,應用程序應該處理以適合應用程序將要使用的語言環境方面的文字,數字,文件等。在本章中,我