Search in sources :

Example 16 with LeadingMarginSpan2

use of android.text.style.LeadingMarginSpan.LeadingMarginSpan2 in project android_frameworks_base by AOSPA.

the class Layout method drawText.

/**
     * @hide
     */
public void drawText(Canvas canvas, int firstLine, int lastLine) {
    int previousLineBottom = getLineTop(firstLine);
    int previousLineEnd = getLineStart(firstLine);
    ParagraphStyle[] spans = NO_PARA_SPANS;
    int spanEnd = 0;
    TextPaint paint = mPaint;
    CharSequence buf = mText;
    Alignment paraAlign = mAlignment;
    TabStops tabStops = null;
    boolean tabStopsIsInitialized = false;
    TextLine tl = TextLine.obtain();
    // The baseline is the top of the following line minus the current line's descent.
    for (int lineNum = firstLine; lineNum <= lastLine; lineNum++) {
        int start = previousLineEnd;
        previousLineEnd = getLineStart(lineNum + 1);
        int end = getLineVisibleEnd(lineNum, start, previousLineEnd);
        int ltop = previousLineBottom;
        int lbottom = getLineTop(lineNum + 1);
        previousLineBottom = lbottom;
        int lbaseline = lbottom - getLineDescent(lineNum);
        int dir = getParagraphDirection(lineNum);
        int left = 0;
        int right = mWidth;
        if (mSpannedText) {
            Spanned sp = (Spanned) buf;
            int textLength = buf.length();
            boolean isFirstParaLine = (start == 0 || buf.charAt(start - 1) == '\n');
            // our problem.
            if (start >= spanEnd && (lineNum == firstLine || isFirstParaLine)) {
                spanEnd = sp.nextSpanTransition(start, textLength, ParagraphStyle.class);
                spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class);
                paraAlign = mAlignment;
                for (int n = spans.length - 1; n >= 0; n--) {
                    if (spans[n] instanceof AlignmentSpan) {
                        paraAlign = ((AlignmentSpan) spans[n]).getAlignment();
                        break;
                    }
                }
                tabStopsIsInitialized = false;
            }
            // Draw all leading margin spans.  Adjust left or right according
            // to the paragraph direction of the line.
            final int length = spans.length;
            boolean useFirstLineMargin = isFirstParaLine;
            for (int n = 0; n < length; n++) {
                if (spans[n] instanceof LeadingMarginSpan2) {
                    int count = ((LeadingMarginSpan2) spans[n]).getLeadingMarginLineCount();
                    int startLine = getLineForOffset(sp.getSpanStart(spans[n]));
                    // the count that is greatest
                    if (lineNum < startLine + count) {
                        useFirstLineMargin = true;
                        break;
                    }
                }
            }
            for (int n = 0; n < length; n++) {
                if (spans[n] instanceof LeadingMarginSpan) {
                    LeadingMarginSpan margin = (LeadingMarginSpan) spans[n];
                    if (dir == DIR_RIGHT_TO_LEFT) {
                        margin.drawLeadingMargin(canvas, paint, right, dir, ltop, lbaseline, lbottom, buf, start, end, isFirstParaLine, this);
                        right -= margin.getLeadingMargin(useFirstLineMargin);
                    } else {
                        margin.drawLeadingMargin(canvas, paint, left, dir, ltop, lbaseline, lbottom, buf, start, end, isFirstParaLine, this);
                        left += margin.getLeadingMargin(useFirstLineMargin);
                    }
                }
            }
        }
        boolean hasTab = getLineContainsTab(lineNum);
        // Can't tell if we have tabs for sure, currently
        if (hasTab && !tabStopsIsInitialized) {
            if (tabStops == null) {
                tabStops = new TabStops(TAB_INCREMENT, spans);
            } else {
                tabStops.reset(TAB_INCREMENT, spans);
            }
            tabStopsIsInitialized = true;
        }
        // Determine whether the line aligns to normal, opposite, or center.
        Alignment align = paraAlign;
        if (align == Alignment.ALIGN_LEFT) {
            align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE;
        } else if (align == Alignment.ALIGN_RIGHT) {
            align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL;
        }
        int x;
        if (align == Alignment.ALIGN_NORMAL) {
            if (dir == DIR_LEFT_TO_RIGHT) {
                x = left + getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
            } else {
                x = right + getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
            }
        } else {
            int max = (int) getLineExtent(lineNum, tabStops, false);
            if (align == Alignment.ALIGN_OPPOSITE) {
                if (dir == DIR_LEFT_TO_RIGHT) {
                    x = right - max + getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
                } else {
                    x = left - max + getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
                }
            } else {
                // Alignment.ALIGN_CENTER
                max = max & ~1;
                x = ((right + left - max) >> 1) + getIndentAdjust(lineNum, Alignment.ALIGN_CENTER);
            }
        }
        paint.setHyphenEdit(getHyphen(lineNum));
        Directions directions = getLineDirections(lineNum);
        if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTab) {
            // XXX: assumes there's nothing additional to be done
            canvas.drawText(buf, start, end, x, lbaseline, paint);
        } else {
            tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops);
            tl.draw(canvas, x, ltop, lbaseline, lbottom);
        }
        paint.setHyphenEdit(0);
    }
    TextLine.recycle(tl);
}
Also used : AlignmentSpan(android.text.style.AlignmentSpan) ParagraphStyle(android.text.style.ParagraphStyle) Paint(android.graphics.Paint) LeadingMarginSpan2(android.text.style.LeadingMarginSpan.LeadingMarginSpan2) LeadingMarginSpan(android.text.style.LeadingMarginSpan)

