Add SubtitleView and CaptionStyleCompat to ExoPlayer.
This commit is contained in:
parent
e4b35e884a
commit
6c3ae7f175
@ -24,13 +24,20 @@ import com.google.android.exoplayer.demo.full.player.DefaultRendererBuilder;
|
|||||||
import com.google.android.exoplayer.demo.full.player.DemoPlayer;
|
import com.google.android.exoplayer.demo.full.player.DemoPlayer;
|
||||||
import com.google.android.exoplayer.demo.full.player.DemoPlayer.RendererBuilder;
|
import com.google.android.exoplayer.demo.full.player.DemoPlayer.RendererBuilder;
|
||||||
import com.google.android.exoplayer.demo.full.player.SmoothStreamingRendererBuilder;
|
import com.google.android.exoplayer.demo.full.player.SmoothStreamingRendererBuilder;
|
||||||
|
import com.google.android.exoplayer.text.CaptionStyleCompat;
|
||||||
|
import com.google.android.exoplayer.text.SubtitleView;
|
||||||
|
import com.google.android.exoplayer.util.Util;
|
||||||
import com.google.android.exoplayer.util.VerboseLogUtil;
|
import com.google.android.exoplayer.util.VerboseLogUtil;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.graphics.Point;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.view.Display;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
@ -38,6 +45,8 @@ import android.view.SurfaceHolder;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.view.View.OnTouchListener;
|
import android.view.View.OnTouchListener;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.view.accessibility.CaptioningManager;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.MediaController;
|
import android.widget.MediaController;
|
||||||
import android.widget.PopupMenu;
|
import android.widget.PopupMenu;
|
||||||
@ -50,6 +59,7 @@ import android.widget.TextView;
|
|||||||
public class FullPlayerActivity extends Activity implements SurfaceHolder.Callback, OnClickListener,
|
public class FullPlayerActivity extends Activity implements SurfaceHolder.Callback, OnClickListener,
|
||||||
DemoPlayer.Listener, DemoPlayer.TextListener {
|
DemoPlayer.Listener, DemoPlayer.TextListener {
|
||||||
|
|
||||||
|
private static final float CAPTION_LINE_HEIGHT_RATIO = 0.0533f;
|
||||||
private static final int MENU_GROUP_TRACKS = 1;
|
private static final int MENU_GROUP_TRACKS = 1;
|
||||||
private static final int ID_OFFSET = 2;
|
private static final int ID_OFFSET = 2;
|
||||||
|
|
||||||
@ -60,7 +70,7 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
|
|||||||
private VideoSurfaceView surfaceView;
|
private VideoSurfaceView surfaceView;
|
||||||
private TextView debugTextView;
|
private TextView debugTextView;
|
||||||
private TextView playerStateTextView;
|
private TextView playerStateTextView;
|
||||||
private TextView subtitlesTextView;
|
private SubtitleView subtitleView;
|
||||||
private Button videoButton;
|
private Button videoButton;
|
||||||
private Button audioButton;
|
private Button audioButton;
|
||||||
private Button textButton;
|
private Button textButton;
|
||||||
@ -108,7 +118,7 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
|
|||||||
debugTextView = (TextView) findViewById(R.id.debug_text_view);
|
debugTextView = (TextView) findViewById(R.id.debug_text_view);
|
||||||
|
|
||||||
playerStateTextView = (TextView) findViewById(R.id.player_state_view);
|
playerStateTextView = (TextView) findViewById(R.id.player_state_view);
|
||||||
subtitlesTextView = (TextView) findViewById(R.id.subtitles);
|
subtitleView = (SubtitleView) findViewById(R.id.subtitles);
|
||||||
|
|
||||||
mediaController = new MediaController(this);
|
mediaController = new MediaController(this);
|
||||||
mediaController.setAnchorView(root);
|
mediaController.setAnchorView(root);
|
||||||
@ -122,6 +132,7 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
|
|||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
configureSubtitleView();
|
||||||
preparePlayer();
|
preparePlayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,10 +391,10 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
|
|||||||
@Override
|
@Override
|
||||||
public void onText(String text) {
|
public void onText(String text) {
|
||||||
if (TextUtils.isEmpty(text)) {
|
if (TextUtils.isEmpty(text)) {
|
||||||
subtitlesTextView.setVisibility(View.INVISIBLE);
|
subtitleView.setVisibility(View.INVISIBLE);
|
||||||
} else {
|
} else {
|
||||||
subtitlesTextView.setVisibility(View.VISIBLE);
|
subtitleView.setVisibility(View.VISIBLE);
|
||||||
subtitlesTextView.setText(text);
|
subtitleView.setText(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,4 +420,40 @@ public class FullPlayerActivity extends Activity implements SurfaceHolder.Callba
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void configureSubtitleView() {
|
||||||
|
CaptionStyleCompat captionStyle;
|
||||||
|
float captionTextSize = getCaptionFontSize();
|
||||||
|
if (Util.SDK_INT >= 19) {
|
||||||
|
captionStyle = getUserCaptionStyleV19();
|
||||||
|
captionTextSize *= getUserCaptionFontScaleV19();
|
||||||
|
} else {
|
||||||
|
captionStyle = CaptionStyleCompat.DEFAULT;
|
||||||
|
}
|
||||||
|
subtitleView.setStyle(captionStyle);
|
||||||
|
subtitleView.setTextSize(captionTextSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getCaptionFontSize() {
|
||||||
|
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
|
||||||
|
.getDefaultDisplay();
|
||||||
|
Point displaySize = new Point();
|
||||||
|
display.getSize(displaySize);
|
||||||
|
return Math.max(getResources().getDimension(R.dimen.subtitle_minimum_font_size),
|
||||||
|
CAPTION_LINE_HEIGHT_RATIO * Math.min(displaySize.x, displaySize.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(19)
|
||||||
|
private float getUserCaptionFontScaleV19() {
|
||||||
|
CaptioningManager captioningManager =
|
||||||
|
(CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE);
|
||||||
|
return captioningManager.getFontScale();
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(19)
|
||||||
|
private CaptionStyleCompat getUserCaptionStyleV19() {
|
||||||
|
CaptioningManager captioningManager =
|
||||||
|
(CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE);
|
||||||
|
return CaptionStyleCompat.createFromCaptionStyle(captioningManager.getUserStyle());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,15 +24,13 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"/>
|
android:layout_gravity="center"/>
|
||||||
|
|
||||||
<TextView android:id="@+id/subtitles"
|
<com.google.android.exoplayer.text.SubtitleView android:id="@+id/subtitles"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center|bottom"
|
android:layout_gravity="bottom|center_horizontal"
|
||||||
android:paddingLeft="8dp"
|
android:layout_marginLeft="16dp"
|
||||||
android:paddingRight="8dp"
|
android:layout_marginRight="16dp"
|
||||||
android:paddingBottom="32dp"
|
android:layout_marginBottom="32dp"
|
||||||
android:gravity="center"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:visibility="invisible"/>
|
android:visibility="invisible"/>
|
||||||
|
|
||||||
<View android:id="@+id/shutter"
|
<View android:id="@+id/shutter"
|
||||||
|
22
demo/src/main/res/values/constants.xml
Normal file
22
demo/src/main/res/values/constants.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<!-- The minimum subtitle font size. -->
|
||||||
|
<dimen name="subtitle_minimum_font_size">13sp</dimen>
|
||||||
|
|
||||||
|
</resources>
|
@ -95,8 +95,8 @@ import com.google.android.exoplayer.upstream.NonBlockingInputStream;
|
|||||||
// likely. The choice of 25% is relatively arbitrary.
|
// likely. The choice of 25% is relatively arbitrary.
|
||||||
int tableSize = (sampleCount * 125) / 100;
|
int tableSize = (sampleCount * 125) / 100;
|
||||||
sampleSizeTable = new int[tableSize];
|
sampleSizeTable = new int[tableSize];
|
||||||
sampleDecodingTimeTable = new int[tableSize];
|
|
||||||
sampleCompositionTimeOffsetTable = new int[tableSize];
|
sampleCompositionTimeOffsetTable = new int[tableSize];
|
||||||
|
sampleDecodingTimeTable = new long[tableSize];
|
||||||
sampleIsSyncFrameTable = new boolean[tableSize];
|
sampleIsSyncFrameTable = new boolean[tableSize];
|
||||||
sampleHasSubsampleEncryptionTable = new boolean[tableSize];
|
sampleHasSubsampleEncryptionTable = new boolean[tableSize];
|
||||||
}
|
}
|
||||||
@ -147,7 +147,7 @@ import com.google.android.exoplayer.upstream.NonBlockingInputStream;
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSamplePresentationTime(int index) {
|
public long getSamplePresentationTime(int index) {
|
||||||
return sampleDecodingTimeTable[index] + sampleCompositionTimeOffsetTable[index];
|
return sampleDecodingTimeTable[index] + sampleCompositionTimeOffsetTable[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* 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.graphics.Color;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.view.accessibility.CaptioningManager;
|
||||||
|
import android.view.accessibility.CaptioningManager.CaptionStyle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A compatibility wrapper for {@link CaptionStyle}.
|
||||||
|
*/
|
||||||
|
public final class CaptionStyleCompat {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edge type value specifying no character edges.
|
||||||
|
*/
|
||||||
|
public static final int EDGE_TYPE_NONE = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edge type value specifying uniformly outlined character edges.
|
||||||
|
*/
|
||||||
|
public static final int EDGE_TYPE_OUTLINE = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edge type value specifying drop-shadowed character edges.
|
||||||
|
*/
|
||||||
|
public static final int EDGE_TYPE_DROP_SHADOW = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edge type value specifying raised bevel character edges.
|
||||||
|
*/
|
||||||
|
public static final int EDGE_TYPE_RAISED = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edge type value specifying depressed bevel character edges.
|
||||||
|
*/
|
||||||
|
public static final int EDGE_TYPE_DEPRESSED = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use color setting specified by the track and fallback to default caption style.
|
||||||
|
*/
|
||||||
|
public static final int USE_TRACK_COLOR_SETTINGS = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default caption style.
|
||||||
|
*/
|
||||||
|
public static final CaptionStyleCompat DEFAULT = new CaptionStyleCompat(
|
||||||
|
Color.WHITE, Color.BLACK, Color.TRANSPARENT, EDGE_TYPE_NONE, Color.WHITE, null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preferred foreground color.
|
||||||
|
*/
|
||||||
|
public final int foregroundColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preferred background color.
|
||||||
|
*/
|
||||||
|
public final int backgroundColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preferred window color.
|
||||||
|
*/
|
||||||
|
public final int windowColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preferred edge type. One of:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link #EDGE_TYPE_NONE}
|
||||||
|
* <li>{@link #EDGE_TYPE_OUTLINE}
|
||||||
|
* <li>{@link #EDGE_TYPE_DROP_SHADOW}
|
||||||
|
* <li>{@link #EDGE_TYPE_RAISED}
|
||||||
|
* <li>{@link #EDGE_TYPE_DEPRESSED}
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public final int edgeType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preferred edge color, if using an edge type other than {@link #EDGE_TYPE_NONE}.
|
||||||
|
*/
|
||||||
|
public final int edgeColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preferred typeface.
|
||||||
|
*/
|
||||||
|
public final Typeface typeface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link CaptionStyleCompat} equivalent to a provided {@link CaptionStyle}.
|
||||||
|
*
|
||||||
|
* @param style A {@link CaptionStyle}.
|
||||||
|
* @return The equivalent {@link CaptionStyleCompat}.
|
||||||
|
*/
|
||||||
|
@TargetApi(19)
|
||||||
|
public static CaptionStyleCompat createFromCaptionStyle(CaptionStyle style) {
|
||||||
|
int windowColor = Util.SDK_INT >= 21 ? getWindowColorV21(style) : Color.TRANSPARENT;
|
||||||
|
return new CaptionStyleCompat(style.foregroundColor, style.backgroundColor, windowColor,
|
||||||
|
style.edgeType, style.edgeColor, style.getTypeface());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param foregroundColor See {@link #foregroundColor}.
|
||||||
|
* @param backgroundColor See {@link #backgroundColor}.
|
||||||
|
* @param windowColor See {@link #windowColor}.
|
||||||
|
* @param edgeType See {@link #edgeType}.
|
||||||
|
* @param edgeColor See {@link #edgeColor}.
|
||||||
|
* @param typeface See {@link #typeface}.
|
||||||
|
*/
|
||||||
|
public CaptionStyleCompat(int foregroundColor, int backgroundColor, int windowColor, int edgeType,
|
||||||
|
int edgeColor, Typeface typeface) {
|
||||||
|
this.foregroundColor = foregroundColor;
|
||||||
|
this.backgroundColor = backgroundColor;
|
||||||
|
this.windowColor = windowColor;
|
||||||
|
this.edgeType = edgeType;
|
||||||
|
this.edgeColor = edgeColor;
|
||||||
|
this.typeface = typeface;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@TargetApi(21)
|
||||||
|
private static int getWindowColorV21(CaptioningManager.CaptionStyle captionStyle) {
|
||||||
|
// TODO: Uncomment when building against API level 21.
|
||||||
|
return Color.TRANSPARENT; //captionStyle.windowColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,295 @@
|
|||||||
|
/*
|
||||||
|
* 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.StaticLayout;
|
||||||
|
import android.text.TextPaint;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A view for rendering captions.
|
||||||
|
* <p>
|
||||||
|
* The caption style and text size can be configured using {@link #setStyle(CaptionStyleCompat)} and
|
||||||
|
* {@link #setTextSize(float)} respectively.
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reusable string builder used for holding text.
|
||||||
|
*/
|
||||||
|
private final StringBuilder textBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
// 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 int foregroundColor;
|
||||||
|
private int backgroundColor;
|
||||||
|
private int edgeColor;
|
||||||
|
private int edgeType;
|
||||||
|
|
||||||
|
private boolean hasMeasurements;
|
||||||
|
private int lastMeasuredWidth;
|
||||||
|
private StaticLayout layout;
|
||||||
|
|
||||||
|
private float spacingMult;
|
||||||
|
private float spacingAdd;
|
||||||
|
private int innerPaddingX;
|
||||||
|
|
||||||
|
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((2 * 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);
|
||||||
|
|
||||||
|
innerPaddingX = 0;
|
||||||
|
setText(text);
|
||||||
|
setTextSize(textSize);
|
||||||
|
setStyle(CaptionStyleCompat.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubtitleView(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBackgroundColor(int color) {
|
||||||
|
backgroundColor = color;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the text to be displayed by the view.
|
||||||
|
*
|
||||||
|
* @param text The text to display.
|
||||||
|
*/
|
||||||
|
public void setText(CharSequence text) {
|
||||||
|
textBuilder.setLength(0);
|
||||||
|
textBuilder.append(text);
|
||||||
|
hasMeasurements = false;
|
||||||
|
requestLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
hasMeasurements = false;
|
||||||
|
requestLayout();
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
hasMeasurements = false;
|
||||||
|
requestLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTypeface(Typeface typeface) {
|
||||||
|
if (textPaint.getTypeface() != typeface) {
|
||||||
|
textPaint.setTypeface(typeface);
|
||||||
|
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(textBuilder, textPaint, maxWidth, null, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user