From 0505a3eef5b679cb724d60f26f48dd641944b74d Mon Sep 17 00:00:00 2001 From: i0l0a Date: Mon, 30 Sep 2019 13:08:26 +0330 Subject: [PATCH] fix height calculation --- .idea/JustifiedTextView.iml | 9 + .idea/encodings.xml | 4 + .idea/misc.xml | 32 ++ .idea/modules.xml | 8 + .idea/vcs.xml | 6 + .idea/workspace.xml | 21 + src/ir/noghteh/JustifiedTextView.java | 786 +++++++++++++------------- 7 files changed, 476 insertions(+), 390 deletions(-) create mode 100644 .idea/JustifiedTextView.iml create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml diff --git a/.idea/JustifiedTextView.iml b/.idea/JustifiedTextView.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/JustifiedTextView.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..15a15b2 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..ad98bfd --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..857d823 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..a8fe0f9 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ir/noghteh/JustifiedTextView.java b/src/ir/noghteh/JustifiedTextView.java index 91a40f3..3743864 100755 --- a/src/ir/noghteh/JustifiedTextView.java +++ b/src/ir/noghteh/JustifiedTextView.java @@ -1,8 +1,5 @@ package ir.noghteh; -import java.util.ArrayList; -import java.util.List; - import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint.Align; @@ -10,12 +7,14 @@ import android.graphics.Typeface; import android.text.TextPaint; import android.util.AttributeSet; -import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnGlobalLayoutListener; +import java.util.ArrayList; +import java.util.List; + /** * @author Mohsen Navabi * @version 1.0 @@ -23,394 +22,401 @@ * @see http://www.noghteh.ir */ public class JustifiedTextView extends View { - - private Context mContext; - - private XmlToClassAttribHandler mXmlParser; - - private TextPaint textPaint; - - private int lineSpace=0; - - private int lineHeight; - - private int textAreaWidth; - - private int measuredViewHeight,measuredViewWidth; - - private String text; - - private List lineList=new ArrayList(); - - - /** - * when we want to draw text after view created to avoid loop in drawing we use this boolean - */ - boolean hasTextBeenDrown=false; - - public JustifiedTextView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - constructor(context,attrs); - } - public JustifiedTextView(Context context, AttributeSet attrs) { - super(context, attrs); - constructor(context,attrs); - } - public JustifiedTextView(Context context) { - super(context); - constructor(context,null); - - } - - private void constructor(Context context, AttributeSet attrs) { - - mContext=context; - mXmlParser=new XmlToClassAttribHandler(mContext,attrs); - initTextPaint(); - - if (attrs!=null){ - String text; - int textColor; - int textSize; - int textSizeUnit; - - text=mXmlParser.getTextValue(); - textColor=mXmlParser.getColorValue(); - textSize=mXmlParser.getTextSize(); - textSizeUnit=mXmlParser.gettextSizeUnit(); - - - setText(text); - setTextColor(textColor); - if (textSizeUnit==-1) - setTextSize(textSize); - else - setTextSize(textSizeUnit, textSize); - + + private Context mContext; + + private XmlToClassAttribHandler mXmlParser; + + private TextPaint textPaint; + + private int lineSpace = 0; + + private int lineHeight; + + private int textAreaWidth; + + private int measuredViewHeight, measuredViewWidth; + + private String text; + + private List lineList = new ArrayList(); + + + /** + * when we want to draw text after view created to avoid loop in drawing we use this boolean + */ + boolean hasTextBeenDrown = false; + + public JustifiedTextView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + constructor(context, attrs); + } + + public JustifiedTextView(Context context, AttributeSet attrs) { + super(context, attrs); + constructor(context, attrs); + } + + public JustifiedTextView(Context context) { + super(context); + constructor(context, null); + + } + + private void constructor(Context context, AttributeSet attrs) { + + mContext = context; + mXmlParser = new XmlToClassAttribHandler(mContext, attrs); + initTextPaint(); + + if (attrs != null) { + String text; + int textColor; + int textSize; + int textSizeUnit; + + text = mXmlParser.getTextValue(); + textColor = mXmlParser.getColorValue(); + textSize = mXmlParser.getTextSize(); + textSizeUnit = mXmlParser.gettextSizeUnit(); + + + setText(text); + setTextColor(textColor); + if (textSizeUnit == -1) + setTextSize(textSize); + else + setTextSize(textSizeUnit, textSize); + // setText(XmlToClassAttribHandler.GetAttributeStringValue(mContext, attrs, namespace, key, "")); - - } - - ViewTreeObserver observer=getViewTreeObserver(); - - - observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { - - @Override - public void onGlobalLayout() { - - if (hasTextBeenDrown) - return; - hasTextBeenDrown=true; - setTextAreaWidth(getWidth()-(getPaddingLeft()+getPaddingRight())); - calculate(); - - } - - - }); - - } - - private void calculate() { - setLineHeight(getTextPaint()); - lineList.clear(); - lineList=divideOriginalTextToStringLineList(getText()); - setMeasuredDimentions(lineList.size(),getLineHeight(),getLineSpace()); - measure(getMeasuredViewWidth(),getMeasuredViewHeight()); - } - - private void initTextPaint(){ - textPaint=new TextPaint(TextPaint.ANTI_ALIAS_FLAG); - textPaint.setTextAlign(Align.RIGHT); - } - + + } + + ViewTreeObserver observer = getViewTreeObserver(); + + + observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { + + @Override + public void onGlobalLayout() { + + if (hasTextBeenDrown) + return; + hasTextBeenDrown = true; + setTextAreaWidth(getWidth() - (getPaddingLeft() + getPaddingRight())); + calculate(); + + } + + + }); + + } + + private void calculate() { + setLineHeight(getTextPaint()); + lineList.clear(); + lineList = divideOriginalTextToStringLineList(getText()); + setMeasuredDimentions(lineList.size(), getLineHeight(), getLineSpace()); + measure(getMeasuredViewWidth(), getMeasuredViewHeight()); + } + + private void initTextPaint() { + textPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); + textPaint.setTextAlign(Align.RIGHT); + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (getMeasuredViewWidth()>0){ - requestLayout(); - setMeasuredDimension(getMeasuredViewWidth(),getMeasuredViewHeight()); - } - else{ - super.onMeasure(widthMeasureSpec, heightMeasureSpec ); - } - invalidate(); - } - - - private int rowIndex=0,colIndex=0; - @Override - protected void onDraw(Canvas canvas) { - - rowIndex=getPaddingTop(); - if (getAlignment()==Align.RIGHT) - colIndex=getPaddingLeft()+getTextAreaWidth(); - else - colIndex=getPaddingLeft(); - - for (int i=0;i divideOriginalTextToStringLineList(String originalText) { - - List listStringLine=new ArrayList(); - - String line=""; - float textWidth; - - String[] listParageraphes = originalText.split("\n"); - - for(int j=0;j0){ - - gapIndex=lineString.indexOf(" ", gapIndex+2); - if (gapIndex==-1){ - gapIndex=0; - gapIndex=lineString.indexOf(" ", gapIndex+1); - if (gapIndex==-1) - return lineString; - } - - lineString=lineString.substring(0, gapIndex)+ " " +lineString.substring(gapIndex+1, lineString.length()); - - lineWidth=textPaint.measureText(lineString); - } - return lineString; - } - /*** - * this method calculate height for a line of text according to defined TextPaint - * @param textPaint - */ - private void setLineHeight(TextPaint textPaint) { - - Rect bounds=new Rect(); - String sampleStr="این حسین کیست که عالم همه دیوانه اوست"; - textPaint.getTextBounds(sampleStr, 0,sampleStr.length(), bounds); - - setLineHeight(bounds.height()); - - } - - /*** - * this method calculate view's height according to line count and line height and view's width - * @param lineListSize - * @param lineHeigth - * @param lineSpace - */ - public void setMeasuredDimentions(int lineListSize,int lineHeigth, int lineSpace){ - int mHeight=lineListSize*(lineHeigth+lineSpace)+lineSpace; - - mHeight+=getPaddingRight()+getPaddingLeft(); - - setMeasuredViewHeight(mHeight); - - setMeasuredViewWidth(getWidth()); - } - - - private int getTextAreaWidth() { - return textAreaWidth; - } - private void setTextAreaWidth(int textAreaWidth) { - this.textAreaWidth = textAreaWidth; - } - - private int getLineHeight() { - return lineHeight; - } - private int getMeasuredViewHeight() { - return measuredViewHeight; - } - private void setMeasuredViewHeight(int measuredViewHeight) { - this.measuredViewHeight = measuredViewHeight; - } - private int getMeasuredViewWidth() { - return measuredViewWidth; - } - private void setMeasuredViewWidth(int measuredViewWidth) { - this.measuredViewWidth = measuredViewWidth; - } - private void setLineHeight(int lineHeight) { - this.lineHeight = lineHeight; - } - - public String getText() { - return text; - } - - /*** - * Sets the string value of the JustifiedTextView. JustifiedTextView does not accept HTML-like formatting. - * Related XML Attributes - * -noghteh:text - * @param text - */ - public void setText(String text) { - this.text = text; - calculate(); - invalidate(); - } - - public void setText(int resid) { - setText(mContext.getResources().getString(resid)); - } - - public Typeface getTypeFace() { - return getTextPaint().getTypeface(); - } - public void setTypeFace(Typeface typeFace) { - getTextPaint().setTypeface(typeFace); - } - - public float getTextSize() { - return getTextPaint().getTextSize(); - } - public void setTextSize(int unit,float textSize) { - textSize=TypedValue.applyDimension(unit, textSize, mContext.getResources().getDisplayMetrics()); - setTextSize(textSize); - } - - private void setTextSize(float textSize) { - getTextPaint().setTextSize(textSize); - calculate(); - invalidate(); - } - - public TextPaint getTextPaint() { - return textPaint; - } - public void setTextPaint(TextPaint textPaint) { - this.textPaint = textPaint; - } - - /*** - * set text color - * @param textColor - */ - public void setTextColor(int textColor) { - getTextPaint().setColor(textColor); - invalidate(); - } - /*** - * define space between lines - * @param lineSpace - */ - public void setLineSpacing(int lineSpace) { - this.lineSpace = lineSpace; - invalidate(); - } - - /*** - * - * @return text color - */ - public int getTextColor() { - return getTextPaint().getColor(); - } - - - - /*** - * space between lines - default is 0 - * @return - */ - public int getLineSpace() { - return lineSpace; - } - - - /*** - * get text alignment - * @return - */ - public Align getAlignment() { - return getTextPaint().getTextAlign(); - } - /*** - * Align text according to your language - * @param align - */ - public void setAlignment(Align align) { - getTextPaint().setTextAlign(align); - invalidate(); - } - - - - - - - + if (getMeasuredViewWidth() > 0) { + requestLayout(); + setMeasuredDimension(getMeasuredViewWidth(), getMeasuredViewHeight()); + } else { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + invalidate(); + } + + + private int rowIndex = 0, colIndex = 0; + + @Override + protected void onDraw(Canvas canvas) { + + rowIndex = getPaddingTop(); + if (getAlignment() == Align.RIGHT) + colIndex = getPaddingLeft() + getTextAreaWidth(); + else + colIndex = getPaddingLeft(); + + for (int i = 0; i < lineList.size(); i++) { + rowIndex += getLineHeight() + getLineSpace(); + + canvas.drawText(lineList.get(i), colIndex, rowIndex, getTextPaint()); + } + + } + + + /*** + * this method get the string and divide it to a list of StringLines according to textAreaWidth + * @param originalText + * @return + */ + private List divideOriginalTextToStringLineList(String originalText) { + + List listStringLine = new ArrayList(); + + String line = ""; + float textWidth; + + String[] listParageraphes = originalText.split("\n"); + + for (int j = 0; j < listParageraphes.length; j++) { + String[] arrayWords = listParageraphes[j].split(" "); + + for (int i = 0; i < arrayWords.length; i++) { + + line += arrayWords[i] + " "; + textWidth = getTextPaint().measureText(line); + + //if text width is equal to textAreaWidth then just add it to ListStringLine + if (getTextAreaWidth() == textWidth) { + + listStringLine.add(line); + line = "";//make line clear + continue; + } + //else if text width excite textAreaWidth then remove last word and justify the StringLine + else if (getTextAreaWidth() < textWidth) { + + int lastWordCount = arrayWords[i].length(); + + //remove last word that cause line width to excite textAreaWidth + line = line.substring(0, line.length() - lastWordCount - 1); + + // if line is empty then should be skipped + if (line.trim().length() == 0) + continue; + + //and then we need to justify line + line = justifyTextLine(textPaint, line.trim(), getTextAreaWidth()); + + listStringLine.add(line); + line = ""; + i--; + continue; + } + + //if we are now at last line of paragraph then just add it + if (i == arrayWords.length - 1) { + listStringLine.add(line); + line = ""; + } + } + } + + return listStringLine; + + } + + /** + * this method add space in line until line width become equal to textAreaWidth + * + * @param lineString + * @param lineWidth + * @param textAreaWidth + * @return + */ + private String justifyTextLine(TextPaint textPaint, String lineString, int textAreaWidth) { + + int gapIndex = 0; + + float lineWidth = textPaint.measureText(lineString); + + while (lineWidth < textAreaWidth && lineWidth > 0) { + + gapIndex = lineString.indexOf(" ", gapIndex + 2); + if (gapIndex == -1) { + gapIndex = 0; + gapIndex = lineString.indexOf(" ", gapIndex + 1); + if (gapIndex == -1) + return lineString; + } + + lineString = lineString.substring(0, gapIndex) + " " + lineString.substring(gapIndex + 1, lineString.length()); + + lineWidth = textPaint.measureText(lineString); + } + return lineString; + } + + /*** + * this method calculate height for a line of text according to defined TextPaint + * @param textPaint + */ + private void setLineHeight(TextPaint textPaint) { + + Rect bounds = new Rect(); + String sampleStr = "این حسین کیست که عالم همه دیوانه اوست"; + textPaint.getTextBounds(sampleStr, 0, sampleStr.length(), bounds); + + setLineHeight(bounds.height()); + + } + + /*** + * this method calculate view's height according to line count and line height and view's width + * @param lineListSize + * @param lineHeigth + * @param lineSpace + */ + public void setMeasuredDimentions(int lineListSize, int lineHeigth, int lineSpace) { + int mHeight = lineListSize * (lineHeigth + lineSpace) + lineSpace; + + mHeight += getPaddingBottom() + getPaddingTop(); + + setMeasuredViewHeight(mHeight); + + setMeasuredViewWidth(getWidth()); + } + + + private int getTextAreaWidth() { + return textAreaWidth; + } + + private void setTextAreaWidth(int textAreaWidth) { + this.textAreaWidth = textAreaWidth; + } + + private int getLineHeight() { + return lineHeight; + } + + private int getMeasuredViewHeight() { + return measuredViewHeight; + } + + private void setMeasuredViewHeight(int measuredViewHeight) { + this.measuredViewHeight = measuredViewHeight; + } + + private int getMeasuredViewWidth() { + return measuredViewWidth; + } + + private void setMeasuredViewWidth(int measuredViewWidth) { + this.measuredViewWidth = measuredViewWidth; + } + + private void setLineHeight(int lineHeight) { + this.lineHeight = lineHeight; + } + + public String getText() { + return text; + } + + /*** + * Sets the string value of the JustifiedTextView. JustifiedTextView does not accept HTML-like formatting. + * Related XML Attributes + * -noghteh:text + * @param text + */ + public void setText(String text) { + this.text = text; + calculate(); + invalidate(); + } + + public void setText(int resid) { + setText(mContext.getResources().getString(resid)); + } + + public Typeface getTypeFace() { + return getTextPaint().getTypeface(); + } + + public void setTypeFace(Typeface typeFace) { + getTextPaint().setTypeface(typeFace); + } + + public float getTextSize() { + return getTextPaint().getTextSize(); + } + + public void setTextSize(int unit, float textSize) { + textSize = TypedValue.applyDimension(unit, textSize, mContext.getResources().getDisplayMetrics()); + setTextSize(textSize); + } + + private void setTextSize(float textSize) { + getTextPaint().setTextSize(textSize); + calculate(); + invalidate(); + } + + public TextPaint getTextPaint() { + return textPaint; + } + + public void setTextPaint(TextPaint textPaint) { + this.textPaint = textPaint; + } + + /*** + * set text color + * @param textColor + */ + public void setTextColor(int textColor) { + getTextPaint().setColor(textColor); + invalidate(); + } + + /*** + * define space between lines + * @param lineSpace + */ + public void setLineSpacing(int lineSpace) { + this.lineSpace = lineSpace; + invalidate(); + } + + /*** + * + * @return text color + */ + public int getTextColor() { + return getTextPaint().getColor(); + } + + + /*** + * space between lines - default is 0 + * @return + */ + public int getLineSpace() { + return lineSpace; + } + + + /*** + * get text alignment + * @return + */ + public Align getAlignment() { + return getTextPaint().getTextAlign(); + } + + /*** + * Align text according to your language + * @param align + */ + public void setAlignment(Align align) { + getTextPaint().setTextAlign(align); + invalidate(); + } + }