Example 17 with LeadingMarginSpan2

use of android.text.style.LeadingMarginSpan.LeadingMarginSpan2 in project android_frameworks_base by ResurrectionRemix.

the class Layout method getParagraphLeadingMargin.

/**
     * Returns the effective leading margin (unsigned) for this line,
     * taking into account LeadingMarginSpan and LeadingMarginSpan2.
     * @param line the line index
     * @return the leading margin of this line
     */
private int getParagraphLeadingMargin(int line) {
    if (!mSpannedText) {
        return 0;
    }
    Spanned spanned = (Spanned) mText;
    int lineStart = getLineStart(line);
    int lineEnd = getLineEnd(line);
    int spanEnd = spanned.nextSpanTransition(lineStart, lineEnd, LeadingMarginSpan.class);
    LeadingMarginSpan[] spans = getParagraphSpans(spanned, lineStart, spanEnd, LeadingMarginSpan.class);
    if (spans.length == 0) {
        // no leading margin span;
        return 0;
    }
    int margin = 0;
    boolean isFirstParaLine = lineStart == 0 || spanned.charAt(lineStart - 1) == '\n';
    boolean useFirstLineMargin = isFirstParaLine;
    for (int i = 0; i < spans.length; i++) {
        if (spans[i] instanceof LeadingMarginSpan2) {
            int spStart = spanned.getSpanStart(spans[i]);
            int spanLine = getLineForOffset(spStart);
            int count = ((LeadingMarginSpan2) spans[i]).getLeadingMarginLineCount();
            // if there is more than one LeadingMarginSpan2, use the count that is greatest
            useFirstLineMargin |= line < spanLine + count;
        }
    }
    for (int i = 0; i < spans.length; i++) {
        LeadingMarginSpan span = spans[i];
        margin += span.getLeadingMargin(useFirstLineMargin);
    }
    return margin;
}
Also used : LeadingMarginSpan2(android.text.style.LeadingMarginSpan.LeadingMarginSpan2) LeadingMarginSpan(android.text.style.LeadingMarginSpan) Paint(android.graphics.Paint)

Example 18 with LeadingMarginSpan2

use of android.text.style.LeadingMarginSpan.LeadingMarginSpan2 in project android_frameworks_base by DirtyUnicorns.

