Repository: incubator-weex Updated Branches: refs/heads/0.16-dev 8b9845ebe -> c95fc2325
* [android] Refactor the process of lines style in text. Project: http://git-wip-us.apache.org/repos/asf/incubator-weex/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-weex/commit/86c2a7d6 Tree: http://git-wip-us.apache.org/repos/asf/incubator-weex/tree/86c2a7d6 Diff: http://git-wip-us.apache.org/repos/asf/incubator-weex/diff/86c2a7d6 Branch: refs/heads/0.16-dev Commit: 86c2a7d60e1e506b7c448fb5e83b6ecc00c5b697 Parents: 92a1616 Author: YorkShen <shenyua...@gmail.com> Authored: Fri Jul 21 14:43:25 2017 +0800 Committer: YorkShen <shenyua...@gmail.com> Committed: Fri Jul 21 14:43:25 2017 +0800 ---------------------------------------------------------------------- .../com/taobao/weex/dom/WXTextDomObject.java | 91 +++++++++++++------- 1 file changed, 60 insertions(+), 31 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/86c2a7d6/android/sdk/src/main/java/com/taobao/weex/dom/WXTextDomObject.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/dom/WXTextDomObject.java b/android/sdk/src/main/java/com/taobao/weex/dom/WXTextDomObject.java index fe06b06..06084a9 100644 --- a/android/sdk/src/main/java/com/taobao/weex/dom/WXTextDomObject.java +++ b/android/sdk/src/main/java/com/taobao/weex/dom/WXTextDomObject.java @@ -18,22 +18,26 @@ */ package com.taobao.weex.dom; +import static com.taobao.weex.dom.WXStyle.UNSET; + import android.graphics.Canvas; import android.graphics.Typeface; import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.text.Editable; import android.text.Layout; import android.text.Spannable; import android.text.SpannableString; +import android.text.SpannableStringBuilder; import android.text.Spanned; +import android.text.SpannedString; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; import android.text.style.AbsoluteSizeSpan; import android.text.style.AlignmentSpan; import android.text.style.ForegroundColorSpan; - import com.taobao.weex.WXEnvironment; import com.taobao.weex.common.Constants; import com.taobao.weex.dom.flex.CSSConstants; @@ -45,15 +49,12 @@ import com.taobao.weex.ui.component.WXTextDecoration; import com.taobao.weex.utils.WXDomUtils; import com.taobao.weex.utils.WXLogUtils; import com.taobao.weex.utils.WXResourceUtils; - import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; -import static com.taobao.weex.dom.WXStyle.UNSET; - /** * Class for calculating a given text's height and width. The calculating of width and height of * text is done by {@link Layout}. @@ -299,7 +300,7 @@ public class WXTextDomObject extends WXDomObject { Layout layout; if (!FloatUtil.floatsEqual(previousWidth, textWidth) || previousLayout == null) { layout = new StaticLayout(spanned, mTextPaint, (int) Math.ceil(textWidth), - Layout.Alignment.ALIGN_NORMAL, 1, 0, false); + Layout.Alignment.ALIGN_NORMAL, 1, 0, false); } else { layout = previousLayout; } @@ -308,37 +309,65 @@ public class WXTextDomObject extends WXDomObject { lastLineStart = layout.getLineStart(mNumberOfLines - 1); lastLineEnd = layout.getLineEnd(mNumberOfLines - 1); if (lastLineStart < lastLineEnd) { - String text = mText.subSequence(0, lastLineStart).toString() + - truncate(mText.substring(lastLineStart, lastLineEnd), - mTextPaint, layout.getWidth(), textOverflow); - spanned = createSpanned(text); + SpannableStringBuilder builder = new SpannableStringBuilder(spanned.subSequence(0, lastLineStart)); + Editable lastLine = new SpannableStringBuilder(spanned.subSequence(lastLineStart, lastLineEnd)); + builder.append(truncate(lastLine, mTextPaint, layout.getWidth(), textOverflow)); + adjustSpansRange(spanned, builder); + spanned = builder; return new StaticLayout(spanned, mTextPaint, (int) Math.ceil(textWidth), - Layout.Alignment.ALIGN_NORMAL, 1, 0, false); + Layout.Alignment.ALIGN_NORMAL, 1, 0, false); } } return layout; } - public @NonNull String truncate(@Nullable String source, @NonNull TextPaint paint, - int desired, @Nullable TextUtils.TruncateAt truncateAt){ - if(!TextUtils.isEmpty(source)){ - StringBuilder builder; - Spanned spanned; + /** + * Truncate the source span to the specified lines. + * Caller of this method must ensure that the lines of text is <strong>greater than desired lines and need truncate</strong>. + * Otherwise, unexpected behavior may happen. + * @param source The source span. + * @param paint the textPaint + * @param desired specified lines. + * @param truncateAt truncate method, null value means clipping overflow text directly, non-null value means using ellipsis strategy to clip + * @return The spans after clipped. + */ + private + @NonNull + Spanned truncate(@Nullable Editable source, @NonNull TextPaint paint, + int desired, @Nullable TextUtils.TruncateAt truncateAt) { + Spanned ret = new SpannedString(""); + if (!TextUtils.isEmpty(source) && source.length() > 0) { StaticLayout layout; - for(int i=source.length();i>0;i--){ - builder=new StringBuilder(i+1); - builder.append(source, 0, i); - if(truncateAt!=null){ - builder.append(ELLIPSIS); - } - spanned = createSpanned(builder.toString()); - layout = new StaticLayout(spanned, paint, desired, Layout.Alignment.ALIGN_NORMAL, 1, 0, true); - if(layout.getLineCount()<=1){ - return spanned.toString(); + if (truncateAt != null) { + source.append(ELLIPSIS); + } + while (source.length() > 1) { + source.delete(source.length() - 2, source.length() - 1); + layout = new StaticLayout(source, paint, desired, Layout.Alignment.ALIGN_NORMAL, 1, 0, true); + if (layout.getLineCount() <= 1) { + ret = source; + break; } } } - return ""; + return ret; + } + + /** + * Adjust span range after truncate due to the wrong span range during span copy and slicing. + * @param beforeTruncate The span before truncate + * @param afterTruncate The span after truncate + */ + private void adjustSpansRange(@NonNull Spanned beforeTruncate, @NonNull Spannable afterTruncate){ + Object[] spans = beforeTruncate.getSpans(0, beforeTruncate.length(), Object.class); + for(Object span:spans){ + int start = beforeTruncate.getSpanStart(span); + int end = beforeTruncate.getSpanEnd(span); + if(start == 0 && end == beforeTruncate.length()){ + afterTruncate.removeSpan(span); + afterTruncate.setSpan(span, 0, afterTruncate.length(), beforeTruncate.getSpanFlags(span)); + } + } } /** @@ -350,7 +379,7 @@ public class WXTextDomObject extends WXDomObject { * @return if forceToDesired is false, it will be the minimum value of the width of text and * outerWidth in case of outerWidth is defined, in other case, it will be outer width. */ - /** package **/ float getTextWidth(TextPaint textPaint,float outerWidth, boolean forceToDesired) { + float getTextWidth(TextPaint textPaint,float outerWidth, boolean forceToDesired) { float textWidth; if (forceToDesired) { textWidth = outerWidth; @@ -385,7 +414,7 @@ public class WXTextDomObject extends WXDomObject { List<SetSpanOperation> ops = createSetSpanOperation(spannable.length(), spanFlag); if (mFontSize == UNSET) { ops.add(new SetSpanOperation(0, spannable.length(), - new AbsoluteSizeSpan(WXText.sDEFAULT_SIZE), spanFlag)); + new AbsoluteSizeSpan(WXText.sDEFAULT_SIZE), spanFlag)); } Collections.reverse(ops); for (SetSpanOperation op : ops) { @@ -408,7 +437,7 @@ public class WXTextDomObject extends WXDomObject { } if (mIsColorSet) { ops.add(new SetSpanOperation(start, end, - new ForegroundColorSpan(mColor), spanFlag)); + new ForegroundColorSpan(mColor), spanFlag)); } if (mFontSize != UNSET) { ops.add(new SetSpanOperation(start, end, new AbsoluteSizeSpan(mFontSize), spanFlag)); @@ -417,8 +446,8 @@ public class WXTextDomObject extends WXDomObject { || mFontWeight != UNSET || mFontFamily != null) { ops.add(new SetSpanOperation(start, end, - new WXCustomStyleSpan(mFontStyle, mFontWeight, mFontFamily), - spanFlag)); + new WXCustomStyleSpan(mFontStyle, mFontWeight, mFontFamily), + spanFlag)); } ops.add(new SetSpanOperation(start, end, new AlignmentSpan.Standard(mAlignment), spanFlag)); if (mLineHeight != UNSET) {