diff --git a/library/src/main/java/com/google/android/exoplayer/text/CuePainter.java b/library/src/main/java/com/google/android/exoplayer/text/CuePainter.java
new file mode 100644
index 0000000000..e8c387fca9
--- /dev/null
+++ b/library/src/main/java/com/google/android/exoplayer/text/CuePainter.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.exoplayer.text;
+
+import com.google.android.exoplayer.util.Util;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Join;
+import android.graphics.Paint.Style;
+import android.graphics.RectF;
+import android.text.Layout.Alignment;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.util.Log;
+
+/**
+ * Draws {@link Cue}s.
+ */
+/* package */ final class CuePainter {
+
+ private static final String TAG = "CuePainter";
+
+ /**
+ * Ratio of inner padding to font size.
+ */
+ private static final float INNER_PADDING_RATIO = 0.125f;
+
+ /**
+ * Use the same line height ratio as WebVtt to match the display with the preview.
+ * WebVtt specifies line height as 5.3% of the viewport height.
+ */
+ private static final float LINE_HEIGHT_FRACTION = 0.0533f;
+
+ /**
+ * The default bottom padding to apply when {@link Cue#line} is {@link Cue#UNSET_VALUE}, as a
+ * fraction of the viewport height.
+ */
+ private static final float DEFAULT_BOTTOM_PADDING_FRACTION = 0.08f;
+
+ /**
+ * Temporary rectangle used for computing line bounds.
+ */
+ private final RectF lineBounds = new RectF();
+
+ // Styled dimensions.
+ private final float cornerRadius;
+ private final float outlineWidth;
+ private final float shadowRadius;
+ private final float shadowOffset;
+ private final float spacingMult;
+ private final float spacingAdd;
+
+ private final TextPaint textPaint;
+ private final Paint paint;
+
+ // Previous input variables.
+ private CharSequence cueText;
+ private int cuePosition;
+ private Alignment cueAlignment;
+ private int foregroundColor;
+ private int backgroundColor;
+ private int windowColor;
+ private int edgeColor;
+ private int edgeType;
+ private int parentLeft;
+ private int parentTop;
+ private int parentRight;
+ private int parentBottom;
+
+ // Derived drawing variables.
+ private StaticLayout textLayout;
+ private int textLeft;
+ private int textTop;
+ private int textPaddingX;
+
+ public CuePainter(Context context) {
+ int[] viewAttr = {android.R.attr.lineSpacingExtra, android.R.attr.lineSpacingMultiplier};
+ TypedArray styledAttributes = context.obtainStyledAttributes(null, viewAttr, 0, 0);
+ spacingAdd = styledAttributes.getDimensionPixelSize(0, 0);
+ spacingMult = styledAttributes.getFloat(1, 1);
+ styledAttributes.recycle();
+
+ Resources resources = context.getResources();
+ DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+ int twoDpInPx = Math.round((2f * displayMetrics.densityDpi) / DisplayMetrics.DENSITY_DEFAULT);
+ cornerRadius = twoDpInPx;
+ outlineWidth = twoDpInPx;
+ shadowRadius = twoDpInPx;
+ shadowOffset = twoDpInPx;
+
+ textPaint = new TextPaint();
+ textPaint.setAntiAlias(true);
+ textPaint.setSubpixelText(true);
+
+ paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setStyle(Style.FILL);
+ }
+
+ /**
+ * Draws the provided {@link Cue} into a canvas with the specified styling.
+ *
+ * A call to this method is able to use cached results of calculations made during the previous
+ * call, and so an instance of this class is able to optimize repeated calls to this method in
+ * which the same parameters are passed.
+ *
+ * @param cue The cue to draw.
+ * @param style The style to use when drawing the cue text.
+ * @param fontScale The font scale.
+ * @param canvas The canvas into which to draw.
+ * @param cueBoxLeft The left position of the enclosing cue box.
+ * @param cueBoxTop The top position of the enclosing cue box.
+ * @param cueBoxRight The right position of the enclosing cue box.
+ * @param cueBoxBottom The bottom position of the enclosing cue box.
+ */
+ public void draw(Cue cue, CaptionStyleCompat style, float fontScale, Canvas canvas,
+ int cueBoxLeft, int cueBoxTop, int cueBoxRight, int cueBoxBottom) {
+ if (TextUtils.equals(cueText, cue.text)
+ && cuePosition == cue.position
+ && Util.areEqual(cueAlignment, cue.alignment)
+ && foregroundColor == style.foregroundColor
+ && backgroundColor == style.backgroundColor
+ && windowColor == style.windowColor
+ && edgeType == style.edgeType
+ && edgeColor == style.edgeColor
+ && Util.areEqual(textPaint.getTypeface(), style.typeface)
+ && parentLeft == cueBoxLeft
+ && parentTop == cueBoxTop
+ && parentRight == cueBoxRight
+ && parentBottom == cueBoxBottom) {
+ // We can use the cached layout.
+ drawLayout(canvas);
+ return;
+ }
+
+ cueText = cue.text;
+ cuePosition = cue.position;
+ cueAlignment = cue.alignment;
+ foregroundColor = style.foregroundColor;
+ backgroundColor = style.backgroundColor;
+ windowColor = style.windowColor;
+ edgeType = style.edgeType;
+ edgeColor = style.edgeColor;
+ textPaint.setTypeface(style.typeface);
+ parentLeft = cueBoxLeft;
+ parentTop = cueBoxTop;
+ parentRight = cueBoxRight;
+ parentBottom = cueBoxBottom;
+
+ int parentWidth = parentRight - parentLeft;
+ int parentHeight = parentBottom - parentTop;
+
+ float textSize = LINE_HEIGHT_FRACTION * parentHeight * fontScale;
+ textPaint.setTextSize(textSize);
+ int textPaddingX = (int) (textSize * INNER_PADDING_RATIO + 0.5f);
+ int availableWidth = parentWidth - textPaddingX * 2;
+ if (availableWidth <= 0) {
+ Log.w(TAG, "Skipped drawing subtitle cue (insufficient space)");
+ return;
+ }
+
+ Alignment layoutAlignment = cueAlignment == null ? Alignment.ALIGN_CENTER : cueAlignment;
+ textLayout = new StaticLayout(cueText, textPaint, availableWidth, layoutAlignment, spacingMult,
+ spacingAdd, true);
+
+ int textHeight = textLayout.getHeight();
+ int textWidth = 0;
+ int lineCount = textLayout.getLineCount();
+ for (int i = 0; i < lineCount; i++) {
+ textWidth = Math.max((int) Math.ceil(textLayout.getLineWidth(i)), textWidth);
+ }
+ textWidth += textPaddingX * 2;
+
+ int textLeft = (parentWidth - textWidth) / 2;
+ int textRight = textLeft + textWidth;
+ int textTop = parentBottom - textHeight
+ - (int) (parentHeight * DEFAULT_BOTTOM_PADDING_FRACTION);
+ int textBottom = textTop + textHeight;
+
+ if (cue.position != Cue.UNSET_VALUE) {
+ if (cue.alignment == Alignment.ALIGN_OPPOSITE) {
+ textRight = (parentWidth * cue.position) / 100 + parentLeft;
+ textLeft = Math.max(textRight - textWidth, parentLeft);
+ } else {
+ textLeft = (parentWidth * cue.position) / 100 + parentLeft;
+ textRight = Math.min(textLeft + textWidth, parentRight);
+ }
+ }
+ if (cue.line != Cue.UNSET_VALUE) {
+ textTop = (parentHeight * cue.line) / 100 + parentTop;
+ textBottom = textTop + textHeight;
+ if (textBottom > parentBottom) {
+ textTop = parentBottom - textHeight;
+ textBottom = parentBottom;
+ }
+ }
+ textWidth = textRight - textLeft;
+
+ // Update the derived drawing variables.
+ this.textLayout = new StaticLayout(cueText, textPaint, textWidth, layoutAlignment, spacingMult,
+ spacingAdd, true);
+ this.textLeft = textLeft;
+ this.textTop = textTop;
+ this.textPaddingX = textPaddingX;
+
+ drawLayout(canvas);
+ }
+
+ /**
+ * Draws {@link #textLayout} into the provided canvas.
+ *
+ * @param canvas The canvas into which to draw.
+ */
+ private void drawLayout(Canvas canvas) {
+ final StaticLayout layout = textLayout;
+ if (layout == null) {
+ // Nothing to draw.
+ return;
+ }
+
+ int saveCount = canvas.save();
+ canvas.translate(textLeft, textTop);
+
+ if (Color.alpha(windowColor) > 0) {
+ paint.setColor(windowColor);
+ canvas.drawRect(-textPaddingX, 0, layout.getWidth() + textPaddingX, layout.getHeight(),
+ paint);
+ }
+
+ if (Color.alpha(backgroundColor) > 0) {
+ paint.setColor(backgroundColor);
+ float previousBottom = layout.getLineTop(0);
+ int lineCount = layout.getLineCount();
+ for (int i = 0; i < lineCount; i++) {
+ lineBounds.left = layout.getLineLeft(i) - textPaddingX;
+ lineBounds.right = layout.getLineRight(i) + textPaddingX;
+ lineBounds.top = previousBottom;
+ lineBounds.bottom = layout.getLineBottom(i);
+ previousBottom = lineBounds.bottom;
+ canvas.drawRoundRect(lineBounds, cornerRadius, cornerRadius, paint);
+ }
+ }
+
+ if (edgeType == CaptionStyleCompat.EDGE_TYPE_OUTLINE) {
+ textPaint.setStrokeJoin(Join.ROUND);
+ textPaint.setStrokeWidth(outlineWidth);
+ textPaint.setColor(edgeColor);
+ textPaint.setStyle(Style.FILL_AND_STROKE);
+ layout.draw(canvas);
+ } else if (edgeType == CaptionStyleCompat.EDGE_TYPE_DROP_SHADOW) {
+ textPaint.setShadowLayer(shadowRadius, shadowOffset, shadowOffset, edgeColor);
+ } else if (edgeType == CaptionStyleCompat.EDGE_TYPE_RAISED
+ || edgeType == CaptionStyleCompat.EDGE_TYPE_DEPRESSED) {
+ boolean raised = edgeType == CaptionStyleCompat.EDGE_TYPE_RAISED;
+ int colorUp = raised ? Color.WHITE : edgeColor;
+ int colorDown = raised ? edgeColor : Color.WHITE;
+ float offset = shadowRadius / 2f;
+ textPaint.setColor(foregroundColor);
+ textPaint.setStyle(Style.FILL);
+ textPaint.setShadowLayer(shadowRadius, -offset, -offset, colorUp);
+ layout.draw(canvas);
+ textPaint.setShadowLayer(shadowRadius, offset, offset, colorDown);
+ }
+
+ textPaint.setColor(foregroundColor);
+ textPaint.setStyle(Style.FILL);
+ layout.draw(canvas);
+ textPaint.setShadowLayer(0, 0, 0, 0);
+
+ canvas.restoreToCount(saveCount);
+ }
+
+}
diff --git a/library/src/main/java/com/google/android/exoplayer/text/SubtitleLayout.java b/library/src/main/java/com/google/android/exoplayer/text/SubtitleLayout.java
index fc8f764ce4..690e7056e7 100644
--- a/library/src/main/java/com/google/android/exoplayer/text/SubtitleLayout.java
+++ b/library/src/main/java/com/google/android/exoplayer/text/SubtitleLayout.java
@@ -16,9 +16,9 @@
package com.google.android.exoplayer.text;
import android.content.Context;
-import android.text.Layout.Alignment;
+import android.graphics.Canvas;
import android.util.AttributeSet;
-import android.view.ViewGroup;
+import android.view.View;
import java.util.ArrayList;
import java.util.List;
@@ -26,28 +26,13 @@ import java.util.List;
/**
* A view for rendering rich-formatted captions.
*/
-public final class SubtitleLayout extends ViewGroup {
+public final class SubtitleLayout extends View {
- /**
- * Use the same line height ratio as WebVtt to match the display with the preview.
- * WebVtt specifies line height as 5.3% of the viewport height.
- */
- private static final float LINE_HEIGHT_FRACTION = 0.0533f;
-
- /**
- * The default bottom padding to apply when {@link Cue#line} is {@link Cue#UNSET_VALUE}, as a
- * fraction of the viewport height.
- */
- private static final float DEFAULT_BOTTOM_PADDING_FRACTION = 0.08f;
-
- private final List subtitleViews;
-
- private List subtitleCues;
- private int viewsInUse;
+ private final List painters;
+ private List cues;
private float fontScale;
- private float textSize;
- private CaptionStyleCompat captionStyle;
+ private CaptionStyleCompat style;
public SubtitleLayout(Context context) {
this(context, null);
@@ -55,9 +40,9 @@ public final class SubtitleLayout extends ViewGroup {
public SubtitleLayout(Context context, AttributeSet attrs) {
super(context, attrs);
- subtitleViews = new ArrayList<>();
+ painters = new ArrayList<>();
fontScale = 1;
- captionStyle = CaptionStyleCompat.DEFAULT;
+ style = CaptionStyleCompat.DEFAULT;
}
/**
@@ -66,131 +51,54 @@ public final class SubtitleLayout extends ViewGroup {
* @param cues The cues to display.
*/
public void setCues(List cues) {
- subtitleCues = cues;
- int size = (cues == null) ? 0 : cues.size();
-
- // create new subtitle views if necessary
- if (size > subtitleViews.size()) {
- for (int i = subtitleViews.size(); i < size; i++) {
- SubtitleView newView = createSubtitleView();
- subtitleViews.add(newView);
- }
+ if (this.cues == cues) {
+ return;
}
-
- // add the views we currently need, if necessary
- for (int i = viewsInUse; i < size; i++) {
- addView(subtitleViews.get(i));
+ this.cues = cues;
+ // Ensure we have sufficient painters.
+ int cueCount = (cues == null) ? 0 : cues.size();
+ while (painters.size() < cueCount) {
+ painters.add(new CuePainter(getContext()));
}
-
- // remove the views we don't currently need, if necessary
- for (int i = size; i < viewsInUse; i++) {
- removeView(subtitleViews.get(i));
- }
-
- viewsInUse = size;
-
- for (int i = 0; i < size; i++) {
- subtitleViews.get(i).setText(cues.get(i).text);
- }
-
- requestLayout();
+ // Invalidate to trigger drawing.
+ invalidate();
}
/**
* Sets the scale of the font.
*
- * @param scale The scale of the font.
+ * @param fontScale The scale of the font.
*/
- public void setFontScale(float scale) {
- fontScale = scale;
- updateSubtitlesTextSize(getHeight());
-
- for (SubtitleView subtitleView : subtitleViews) {
- subtitleView.setTextSize(textSize);
+ public void setFontScale(float fontScale) {
+ if (this.fontScale == fontScale) {
+ return;
}
- requestLayout();
+ this.fontScale = fontScale;
+ // Invalidate to trigger drawing.
+ invalidate();
}
/**
* Configures the view according to the given style.
*
- * @param captionStyle A style for the view.
+ * @param style A style for the view.
*/
- public void setStyle(CaptionStyleCompat captionStyle) {
- this.captionStyle = captionStyle;
-
- for (SubtitleView subtitleView : subtitleViews) {
- subtitleView.setStyle(captionStyle);
+ public void setStyle(CaptionStyleCompat style) {
+ if (this.style == style) {
+ return;
}
- requestLayout();
+ this.style = style;
+ // Invalidate to trigger drawing.
+ invalidate();
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int width = MeasureSpec.getSize(widthMeasureSpec);
- int height = MeasureSpec.getSize(heightMeasureSpec);
- setMeasuredDimension(width, height);
-
- updateSubtitlesTextSize(height);
-
- for (int i = 0; i < viewsInUse; i++) {
- subtitleViews.get(i).setTextSize(textSize);
- subtitleViews.get(i).measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
+ public void dispatchDraw(Canvas canvas) {
+ int cueCount = (cues == null) ? 0 : cues.size();
+ for (int i = 0; i < cueCount; i++) {
+ painters.get(i).draw(cues.get(i), style, fontScale, canvas, getLeft(), getTop(), getRight(),
+ getBottom());
}
}
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- int width = right - left;
- int height = bottom - top;
-
- for (int i = 0; i < viewsInUse; i++) {
- SubtitleView subtitleView = subtitleViews.get(i);
- Cue subtitleCue = subtitleCues.get(i);
-
- int viewLeft = (width - subtitleView.getMeasuredWidth()) / 2;
- int viewRight = viewLeft + subtitleView.getMeasuredWidth();
- int viewTop = bottom - subtitleView.getMeasuredHeight()
- - (int) (height * DEFAULT_BOTTOM_PADDING_FRACTION);
- int viewBottom = bottom;
-
- if (subtitleCue.alignment != null) {
- subtitleView.setTextAlignment(subtitleCue.alignment);
- } else {
- subtitleView.setTextAlignment(Alignment.ALIGN_CENTER);
- }
- if (subtitleCue.position != Cue.UNSET_VALUE) {
- if (subtitleCue.alignment == Alignment.ALIGN_OPPOSITE) {
- viewRight = (int) ((width * (double) subtitleCue.position) / 100) + left;
- viewLeft = Math.max(viewRight - subtitleView.getMeasuredWidth(), left);
- } else {
- viewLeft = (int) ((width * (double) subtitleCue.position) / 100) + left;
- viewRight = Math.min(viewLeft + subtitleView.getMeasuredWidth(), right);
- }
- }
- if (subtitleCue.line != Cue.UNSET_VALUE) {
- viewTop = (int) (height * (double) subtitleCue.line / 100) + top;
- viewBottom = viewTop + subtitleView.getMeasuredHeight();
- if (viewBottom > bottom) {
- viewTop = bottom - subtitleView.getMeasuredHeight();
- viewBottom = bottom;
- }
- }
-
- subtitleView.layout(viewLeft, viewTop, viewRight, viewBottom);
- }
- }
-
- private void updateSubtitlesTextSize(int height) {
- textSize = LINE_HEIGHT_FRACTION * height * fontScale;
- }
-
- private SubtitleView createSubtitleView() {
- SubtitleView view = new SubtitleView(getContext());
- view.setStyle(captionStyle);
- view.setTextSize(textSize);
- return view;
- }
-
}
diff --git a/library/src/main/java/com/google/android/exoplayer/text/SubtitleView.java b/library/src/main/java/com/google/android/exoplayer/text/SubtitleView.java
deleted file mode 100644
index c317508dd4..0000000000
--- a/library/src/main/java/com/google/android/exoplayer/text/SubtitleView.java
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.android.exoplayer.text;
-
-import com.google.android.exoplayer.util.Util;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Paint.Join;
-import android.graphics.Paint.Style;
-import android.graphics.RectF;
-import android.graphics.Typeface;
-import android.text.Layout.Alignment;
-import android.text.StaticLayout;
-import android.text.TextPaint;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.view.View;
-
-/**
- * A view for rendering a single caption.
- */
-public class SubtitleView extends View {
-
- /**
- * Ratio of inner padding to font size.
- */
- private static final float INNER_PADDING_RATIO = 0.125f;
-
- /**
- * Temporary rectangle used for computing line bounds.
- */
- private final RectF lineBounds = new RectF();
-
- // Styled dimensions.
- private final float cornerRadius;
- private final float outlineWidth;
- private final float shadowRadius;
- private final float shadowOffset;
-
- private TextPaint textPaint;
- private Paint paint;
-
- private CharSequence text;
-
- private int foregroundColor;
- private int backgroundColor;
- private int edgeColor;
- private int edgeType;
-
- private boolean hasMeasurements;
- private int lastMeasuredWidth;
- private StaticLayout layout;
-
- private Alignment alignment;
- private float spacingMult;
- private float spacingAdd;
- private int innerPaddingX;
-
- public SubtitleView(Context context) {
- this(context, null);
- }
-
- public SubtitleView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SubtitleView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- int[] viewAttr = {android.R.attr.text, android.R.attr.textSize,
- android.R.attr.lineSpacingExtra, android.R.attr.lineSpacingMultiplier};
- TypedArray a = context.obtainStyledAttributes(attrs, viewAttr, defStyleAttr, 0);
- CharSequence text = a.getText(0);
- int textSize = a.getDimensionPixelSize(1, 15);
- spacingAdd = a.getDimensionPixelSize(2, 0);
- spacingMult = a.getFloat(3, 1);
- a.recycle();
-
- Resources resources = getContext().getResources();
- DisplayMetrics displayMetrics = resources.getDisplayMetrics();
- int twoDpInPx = Math.round((2f * displayMetrics.densityDpi) / DisplayMetrics.DENSITY_DEFAULT);
- cornerRadius = twoDpInPx;
- outlineWidth = twoDpInPx;
- shadowRadius = twoDpInPx;
- shadowOffset = twoDpInPx;
-
- textPaint = new TextPaint();
- textPaint.setAntiAlias(true);
- textPaint.setSubpixelText(true);
-
- alignment = Alignment.ALIGN_CENTER;
-
- paint = new Paint();
- paint.setAntiAlias(true);
-
- innerPaddingX = 0;
- setText(text);
- setTextSize(textSize);
- setStyle(CaptionStyleCompat.DEFAULT);
- }
-
- @Override
- public void setBackgroundColor(int color) {
- backgroundColor = color;
- forceUpdate(false);
- }
-
- /**
- * Sets the text to be displayed by the view.
- *
- * @param text The text to display.
- */
- public void setText(CharSequence text) {
- this.text = text;
- forceUpdate(true);
- }
-
- /**
- * Sets the text size in pixels.
- *
- * @param size The text size in pixels.
- */
- public void setTextSize(float size) {
- if (textPaint.getTextSize() != size) {
- textPaint.setTextSize(size);
- innerPaddingX = (int) (size * INNER_PADDING_RATIO + 0.5f);
- forceUpdate(true);
- }
- }
-
- /**
- * Sets the text alignment.
- *
- * @param textAlignment The text alignment.
- */
- public void setTextAlignment(Alignment textAlignment) {
- alignment = textAlignment;
- }
-
- /**
- * Configures the view according to the given style.
- *
- * @param style A style for the view.
- */
- public void setStyle(CaptionStyleCompat style) {
- foregroundColor = style.foregroundColor;
- backgroundColor = style.backgroundColor;
- edgeType = style.edgeType;
- edgeColor = style.edgeColor;
- setTypeface(style.typeface);
- super.setBackgroundColor(style.windowColor);
- forceUpdate(true);
- }
-
- private void setTypeface(Typeface typeface) {
- if (textPaint.getTypeface() != typeface) {
- textPaint.setTypeface(typeface);
- forceUpdate(true);
- }
- }
-
- private void forceUpdate(boolean needsLayout) {
- if (needsLayout) {
- hasMeasurements = false;
- requestLayout();
- }
- invalidate();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int widthSpec = MeasureSpec.getSize(widthMeasureSpec);
-
- if (computeMeasurements(widthSpec)) {
- final StaticLayout layout = this.layout;
- final int paddingX = getPaddingLeft() + getPaddingRight() + innerPaddingX * 2;
- final int height = layout.getHeight() + getPaddingTop() + getPaddingBottom();
- int width = 0;
- int lineCount = layout.getLineCount();
- for (int i = 0; i < lineCount; i++) {
- width = Math.max((int) Math.ceil(layout.getLineWidth(i)), width);
- }
- width += paddingX;
- setMeasuredDimension(width, height);
- } else if (Util.SDK_INT >= 11) {
- setTooSmallMeasureDimensionV11();
- } else {
- setMeasuredDimension(0, 0);
- }
- }
-
- @TargetApi(11)
- private void setTooSmallMeasureDimensionV11() {
- setMeasuredDimension(MEASURED_STATE_TOO_SMALL, MEASURED_STATE_TOO_SMALL);
- }
-
- @Override
- public void onLayout(boolean changed, int l, int t, int r, int b) {
- final int width = r - l;
- computeMeasurements(width);
- }
-
- private boolean computeMeasurements(int maxWidth) {
- if (hasMeasurements && maxWidth == lastMeasuredWidth) {
- return true;
- }
-
- // Account for padding.
- final int paddingX = getPaddingLeft() + getPaddingRight() + innerPaddingX * 2;
- maxWidth -= paddingX;
- if (maxWidth <= 0) {
- return false;
- }
-
- hasMeasurements = true;
- lastMeasuredWidth = maxWidth;
- layout = new StaticLayout(text, textPaint, maxWidth, alignment, spacingMult, spacingAdd, true);
- return true;
- }
-
- @Override
- protected void onDraw(Canvas c) {
- final StaticLayout layout = this.layout;
- if (layout == null) {
- return;
- }
-
- final int saveCount = c.save();
- final int innerPaddingX = this.innerPaddingX;
- c.translate(getPaddingLeft() + innerPaddingX, getPaddingTop());
-
- final int lineCount = layout.getLineCount();
- final Paint textPaint = this.textPaint;
- final Paint paint = this.paint;
- final RectF bounds = lineBounds;
-
- if (Color.alpha(backgroundColor) > 0) {
- final float cornerRadius = this.cornerRadius;
- float previousBottom = layout.getLineTop(0);
-
- paint.setColor(backgroundColor);
- paint.setStyle(Style.FILL);
-
- for (int i = 0; i < lineCount; i++) {
- bounds.left = layout.getLineLeft(i) - innerPaddingX;
- bounds.right = layout.getLineRight(i) + innerPaddingX;
- bounds.top = previousBottom;
- bounds.bottom = layout.getLineBottom(i);
- previousBottom = bounds.bottom;
-
- c.drawRoundRect(bounds, cornerRadius, cornerRadius, paint);
- }
- }
-
- if (edgeType == CaptionStyleCompat.EDGE_TYPE_OUTLINE) {
- textPaint.setStrokeJoin(Join.ROUND);
- textPaint.setStrokeWidth(outlineWidth);
- textPaint.setColor(edgeColor);
- textPaint.setStyle(Style.FILL_AND_STROKE);
- layout.draw(c);
- } else if (edgeType == CaptionStyleCompat.EDGE_TYPE_DROP_SHADOW) {
- textPaint.setShadowLayer(shadowRadius, shadowOffset, shadowOffset, edgeColor);
- } else if (edgeType == CaptionStyleCompat.EDGE_TYPE_RAISED
- || edgeType == CaptionStyleCompat.EDGE_TYPE_DEPRESSED) {
- boolean raised = edgeType == CaptionStyleCompat.EDGE_TYPE_RAISED;
- int colorUp = raised ? Color.WHITE : edgeColor;
- int colorDown = raised ? edgeColor : Color.WHITE;
- float offset = shadowRadius / 2f;
- textPaint.setColor(foregroundColor);
- textPaint.setStyle(Style.FILL);
- textPaint.setShadowLayer(shadowRadius, -offset, -offset, colorUp);
- layout.draw(c);
- textPaint.setShadowLayer(shadowRadius, offset, offset, colorDown);
- }
-
- textPaint.setColor(foregroundColor);
- textPaint.setStyle(Style.FILL);
- layout.draw(c);
- textPaint.setShadowLayer(0, 0, 0, 0);
- c.restoreToCount(saveCount);
- }
-
-}