the class Layout method getParagraphLeadingMargin.

/**
     * Returns the effective leading margin (unsigned) for this line,
     * taking into account LeadingMarginSpan and LeadingMarginSpan2.
     * @param line the line index
     * @return the leading margin of this line
     */
private int getParagraphLeadingMargin(int line) {
    if (!mSpannedText) {
        return 0;
    }
    Spanned spanned = (Spanned) mText;
    int lineStart = getLineStart(line);
    int lineEnd = getLineEnd(line);
    int spanEnd = spanned.nextSpanTransition(lineStart, lineEnd, LeadingMarginSpan.class);
    LeadingMarginSpan[] spans = getParagraphSpans(spanned, lineStart, spanEnd, LeadingMarginSpan.class);
    if (spans.length == 0) {
        // no leading margin span;
        return 0;
    }
    int margin = 0;
    boolean isFirstParaLine = lineStart == 0 || spanned.charAt(lineStart - 1) == '\n';
    boolean useFirstLineMargin = isFirstParaLine;
    for (int i = 0; i < spans.length; i++) {
        if (spans[i] instanceof LeadingMarginSpan2) {
            int spStart = spanned.getSpanStart(spans[i]);
            int spanLine = getLineForOffset(spStart);
            int count = ((LeadingMarginSpan2) spans[i]).getLeadingMarginLineCount();
            // if there is more than one LeadingMarginSpan2, use the count that is greatest
            useFirstLineMargin |= line < spanLine + count;
        }
    }
    for (int i = 0; i < spans.length; i++) {
        LeadingMarginSpan span = spans[i];
        margin += span.getLeadingMargin(useFirstLineMargin);
    }
    return margin;
}
Also used : LeadingMarginSpan2(android.text.style.LeadingMarginSpan.LeadingMarginSpan2) LeadingMarginSpan(android.text.style.LeadingMarginSpan) Paint(android.graphics.Paint)

Example 19 with LeadingMarginSpan2

use of android.text.style.LeadingMarginSpan.LeadingMarginSpan2 in project android_frameworks_base by DirtyUnicorns.

the class StaticLayout method generate.

