private Layout makeNewLayout(int i, BoringLayout.Metrics metrics, int i2, boolean z) { int i3; boolean z2; if (i < 0) { i3 = 0; } else { i3 = i; } this.mOldMaxLines = this.mMaxLines; if (this.mEllipsize != null) { z2 = true; } else { z2 = false; } return makeSingleLayout(i3, metrics, i2, this.mLayoutAlignment, z2, this.mEllipsize, z); }
private Layout makeSingleLayout(int i, BoringLayout.Metrics metrics, int i2, Alignment alignment, boolean z, TextUtils.TruncateAt truncateAt, boolean z2) { BoringLayout.Metrics isBoring; if (metrics == UNKNOWN_BORING) { isBoring = BoringLayout.isBoring(this.mText, this.mPaint, this.mBoring); if (isBoring != null) { this.mBoring = isBoring; } } else { isBoring = metrics; } if (isBoring != null) { if (isBoring.width <= i && (truncateAt == null || isBoring.width <= i2)) { return BoringLayout.make(this.mText, this.mPaint, i, alignment, this.mLineSpacingMult, this.mLineSpacingAdd, isBoring, this.mIncludeFontPadding); } else if (z && isBoring.width <= i) { return BoringLayout.make(this.mText, this.mPaint, i, alignment, this.mLineSpacingMult, this.mLineSpacingAdd, isBoring, this.mIncludeFontPadding, truncateAt, i2); } else if (z) { return StaticLayoutWithMaxLines.create(this.mText, 0, this.mText.length(), this.mPaint, i, alignment, this.mLineSpacingMult, this.mLineSpacingAdd, this.mIncludeFontPadding, truncateAt, i2, this.mMaxLines); } else { return new StaticLayout(this.mText, this.mPaint, i, alignment, this.mLineSpacingMult, this.mLineSpacingAdd, this.mIncludeFontPadding); } } else if (z) { return StaticLayoutWithMaxLines.create(this.mText, 0, this.mText.length(), this.mPaint, i, alignment, this.mLineSpacingMult, this.mLineSpacingAdd, this.mIncludeFontPadding, truncateAt, i2, this.mMaxLines); } else { return new StaticLayout(this.mText, this.mPaint, i, alignment, this.mLineSpacingMult, this.mLineSpacingAdd, this.mIncludeFontPadding); } }
public ContactChipDrawable(int paddingLeft, int paddingRight, Typeface typeface, int textColor, int textSize, int backgroundColor) { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(textColor); mPaint.setTypeface(typeface); mPaint.setTextSize(textSize); mTextPaint = new TextPaint(mPaint); mMetrics = new BoringLayout.Metrics(); Paint.FontMetricsInt temp = mTextPaint.getFontMetricsInt(); mMetrics.ascent = temp.ascent; mMetrics.bottom = temp.bottom; mMetrics.descent = temp.descent; mMetrics.top = temp.top; mMetrics.leading = temp.leading; mRect = new RectF(); mMatrix = new Matrix(); mPaddingLeft = paddingLeft; mPaddingRight = paddingRight; mBackgroundColor = backgroundColor; }
private void updateLayout(){ if(mContactName == null) return; Rect bounds = getBounds(); if(bounds.width() == 0 || bounds.height() == 0) return; int outerWidth = Math.max(0, bounds.width() - bounds.height() - mPaddingLeft - mPaddingRight); mMetrics.width = Math.round(mTextPaint.measureText(mContactName, 0, mContactName.length()) + 0.5f); if(mBoringLayout == null) mBoringLayout = BoringLayout.make(mContactName, mTextPaint, outerWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 1f, mMetrics, true, TextUtils.TruncateAt.END, outerWidth); else mBoringLayout = mBoringLayout.replaceOrMake(mContactName, mTextPaint, outerWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 1f, mMetrics, true, TextUtils.TruncateAt.END, outerWidth); }
/** * Sets values to choose from * @param values New values to choose from */ public void setValues(CharSequence[] values) { if (this.values != values) { this.values = values; if (this.values != null) { layouts = new BoringLayout[this.values.length]; for (int i = 0; i < layouts.length; i++) { layouts[i] = new BoringLayout(this.values[i], textPaint, itemWidth, Layout.Alignment.ALIGN_CENTER, 1f, 1f, boringMetrics, false, ellipsize, itemWidth); } } else { layouts = new BoringLayout[0]; } // start marque only if has already been measured if (getWidth() > 0) { startMarqueeIfNeeded(); } requestLayout(); invalidate(); } }
private void updateLayout(){ if(mContactName == null) return; Rect bounds = getBounds(); if(bounds.width() == 0 || bounds.height() == 0) return; int outerWidth = Math.max(0, bounds.width() - bounds.height() - mPaddingLeft - mPaddingRight); mMetrics.width = (int) FloatMath.ceil(mTextPaint.measureText(mContactName, 0, mContactName.length())); if(mBoringLayout == null) mBoringLayout = BoringLayout.make(mContactName, mTextPaint, outerWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 1f, mMetrics, true, TextUtils.TruncateAt.END, outerWidth); else mBoringLayout = mBoringLayout.replaceOrMake(mContactName, mTextPaint, outerWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 1f, mMetrics, true, TextUtils.TruncateAt.END, outerWidth); }
protected void onMeasure(int i, int i2) { String generateCacheKey = generateCacheKey(); this.mCacheText = (TextCache) getAreaCache(generateCacheKey); if (this.mCacheText == null || this.mCacheText.mLayout == null || this.mMeasureDirty) { this.mCacheText = new TextCache(); addAreaCache(generateCacheKey, this.mCacheText); int mode = View.MeasureSpec.getMode(i); int mode2 = View.MeasureSpec.getMode(i2); int size = View.MeasureSpec.getSize(i); int size2 = View.MeasureSpec.getSize(i2); if (mode == 1073741824) { this.mCacheText.measuredWidth = size; } else { this.mBoring = BoringLayout.isBoring(this.mText, this.mPaint, UNKNOWN_BORING); if (this.mBoring == null || this.mBoring == UNKNOWN_BORING) { this.mCacheText.measuredWidth = (int) Layout.getDesiredWidth(this.mText, this.mPaint); } else { this.mCacheText.measuredWidth = this.mBoring.width; } TextCache textCache = this.mCacheText; textCache.measuredWidth = textCache.measuredWidth + (this.paddingLeft + this.paddingRight); if (mode == Integer.MIN_VALUE) { this.mCacheText.measuredWidth = Math.min(size, this.mCacheText.measuredWidth); } } mode = (this.mCacheText.measuredWidth - this.paddingLeft) - this.paddingRight; this.mCacheText.mLayout = makeNewLayout(mode, this.mBoring, mode, false); if (mode2 == 1073741824) { this.mCacheText.measuredHeight = size2; } else { this.mCacheText.measuredHeight = getDesiredHeight(); if (mode2 == Integer.MIN_VALUE) { this.mCacheText.measuredHeight = Math.min(size2, this.mCacheText.measuredHeight); } } setMeasuredDimension(this.mCacheText.measuredWidth, this.mCacheText.measuredHeight); return; } setMeasuredDimension(this.mCacheText.measuredWidth, this.mCacheText.measuredHeight); }
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mLayout == null && !TextUtils.isEmpty(mText)) { BoringLayout.Metrics fm = new BoringLayout.Metrics(); fm.width = (int) Layout.getDesiredWidth(mText, mTextPaint); mLayout = BoringLayout.make(mText, mTextPaint, MeasureSpec.getSize(widthMeasureSpec), TextViewAttrsHelper.getLayoutAlignment(this, getGravity()), mAttrsHelper.mSpacingMultiplier, mAttrsHelper.mSpacingAdd, fm, true); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
public ContactChipSpan(CharSequence name, int height, int maxWidth, int paddingLeft, int paddingRight, Typeface typeface, int textColor, int textSize, int backgroundColor) { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(textColor); mPaint.setTypeface(typeface); mPaint.setTextSize(textSize); mTextPaint = new TextPaint(mPaint); mRect = new RectF(); mMatrix = new Matrix(); mContactName = name; mPaddingLeft = paddingLeft; mPaddingRight = paddingRight; mBackgroundColor = backgroundColor; mHeight = height; mWidth = Math.round(Math.min(maxWidth, mPaint.measureText(name, 0, name.length()) + paddingLeft + paddingRight + height)); int outerWidth = Math.max(0, mWidth - mPaddingLeft - mPaddingRight - mHeight); Paint.FontMetricsInt temp = mTextPaint.getFontMetricsInt(); BoringLayout.Metrics mMetrics = new BoringLayout.Metrics(); mMetrics.width = Math.round(mTextPaint.measureText(mContactName, 0, mContactName.length()) + 0.5f); mMetrics.ascent = temp.ascent; mMetrics.bottom = temp.bottom; mMetrics.descent = temp.descent; mMetrics.top = temp.top; mMetrics.leading = temp.leading; mBoringLayout = BoringLayout.make(mContactName, mTextPaint, outerWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 1f, mMetrics, true, TextUtils.TruncateAt.END, outerWidth); }
private static BoringLayout.Metrics toBoringFontMetrics(FontMetricsInt metrics, @Nullable BoringLayout.Metrics fontMetrics) { if (fontMetrics == null) { fontMetrics = new BoringLayout.Metrics(); } fontMetrics.ascent = metrics.ascent; fontMetrics.bottom = metrics.bottom; fontMetrics.descent = metrics.descent; fontMetrics.leading = metrics.leading; fontMetrics.top = metrics.top; return fontMetrics; }
private Layout obtainLayout(CharSequence source, TextPaint paint, int widthMeasureSpec, boolean isSubtitle) { int width = MeasureSpec.getSize(widthMeasureSpec) - super.getCompoundPaddingLeft() - super.getCompoundPaddingRight(); BoringLayout.Metrics metrics = BoringLayout.isBoring(source, paint); Layout.Alignment alignment = isSubtitle ? Layout.Alignment.ALIGN_NORMAL : Layout.Alignment.ALIGN_OPPOSITE; if (metrics != null) { return BoringLayout.make(source, paint, width, alignment, 1.0f, 0, metrics, false); } else { return new StaticLayout(source, paint, width, alignment, 1.0f, 0, false); } }
public ContactChipSpan(CharSequence name, int height, int maxWidth, int paddingLeft, int paddingRight, Typeface typeface, int textColor, int textSize, int backgroundColor) { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(textColor); mPaint.setTypeface(typeface); mPaint.setTextSize(textSize); mTextPaint = new TextPaint(mPaint); mRect = new RectF(); mMatrix = new Matrix(); mContactName = name; mPaddingLeft = paddingLeft; mPaddingRight = paddingRight; mBackgroundColor = backgroundColor; mHeight = height; mWidth = Math.round(Math.min(maxWidth, mPaint.measureText(name, 0, name.length()) + paddingLeft + paddingRight + height)); int outerWidth = Math.max(0, mWidth - mPaddingLeft - mPaddingRight - mHeight); Paint.FontMetricsInt temp = mTextPaint.getFontMetricsInt(); BoringLayout.Metrics mMetrics = new BoringLayout.Metrics(); mMetrics.width = (int)FloatMath.ceil(mTextPaint.measureText(mContactName, 0, mContactName.length())); mMetrics.ascent = temp.ascent; mMetrics.bottom = temp.bottom; mMetrics.descent = temp.descent; mMetrics.top = temp.top; mMetrics.leading = temp.leading; mBoringLayout = BoringLayout.make(mContactName, mTextPaint, outerWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 1f, mMetrics, true, TextUtils.TruncateAt.END, outerWidth); }
private void makeNewLayout(int wantWidth, BoringLayout.Metrics boring, int ellipsisWidth, boolean bringIntoView) { if (wantWidth < 0) { wantWidth = 0; } mOldMaxLines = mMaxLines; boolean shouldEllipsize = (mEllipsize != null); mLayout = makeSingleLayout(wantWidth, boring, ellipsisWidth, mLayoutAlignment, shouldEllipsize, mEllipsize, bringIntoView); }
@Override public void measure(CSSNode node, float width, float height, MeasureOutput measureOutput) { // TODO(5578671): Handle text direction (see View#getTextDirectionHeuristic) ReactTextShadowNode reactCSSNode = (ReactTextShadowNode) node; TextPaint textPaint = sTextPaintInstance; Layout layout; Spanned text = Assertions.assertNotNull( reactCSSNode.mPreparedSpannableText, "Spannable element has not been prepared in onBeforeLayout"); BoringLayout.Metrics boring = BoringLayout.isBoring(text, textPaint); float desiredWidth = boring == null ? Layout.getDesiredWidth(text, textPaint) : Float.NaN; if (boring == null && (CSSConstants.isUndefined(width) || (!CSSConstants.isUndefined(desiredWidth) && desiredWidth <= width))) { // Is used when the width is not known and the text is not boring, ie. if it contains // unicode characters. layout = new StaticLayout( text, textPaint, (int) Math.ceil(desiredWidth), Layout.Alignment.ALIGN_NORMAL, 1, 0, true); } else if (boring != null && (CSSConstants.isUndefined(width) || boring.width <= width)) { // Is used for single-line, boring text when the width is either unknown or bigger // than the width of the text. layout = BoringLayout.make( text, textPaint, boring.width, Layout.Alignment.ALIGN_NORMAL, 1, 0, boring, true); } else { // Is used for multiline, boring text and the width is known. layout = new StaticLayout( text, textPaint, (int) width, Layout.Alignment.ALIGN_NORMAL, 1, 0, true); } measureOutput.height = layout.getHeight(); measureOutput.width = layout.getWidth(); if (reactCSSNode.mNumberOfLines != UNSET && reactCSSNode.mNumberOfLines < layout.getLineCount()) { measureOutput.height = layout.getLineBottom(reactCSSNode.mNumberOfLines - 1); } if (reactCSSNode.mLineHeight != UNSET) { int lines = reactCSSNode.mNumberOfLines != UNSET ? Math.min(reactCSSNode.mNumberOfLines, layout.getLineCount()) : layout.getLineCount(); float lineHeight = PixelUtil.toPixelFromSP(reactCSSNode.mLineHeight); measureOutput.height = lineHeight * lines; } }
private void drawWeek(Canvas canvas, int layoutIndex, int weekOffset) { int saveCount = canvas.save(); int labelHeight = dayLabelLayouts[0].getHeight(); float circleRadius = dayWidth / 3; int centerY = layouts[0].getHeight() / 2; float dateLineOffset = circleRadius - centerY; for (int i = 0; i < 7; i++) { int itemIndex = weekOffset * 7 + i; BoringLayout layout = layouts[layoutIndex + i]; BoringLayout labelLayout = dayLabelLayouts[i]; dayLabelTextPain.setColor(getTextColor(dayLabelTextColor, itemIndex)); labelLayout.draw(canvas); dayTextPaint.setColor(getTextColor(dayTextColor, itemIndex)); int count = canvas.save(); canvas.translate(0, labelHeight + dateLineOffset + labelPadding); if (dayDrawable != null) { dayDrawable.setBounds(backgroundRect); dayDrawable.setState(getItemDrawableState(itemIndex)); dayDrawable.draw(canvas); } if (indicatorDrawable != null && dayIndicators.get(itemIndex - dayDelta, false)) { indicatorDrawable.setBounds(indicatorRect); indicatorDrawable.setState(getItemDrawableState(itemIndex)); indicatorDrawable.draw(canvas); } layout.draw(canvas); canvas.restoreToCount(count); canvas.translate(dayWidth, 0); } canvas.restoreToCount(saveCount); }
private void remakeLayout() { if (getWidth() > 0) { LocalDate day = getRelativeFirstDay(-1); for (int i = 0; i < layouts.length; i++) { String dayText = String.valueOf(day.getDayOfMonth()); if (layouts[i] == null) { layouts[i] = BoringLayout.make(dayText, dayTextPaint, dayWidth, Layout.Alignment.ALIGN_CENTER, 1f, 1f, dayMetrics, false, ellipsize, dayWidth); } else { layouts[i].replaceOrMake(dayText, dayTextPaint, dayWidth, Layout.Alignment.ALIGN_CENTER, 1f, 1f, dayMetrics, false, ellipsize, dayWidth); } day = day.plusDays(1); } DayOfWeek dayOfWeek = firstDayOfWeek; // first index is 1 for (int i = 0; i < dayLabelLayouts.length; i++) { CharSequence name; if (labelNames == null) { name = dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.getDefault()); } else { int index = dayOfWeek.getValue() - 1; name = labelNames[index]; } if (dayLabelLayouts[i] == null) { dayLabelLayouts[i] = BoringLayout.make(name, dayLabelTextPain, dayWidth, Layout.Alignment.ALIGN_CENTER, 1f, 1f, dayLabelMetrics, false, ellipsize, dayWidth); } else { dayLabelLayouts[i].replaceOrMake(name, dayLabelTextPain, dayWidth, Layout.Alignment.ALIGN_CENTER, 1f, 1f, dayLabelMetrics, false, ellipsize, dayWidth); } dayOfWeek = dayOfWeek.plus(1); } } }
@Override public void measure( CSSNode node, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode, MeasureOutput measureOutput) { // TODO(5578671): Handle text direction (see View#getTextDirectionHeuristic) ReactTextShadowNode reactCSSNode = (ReactTextShadowNode) node; TextPaint textPaint = sTextPaintInstance; Layout layout; Spanned text = Assertions.assertNotNull( reactCSSNode.mPreparedSpannableText, "Spannable element has not been prepared in onBeforeLayout"); BoringLayout.Metrics boring = BoringLayout.isBoring(text, textPaint); float desiredWidth = boring == null ? Layout.getDesiredWidth(text, textPaint) : Float.NaN; // technically, width should never be negative, but there is currently a bug in boolean unconstrainedWidth = widthMode == CSSMeasureMode.UNDEFINED || width < 0; if (boring == null && (unconstrainedWidth || (!CSSConstants.isUndefined(desiredWidth) && desiredWidth <= width))) { // Is used when the width is not known and the text is not boring, ie. if it contains // unicode characters. layout = new StaticLayout( text, textPaint, (int) Math.ceil(desiredWidth), Layout.Alignment.ALIGN_NORMAL, 1, 0, true); } else if (boring != null && (unconstrainedWidth || boring.width <= width)) { // Is used for single-line, boring text when the width is either unknown or bigger // than the width of the text. layout = BoringLayout.make( text, textPaint, boring.width, Layout.Alignment.ALIGN_NORMAL, 1, 0, boring, true); } else { // Is used for multiline, boring text and the width is known. layout = new StaticLayout( text, textPaint, (int) width, Layout.Alignment.ALIGN_NORMAL, 1, 0, true); } measureOutput.height = layout.getHeight(); measureOutput.width = layout.getWidth(); if (reactCSSNode.mNumberOfLines != UNSET && reactCSSNode.mNumberOfLines < layout.getLineCount()) { measureOutput.height = layout.getLineBottom(reactCSSNode.mNumberOfLines - 1); } if (reactCSSNode.mLineHeight != UNSET) { int lines = reactCSSNode.mNumberOfLines != UNSET ? Math.min(reactCSSNode.mNumberOfLines, layout.getLineCount()) : layout.getLineCount(); float lineHeight = PixelUtil.toPixelFromSP(reactCSSNode.mLineHeight); measureOutput.height = lineHeight * lines; } }
@Override public void measure( CSSNodeAPI node, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode, MeasureOutput measureOutput) { // TODO(5578671): Handle text direction (see View#getTextDirectionHeuristic) ReactTextShadowNode reactCSSNode = (ReactTextShadowNode) node; TextPaint textPaint = sTextPaintInstance; Layout layout; Spanned text = Assertions.assertNotNull( reactCSSNode.mPreparedSpannableText, "Spannable element has not been prepared in onBeforeLayout"); BoringLayout.Metrics boring = BoringLayout.isBoring(text, textPaint); float desiredWidth = boring == null ? Layout.getDesiredWidth(text, textPaint) : Float.NaN; // technically, width should never be negative, but there is currently a bug in boolean unconstrainedWidth = widthMode == CSSMeasureMode.UNDEFINED || width < 0; if (boring == null && (unconstrainedWidth || (!CSSConstants.isUndefined(desiredWidth) && desiredWidth <= width))) { // Is used when the width is not known and the text is not boring, ie. if it contains // unicode characters. layout = new StaticLayout( text, textPaint, (int) Math.ceil(desiredWidth), Layout.Alignment.ALIGN_NORMAL, 1.f, 0.f, true); } else if (boring != null && (unconstrainedWidth || boring.width <= width)) { // Is used for single-line, boring text when the width is either unknown or bigger // than the width of the text. layout = BoringLayout.make( text, textPaint, boring.width, Layout.Alignment.ALIGN_NORMAL, 1.f, 0.f, boring, true); } else { // Is used for multiline, boring text and the width is known. layout = new StaticLayout( text, textPaint, (int) width, Layout.Alignment.ALIGN_NORMAL, 1.f, 0.f, true); } measureOutput.height = layout.getHeight(); measureOutput.width = layout.getWidth(); if (reactCSSNode.mNumberOfLines != UNSET && reactCSSNode.mNumberOfLines < layout.getLineCount()) { measureOutput.height = layout.getLineBottom(reactCSSNode.mNumberOfLines - 1); } }
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int saveCount = canvas.getSaveCount(); canvas.save(); int selectedItem = this.selectedItem; float itemWithPadding = itemWidth + dividerSize; // translate horizontal to center canvas.translate(itemWithPadding * sideItems, 0); if (values != null) { for (int i = 0; i < values.length; i++) { // set text color for item textPaint.setColor(getTextColor(i)); textPaint.setTextSize(getTextSize(i)); // get text layout BoringLayout layout = layouts[i]; int saveCountHeight = canvas.getSaveCount(); canvas.save(); float x = 0; float lineWidth = layout.getLineWidth(0); if (lineWidth > itemWidth) { if (isRtl(values[i])) { x += (lineWidth - itemWidth) / 2; } else { x -= (lineWidth - itemWidth) / 2; } } if (marquee != null && i == selectedItem) { x += marquee.getScroll(); } // translate vertically to center canvas.translate(-x, (canvas.getHeight() - layout.getHeight()) / 2); RectF clipBounds; if (x == 0) { clipBounds = itemClipBounds; } else { clipBounds = itemClipBoundsOffset; clipBounds.set(itemClipBounds); clipBounds.offset(x, 0); } canvas.clipRect(clipBounds); layout.draw(canvas); if (marquee != null && i == selectedItem && marquee.shouldDrawGhost()) { canvas.translate(marquee.getGhostOffset(), 0); layout.draw(canvas); } // restore vertical translation canvas.restoreToCount(saveCountHeight); // translate horizontal for 1 item canvas.translate(itemWithPadding, 0); } } // restore horizontal translation canvas.restoreToCount(saveCount); drawEdgeEffect(canvas, leftEdgeEffect, 270); drawEdgeEffect(canvas, rightEdgeEffect, 90); }