/* package */
void generate(Builder b, boolean includepad, boolean trackpad) {
    CharSequence source = b.mText;
    int bufStart = b.mStart;
    int bufEnd = b.mEnd;
    TextPaint paint = b.mPaint;
    int outerWidth = b.mWidth;
    TextDirectionHeuristic textDir = b.mTextDir;
    float spacingmult = b.mSpacingMult;
    float spacingadd = b.mSpacingAdd;
    float ellipsizedWidth = b.mEllipsizedWidth;
    TextUtils.TruncateAt ellipsize = b.mEllipsize;
    // TODO: move to builder to avoid allocation costs
    LineBreaks lineBreaks = new LineBreaks();
    // store span end locations
    int[] spanEndCache = new int[4];
    // store fontMetrics per span range
    // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range)
    int[] fmCache = new int[4 * 4];
    // TODO: also respect LocaleSpan within the text
    b.setLocale(paint.getTextLocale());
    mLineCount = 0;
    int v = 0;
    boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
    Paint.FontMetricsInt fm = b.mFontMetricsInt;
    int[] chooseHtv = null;
    MeasuredText measured = b.mMeasuredText;
    Spanned spanned = null;
    if (source instanceof Spanned)
        spanned = (Spanned) source;
    int paraEnd;
    for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
        paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd);
        if (paraEnd < 0)
            paraEnd = bufEnd;
        else
            paraEnd++;
        int firstWidthLineCount = 1;
        int firstWidth = outerWidth;
        int restWidth = outerWidth;
        LineHeightSpan[] chooseHt = null;
        if (spanned != null) {
            LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd, LeadingMarginSpan.class);
            for (int i = 0; i < sp.length; i++) {
                LeadingMarginSpan lms = sp[i];
                firstWidth -= sp[i].getLeadingMargin(true);
                restWidth -= sp[i].getLeadingMargin(false);
                // leading margin spans, not just this particular one
                if (lms instanceof LeadingMarginSpan2) {
                    LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
                    firstWidthLineCount = Math.max(firstWidthLineCount, lms2.getLeadingMarginLineCount());
                }
            }
            chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
            if (chooseHt.length == 0) {
                // So that out() would not assume it has any contents
                chooseHt = null;
            } else {
                if (chooseHtv == null || chooseHtv.length < chooseHt.length) {
                    chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
                }
                for (int i = 0; i < chooseHt.length; i++) {
                    int o = spanned.getSpanStart(chooseHt[i]);
                    if (o < paraStart) {
                        // starts in this layout, before the
                        // current paragraph
                        chooseHtv[i] = getLineTop(getLineForOffset(o));
                    } else {
                        // starts in this paragraph
                        chooseHtv[i] = v;
                    }
                }
            }
        }
        measured.setPara(source, paraStart, paraEnd, textDir, b);
        char[] chs = measured.mChars;
        float[] widths = measured.mWidths;
        byte[] chdirs = measured.mLevels;
        int dir = measured.mDir;
        boolean easy = measured.mEasy;
        // tab stop locations
        int[] variableTabStops = null;
        if (spanned != null) {
            TabStopSpan[] spans = getParagraphSpans(spanned, paraStart, paraEnd, TabStopSpan.class);
            if (spans.length > 0) {
                int[] stops = new int[spans.length];
                for (int i = 0; i < spans.length; i++) {
                    stops[i] = spans[i].getTabStop();
                }
                Arrays.sort(stops, 0, stops.length);
                variableTabStops = stops;
            }
        }
        nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart, firstWidth, firstWidthLineCount, restWidth, variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency);
        if (mLeftIndents != null || mRightIndents != null) {
            // TODO(raph) performance: it would be better to do this once per layout rather
            // than once per paragraph, but that would require a change to the native
            // interface.
            int leftLen = mLeftIndents == null ? 0 : mLeftIndents.length;
            int rightLen = mRightIndents == null ? 0 : mRightIndents.length;
            int indentsLen = Math.max(1, Math.max(leftLen, rightLen) - mLineCount);
            int[] indents = new int[indentsLen];
            for (int i = 0; i < indentsLen; i++) {
                int leftMargin = mLeftIndents == null ? 0 : mLeftIndents[Math.min(i + mLineCount, leftLen - 1)];
                int rightMargin = mRightIndents == null ? 0 : mRightIndents[Math.min(i + mLineCount, rightLen - 1)];
                indents[i] = leftMargin + rightMargin;
            }
            nSetIndents(b.mNativePtr, indents);
        }
        // measurement has to be done before performing line breaking
        // but we don't want to recompute fontmetrics or span ranges the
        // second time, so we cache those and then use those stored values
        int fmCacheCount = 0;
        int spanEndCacheCount = 0;
        for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
            if (fmCacheCount * 4 >= fmCache.length) {
                int[] grow = new int[fmCacheCount * 4 * 2];
                System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4);
                fmCache = grow;
            }
            if (spanEndCacheCount >= spanEndCache.length) {
                int[] grow = new int[spanEndCacheCount * 2];
                System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount);
                spanEndCache = grow;
            }
            if (spanned == null) {
                spanEnd = paraEnd;
                int spanLen = spanEnd - spanStart;
                measured.addStyleRun(paint, spanLen, fm);
            } else {
                spanEnd = spanned.nextSpanTransition(spanStart, paraEnd, MetricAffectingSpan.class);
                int spanLen = spanEnd - spanStart;
                MetricAffectingSpan[] spans = spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
                spans = TextUtils.removeEmptySpans(spans, spanned, MetricAffectingSpan.class);
                measured.addStyleRun(paint, spans, spanLen, fm);
            }
            // the order of storage here (top, bottom, ascent, descent) has to match the code below
            // where these values are retrieved
            fmCache[fmCacheCount * 4 + 0] = fm.top;
            fmCache[fmCacheCount * 4 + 1] = fm.bottom;
            fmCache[fmCacheCount * 4 + 2] = fm.ascent;
            fmCache[fmCacheCount * 4 + 3] = fm.descent;
            fmCacheCount++;
            spanEndCache[spanEndCacheCount] = spanEnd;
            spanEndCacheCount++;
        }
        nGetWidths(b.mNativePtr, widths);
        int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks, lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length);
        int[] breaks = lineBreaks.breaks;
        float[] lineWidths = lineBreaks.widths;
        int[] flags = lineBreaks.flags;
        final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
        final boolean ellipsisMayBeApplied = ellipsize != null && (ellipsize == TextUtils.TruncateAt.END || (mMaximumVisibleLineCount == 1 && ellipsize != TextUtils.TruncateAt.MARQUEE));
        if (remainingLineCount > 0 && remainingLineCount < breakCount && ellipsisMayBeApplied) {
            // Calculate width and flag.
            float width = 0;
            int flag = 0;
            for (int i = remainingLineCount - 1; i < breakCount; i++) {
                if (i == breakCount - 1) {
                    width += lineWidths[i];
                } else {
                    for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
                        width += widths[j];
                    }
                }
                flag |= flags[i] & TAB_MASK;
            }
            // Treat the last line and overflowed lines as a single line.
            breaks[remainingLineCount - 1] = breaks[breakCount - 1];
            lineWidths[remainingLineCount - 1] = width;
            flags[remainingLineCount - 1] = flag;
            breakCount = remainingLineCount;
        }
        // here is the offset of the starting character of the line we are currently measuring
        int here = paraStart;
        int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
        int fmCacheIndex = 0;
        int spanEndCacheIndex = 0;
        int breakIndex = 0;
        for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
            // retrieve end of span
            spanEnd = spanEndCache[spanEndCacheIndex++];
            // retrieve cached metrics, order matches above
            fm.top = fmCache[fmCacheIndex * 4 + 0];
            fm.bottom = fmCache[fmCacheIndex * 4 + 1];
            fm.ascent = fmCache[fmCacheIndex * 4 + 2];
            fm.descent = fmCache[fmCacheIndex * 4 + 3];
            fmCacheIndex++;
            if (fm.top < fmTop) {
                fmTop = fm.top;
            }
            if (fm.ascent < fmAscent) {
                fmAscent = fm.ascent;
            }
            if (fm.descent > fmDescent) {
                fmDescent = fm.descent;
            }
            if (fm.bottom > fmBottom) {
                fmBottom = fm.bottom;
            }
            // skip breaks ending before current span range
            while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
                breakIndex++;
            }
            while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
                int endPos = paraStart + breaks[breakIndex];
                boolean moreChars = (endPos < bufEnd);
                v = out(source, here, endPos, fmAscent, fmDescent, fmTop, fmBottom, v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, flags[breakIndex], needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad, chs, widths, paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint, moreChars);
                if (endPos < spanEnd) {
                    // preserve metrics for current span
                    fmTop = fm.top;
                    fmBottom = fm.bottom;
                    fmAscent = fm.ascent;
                    fmDescent = fm.descent;
                } else {
                    fmTop = fmBottom = fmAscent = fmDescent = 0;
                }
                here = endPos;
                breakIndex++;
                if (mLineCount >= mMaximumVisibleLineCount) {
                    return;
                }
            }
        }
        if (paraEnd == bufEnd)
            break;
    }
    if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) && mLineCount < mMaximumVisibleLineCount) {
        // Log.e("text", "output last " + bufEnd);
        measured.setPara(source, bufEnd, bufEnd, textDir, b);
        paint.getFontMetricsInt(fm);
        v = out(source, bufEnd, bufEnd, fm.ascent, fm.descent, fm.top, fm.bottom, v, spacingmult, spacingadd, null, null, fm, 0, needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd, includepad, trackpad, null, null, bufStart, ellipsize, ellipsizedWidth, 0, paint, false);
    }
}
Also used : LineHeightSpan(android.text.style.LineHeightSpan) TabStopSpan(android.text.style.TabStopSpan) Paint(android.graphics.Paint) Paint(android.graphics.Paint) LeadingMarginSpan2(android.text.style.LeadingMarginSpan.LeadingMarginSpan2) LeadingMarginSpan(android.text.style.LeadingMarginSpan) MetricAffectingSpan(android.text.style.MetricAffectingSpan)

Example 20 with LeadingMarginSpan2

use of android.text.style.LeadingMarginSpan.LeadingMarginSpan2 in project android_frameworks_base by crdroidandroid.

the class StaticLayout method generate.

/* package */
void generate(Builder b, boolean includepad, boolean trackpad) {
    CharSequence source = b.mText;
    int bufStart = b.mStart;
    int bufEnd = b.mEnd;
    TextPaint paint = b.mPaint;
    int outerWidth = b.mWidth;
    TextDirectionHeuristic textDir = b.mTextDir;
    float spacingmult = b.mSpacingMult;
    float spacingadd = b.mSpacingAdd;
    float ellipsizedWidth = b.mEllipsizedWidth;
    TextUtils.TruncateAt ellipsize = b.mEllipsize;
    // TODO: move to builder to avoid allocation costs
    LineBreaks lineBreaks = new LineBreaks();
    // store span end locations
    int[] spanEndCache = new int[4];
    // store fontMetrics per span range
    // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range)
    int[] fmCache = new int[4 * 4];
    // TODO: also respect LocaleSpan within the text
    b.setLocale(paint.getTextLocale());
    mLineCount = 0;
    int v = 0;
    boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
    Paint.FontMetricsInt fm = b.mFontMetricsInt;
    int[] chooseHtv = null;
    MeasuredText measured = b.mMeasuredText;
    Spanned spanned = null;
    if (source instanceof Spanned)
        spanned = (Spanned) source;
    int paraEnd;
    for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
        paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd);
        if (paraEnd < 0)
            paraEnd = bufEnd;
        else
            paraEnd++;
        int firstWidthLineCount = 1;
        int firstWidth = outerWidth;
        int restWidth = outerWidth;
        LineHeightSpan[] chooseHt = null;
        if (spanned != null) {
            LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd, LeadingMarginSpan.class);
            for (int i = 0; i < sp.length; i++) {
                LeadingMarginSpan lms = sp[i];
                firstWidth -= sp[i].getLeadingMargin(true);
                restWidth -= sp[i].getLeadingMargin(false);
                // leading margin spans, not just this particular one
                if (lms instanceof LeadingMarginSpan2) {
                    LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
                    firstWidthLineCount = Math.max(firstWidthLineCount, lms2.getLeadingMarginLineCount());
                }
            }
            chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
            if (chooseHt.length == 0) {
                // So that out() would not assume it has any contents
                chooseHt = null;
            } else {
                if (chooseHtv == null || chooseHtv.length < chooseHt.length) {
                    chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
                }
                for (int i = 0; i < chooseHt.length; i++) {
                    int o = spanned.getSpanStart(chooseHt[i]);
                    if (o < paraStart) {
                        // starts in this layout, before the
                        // current paragraph
                        chooseHtv[i] = getLineTop(getLineForOffset(o));
                    } else {
                        // starts in this paragraph
                        chooseHtv[i] = v;
                    }
                }
            }
        }
        measured.setPara(source, paraStart, paraEnd, textDir, b);
        char[] chs = measured.mChars;
        float[] widths = measured.mWidths;
        byte[] chdirs = measured.mLevels;
        int dir = measured.mDir;
        boolean easy = measured.mEasy;
        // tab stop locations
        int[] variableTabStops = null;
        if (spanned != null) {
            TabStopSpan[] spans = getParagraphSpans(spanned, paraStart, paraEnd, TabStopSpan.class);
            if (spans.length > 0) {
                int[] stops = new int[spans.length];
                for (int i = 0; i < spans.length; i++) {
                    stops[i] = spans[i].getTabStop();
                }
                Arrays.sort(stops, 0, stops.length);
                variableTabStops = stops;
            }
        }
        nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart, firstWidth, firstWidthLineCount, restWidth, variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency);
        if (mLeftIndents != null || mRightIndents != null) {
            // TODO(raph) performance: it would be better to do this once per layout rather
            // than once per paragraph, but that would require a change to the native
            // interface.
            int leftLen = mLeftIndents == null ? 0 : mLeftIndents.length;
            int rightLen = mRightIndents == null ? 0 : mRightIndents.length;
            int indentsLen = Math.max(1, Math.max(leftLen, rightLen) - mLineCount);
            int[] indents = new int[indentsLen];
            for (int i = 0; i < indentsLen; i++) {
                int leftMargin = mLeftIndents == null ? 0 : mLeftIndents[Math.min(i + mLineCount, leftLen - 1)];
                int rightMargin = mRightIndents == null ? 0 : mRightIndents[Math.min(i + mLineCount, rightLen - 1)];
                indents[i] = leftMargin + rightMargin;
            }
            nSetIndents(b.mNativePtr, indents);
        }
        // measurement has to be done before performing line breaking
        // but we don't want to recompute fontmetrics or span ranges the
        // second time, so we cache those and then use those stored values
        int fmCacheCount = 0;
        int spanEndCacheCount = 0;
        for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
            if (fmCacheCount * 4 >= fmCache.length) {
                int[] grow = new int[fmCacheCount * 4 * 2];
                System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4);
                fmCache = grow;
            }
            if (spanEndCacheCount >= spanEndCache.length) {
                int[] grow = new int[spanEndCacheCount * 2];
                System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount);
                spanEndCache = grow;
            }
            if (spanned == null) {
                spanEnd = paraEnd;
                int spanLen = spanEnd - spanStart;
                measured.addStyleRun(paint, spanLen, fm);
            } else {
                spanEnd = spanned.nextSpanTransition(spanStart, paraEnd, MetricAffectingSpan.class);
                int spanLen = spanEnd - spanStart;
                MetricAffectingSpan[] spans = spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
                spans = TextUtils.removeEmptySpans(spans, spanned, MetricAffectingSpan.class);
                measured.addStyleRun(paint, spans, spanLen, fm);
            }
            // the order of storage here (top, bottom, ascent, descent) has to match the code below
            // where these values are retrieved
            fmCache[fmCacheCount * 4 + 0] = fm.top;
            fmCache[fmCacheCount * 4 + 1] = fm.bottom;
            fmCache[fmCacheCount * 4 + 2] = fm.ascent;
            fmCache[fmCacheCount * 4 + 3] = fm.descent;
            fmCacheCount++;
            spanEndCache[spanEndCacheCount] = spanEnd;
            spanEndCacheCount++;
        }
        nGetWidths(b.mNativePtr, widths);
        int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks, lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length);
        int[] breaks = lineBreaks.breaks;
        float[] lineWidths = lineBreaks.widths;
        int[] flags = lineBreaks.flags;
        final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
        final boolean ellipsisMayBeApplied = ellipsize != null && (ellipsize == TextUtils.TruncateAt.END || (mMaximumVisibleLineCount == 1 && ellipsize != TextUtils.TruncateAt.MARQUEE));
        if (remainingLineCount > 0 && remainingLineCount < breakCount && ellipsisMayBeApplied) {
            // Calculate width and flag.
            float width = 0;
            int flag = 0;
            for (int i = remainingLineCount - 1; i < breakCount; i++) {
                if (i == breakCount - 1) {
                    width += lineWidths[i];
                } else {
                    for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
                        width += widths[j];
                    }
                }
                flag |= flags[i] & TAB_MASK;
            }
            // Treat the last line and overflowed lines as a single line.
            breaks[remainingLineCount - 1] = breaks[breakCount - 1];
            lineWidths[remainingLineCount - 1] = width;
            flags[remainingLineCount - 1] = flag;
            breakCount = remainingLineCount;
        }
        // here is the offset of the starting character of the line we are currently measuring
        int here = paraStart;
        int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
        int fmCacheIndex = 0;
        int spanEndCacheIndex = 0;
        int breakIndex = 0;
        for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
            // retrieve end of span
            spanEnd = spanEndCache[spanEndCacheIndex++];
            // retrieve cached metrics, order matches above
            fm.top = fmCache[fmCacheIndex * 4 + 0];
            fm.bottom = fmCache[fmCacheIndex * 4 + 1];
            fm.ascent = fmCache[fmCacheIndex * 4 + 2];
            fm.descent = fmCache[fmCacheIndex * 4 + 3];
            fmCacheIndex++;
            if (fm.top < fmTop) {
                fmTop = fm.top;
            }
            if (fm.ascent < fmAscent) {
                fmAscent = fm.ascent;
            }
            if (fm.descent > fmDescent) {
                fmDescent = fm.descent;
            }
            if (fm.bottom > fmBottom) {
                fmBottom = fm.bottom;
            }
            // skip breaks ending before current span range
            while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
                breakIndex++;
            }
            while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
                int endPos = paraStart + breaks[breakIndex];
                boolean moreChars = (endPos < bufEnd);
                v = out(source, here, endPos, fmAscent, fmDescent, fmTop, fmBottom, v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, flags[breakIndex], needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad, chs, widths, paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint, moreChars);
                if (endPos < spanEnd) {
                    // preserve metrics for current span
                    fmTop = fm.top;
                    fmBottom = fm.bottom;
                    fmAscent = fm.ascent;
                    fmDescent = fm.descent;
                } else {
                    fmTop = fmBottom = fmAscent = fmDescent = 0;
                }
                here = endPos;
                breakIndex++;
                if (mLineCount >= mMaximumVisibleLineCount) {
                    return;
                }
            }
        }
        if (paraEnd == bufEnd)
            break;
    }
    if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) && mLineCount < mMaximumVisibleLineCount) {
        // Log.e("text", "output last " + bufEnd);
        measured.setPara(source, bufEnd, bufEnd, textDir, b);
        paint.getFontMetricsInt(fm);
        v = out(source, bufEnd, bufEnd, fm.ascent, fm.descent, fm.top, fm.bottom, v, spacingmult, spacingadd, null, null, fm, 0, needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd, includepad, trackpad, null, null, bufStart, ellipsize, ellipsizedWidth, 0, paint, false);
    }
}
Also used : LineHeightSpan(android.text.style.LineHeightSpan) TabStopSpan(android.text.style.TabStopSpan) Paint(android.graphics.Paint) Paint(android.graphics.Paint) LeadingMarginSpan2(android.text.style.LeadingMarginSpan.LeadingMarginSpan2) LeadingMarginSpan(android.text.style.LeadingMarginSpan) MetricAffectingSpan(android.text.style.MetricAffectingSpan)

Aggregations

Paint (android.graphics.Paint)21 LeadingMarginSpan (android.text.style.LeadingMarginSpan)21 LeadingMarginSpan2 (android.text.style.LeadingMarginSpan.LeadingMarginSpan2)21 AlignmentSpan (android.text.style.AlignmentSpan)7 LineHeightSpan (android.text.style.LineHeightSpan)7 MetricAffectingSpan (android.text.style.MetricAffectingSpan)7 ParagraphStyle (android.text.style.ParagraphStyle)7 TabStopSpan (android.text.style.TabStopSpan)7 Bitmap (android.graphics.Bitmap)2 LineBackgroundSpan (android.text.style.LineBackgroundSpan)1