();
44 | for (SubtitleFileReader subtitleFileReader : readers) {
45 | lrcExts.add(subtitleFileReader.getSupportFileExt());
46 | }
47 | return lrcExts;
48 | }
49 |
50 | /**
51 | * 获取文件读取器
52 | *
53 | * @param file
54 | * @return
55 | */
56 | public static SubtitleFileReader getSubtitleFileReader(File file) {
57 | return getSubtitleFileReader(file.getName());
58 | }
59 |
60 | /**
61 | * 获取歌词文件读取器
62 | *
63 | * @param fileName
64 | * @return
65 | */
66 | public static SubtitleFileReader getSubtitleFileReader(String fileName) {
67 | String ext = FileUtil.getFileExt(fileName);
68 | for (SubtitleFileReader subtitleFileReader : readers) {
69 | if (subtitleFileReader.isFileSupported(ext)) {
70 | return subtitleFileReader;
71 | }
72 | }
73 | return null;
74 | }
75 |
76 | /**
77 | * 获取保存器
78 | *
79 | * @param file
80 | * @return
81 | */
82 | public static SubtitleFileWriter getSubtitleFileWriter(File file) {
83 | return getSubtitleFileWriter(file.getName());
84 | }
85 |
86 | /**
87 | * 获取保存器
88 | *
89 | * @param fileName
90 | * @return
91 | */
92 | public static SubtitleFileWriter getSubtitleFileWriter(String fileName) {
93 | String ext = FileUtil.getFileExt(fileName);
94 | for (SubtitleFileWriter subtitleFileWriter : writers) {
95 | if (subtitleFileWriter.isFileSupported(ext)) {
96 | return subtitleFileWriter;
97 | }
98 | }
99 | return null;
100 | }
101 |
102 | /**
103 | * 解析字幕文本
104 | *
105 | * @param subtitleLine
106 | * @return html格式对应的字幕文本
107 | */
108 | public static String[] parseSubtitleText(String subtitleLine) {
109 | String[] result = {"", ""};
110 | String regex = "\\{[^\\{]+\\}";
111 | //去掉样式
112 | result[0] = subtitleLine.replaceAll(regex, "");
113 | //加载样式
114 | Pattern tempPattern = Pattern.compile(regex);
115 | Matcher tempMatcher = tempPattern.matcher(subtitleLine);
116 | if (tempMatcher.find()) {
117 | StringBuilder subtitleTextSB = new StringBuilder();
118 | String[] splitSubtitles = subtitleLine.split(regex, -1);
119 | int index = 0;
120 |
121 | Pattern pattern = Pattern.compile(regex);
122 | Matcher matcher = pattern.matcher(subtitleLine);
123 | //遍历样式字符串
124 | while (matcher.find()) {
125 | if (index == 0 && splitSubtitles.length > 0 && !TextUtils.isEmpty(splitSubtitles[0])) {
126 | subtitleTextSB.append(splitSubtitles[0]);
127 | }
128 | String styleString = matcher.group();
129 | if (index + 1 >= splitSubtitles.length) {
130 | break;
131 | }
132 | String splitSubtitle = splitSubtitles[index + 1];
133 | String subtitleText = getSubtitleText(styleString, splitSubtitle);
134 | subtitleTextSB.append(subtitleText);
135 |
136 | index++;
137 | }
138 |
139 | //如果没有样式
140 | if (index == 0 && splitSubtitles.length > 0 && !TextUtils.isEmpty(splitSubtitles[0])) {
141 | subtitleTextSB.append(splitSubtitles[0]);
142 | }
143 | //添加剩余的字幕内容
144 | for (index++; index < splitSubtitles.length; index++) {
145 | if (!TextUtils.isEmpty(splitSubtitles[index])) {
146 | subtitleTextSB.append(splitSubtitles[index]);
147 | }
148 | }
149 | result[1] = subtitleTextSB.toString();
150 | } else {
151 | result[1] = subtitleLine;
152 | }
153 | return result;
154 | }
155 |
156 | /**
157 | * 获取字幕文本
158 | *
159 | * @param styleString 样式字符串
160 | * @param splitSubtitle 分隔后的字幕文本
161 | * @return
162 | */
163 | private static String getSubtitleText(String styleString, String splitSubtitle) {
164 | StringBuilder result = new StringBuilder();
165 | int start = styleString.indexOf("{");
166 | int end = styleString.lastIndexOf("}");
167 | styleString = styleString.substring(start + 1, end);
168 | styleString = styleString.replaceAll("\\\\", "\\$");
169 | if (styleString.contains("$")) {
170 | result.append(" 粗体,i<0/1> 斜体,u<0/1> 下划线,s<0/1> 删除线(0=关闭,1=开启)
184 |
185 | if (style.startsWith("b1")) {
186 | splitSubtitle = "" + splitSubtitle + "";
187 | } else if (style.startsWith("i1")) {
188 | splitSubtitle = "" + splitSubtitle + "";
189 | } else if (style.startsWith("u1")) {
190 | splitSubtitle = "" + splitSubtitle + "";
191 | } else if (style.startsWith("s1")) {
192 | splitSubtitle = "" + splitSubtitle + "";
193 | }
194 |
195 | } else if (style.startsWith("c&H") || style.startsWith("1c&H")) {
196 | //c&H& 改变主体颜色(同1c)
197 | //1c&H& 改变主体颜色
198 | int endIndex = style.lastIndexOf("&");
199 | style = style.substring(0, endIndex).trim();
200 | String color = "";
201 | if (style.startsWith("c&H")) {
202 | color = convertRgbColor(style.substring("c&H".length()).trim());
203 | } else {
204 | color = convertRgbColor(style.substring("1c&H".length()).trim());
205 | }
206 | result.append(" color=\"#" + color + "\"");
207 |
208 | }
209 | }
210 | result.append(">");
211 | }
212 | //修改成html标签
213 | if (result.length() > 0) {
214 | result.append(splitSubtitle);
215 | result.append("");
216 | } else {
217 | result.append(splitSubtitle);
218 | }
219 | return result.toString();
220 | }
221 |
222 | /**
223 | * 获取rgb颜色字符串
224 | * 版权归作者所有,任何形式转载请联系作者。
225 | * 作者:无条件积极关注(来自豆瓣)
226 | * 来源:https://www.douban.com/note/658520175/
227 | *
228 | * 颜色格式:&Haabbggrr,均为十六进制,取值0-F。
229 | * 前2位(alpha)为透明度,00=不透明,FF=DEC255=全透明;后6是BGR蓝绿红颜色。 排在最前的00可以忽略不写, 如:{\c&HFF&}={\c&H0000FF&}为纯红色、&HFFFFFF=纯白色、&HC8000000=透明度为200的黑色。
230 | *
231 | * @param abgrColorString
232 | * @return
233 | */
234 | public static String convertArgbColor(String abgrColorString) {
235 | if (abgrColorString.length() == 8) {
236 | return abgrColorString.substring(6, 8) + abgrColorString.substring(4, 6) + abgrColorString.substring(2, 4);
237 | }
238 | return abgrColorString.substring(4, 6) + abgrColorString.substring(2, 4) + abgrColorString.substring(0, 2);
239 | }
240 |
241 | /**
242 | * 获取rgb颜色字符串
243 | *
244 | * @param bgrColorString
245 | * @return
246 | */
247 | public static String convertRgbColor(String bgrColorString) {
248 | return convertArgbColor(bgrColorString);
249 | }
250 |
251 | /**
252 | * 获取bgr颜色字符串
253 | *
254 | * @param rgbColorString
255 | * @return
256 | */
257 | public static String convertBgrColor(String rgbColorString) {
258 | return convertRgbColor(rgbColorString);
259 | }
260 |
261 | /**
262 | * 获取abgr颜色字符串
263 | *
264 | * @param argbColorString
265 | * @return
266 | */
267 | public static String convertAbgrColor(String argbColorString) {
268 | return convertRgbColor(argbColorString);
269 | }
270 |
271 | /**
272 | * 根据当前播放进度获取当前行字幕内容
273 | *
274 | * @param subtitleLineInfos
275 | * @param curPlayingTime
276 | * @param playOffset
277 | * @return
278 | */
279 | public static int getLineNumber(List subtitleLineInfos, long curPlayingTime, long playOffset) {
280 | if (subtitleLineInfos != null && subtitleLineInfos.size() > 0) {
281 | //添加歌词增量
282 | long nowPlayingTime = curPlayingTime + playOffset;
283 | for (int i = 0; i < subtitleLineInfos.size(); i++) {
284 | SubtitleLineInfo subtitleLineInfo = subtitleLineInfos.get(i);
285 | int lineStartTime = subtitleLineInfo.getStartTime();
286 | int lineEndTime = subtitleLineInfo.getEndTime();
287 | if (nowPlayingTime < lineStartTime) {
288 | return -1;
289 | } else if (nowPlayingTime >= lineStartTime && nowPlayingTime <= lineEndTime) {
290 | return i;
291 | }
292 | }
293 | }
294 | return -1;
295 | }
296 | }
297 |
--------------------------------------------------------------------------------
/subtitlelibrary/src/main/java/com/zlm/subtitlelibrary/util/TimeUtil.java:
--------------------------------------------------------------------------------
1 | package com.zlm.subtitlelibrary.util;
2 |
3 | /**
4 | * @Description: 时间处理类
5 | * @author: zhangliangming
6 | * @date: 2019-01-12 21:37
7 | **/
8 | public class TimeUtil {
9 | /**
10 | * 解析字幕时间
11 | *
12 | * @param timeString 00:00:00,000
13 | * @return
14 | */
15 | public static int parseSubtitleTime(String timeString) {
16 | timeString = timeString.replace(",", ":");
17 | timeString = timeString.replace(".", ":");
18 | String timedata[] = timeString.split(":");
19 | int second = 1000;
20 | int minute = 60 * second;
21 | int hour = 60 * minute;
22 | int msec = 0;
23 | if (timedata[3].length() == 2) {
24 | msec = Integer.parseInt(timedata[3]) * 10;
25 | } else {
26 | msec = Integer.parseInt(timedata[3]);
27 | }
28 | return Integer.parseInt(timedata[0]) * hour + Integer.parseInt(timedata[1]) * minute
29 | + Integer.parseInt(timedata[2]) * second + msec;
30 | }
31 |
32 | /**
33 | * 毫秒转时间字符串
34 | *
35 | * @param msecTotal
36 | * @return 00:00:00,000
37 | */
38 | public static String parseHHMMSSFFFString(int msecTotal) {
39 | int msec = msecTotal % 1000;
40 | msecTotal /= 1000;
41 | int minute = msecTotal / 60;
42 | int hour = minute / 60;
43 | int second = msecTotal % 60;
44 | minute %= 60;
45 | return String.format("%02d:%02d:%02d,%03d", hour, minute, second, msec);
46 | }
47 |
48 | /**
49 | * 毫秒转时间字符串
50 | *
51 | * @param msecTotal
52 | * @return 00:00:00.00
53 | */
54 | public static String parseHHMMSSFFString(int msecTotal) {
55 | int msec = msecTotal % 1000;
56 | msecTotal /= 1000;
57 | int minute = msecTotal / 60;
58 | int hour = minute / 60;
59 | int second = msecTotal % 60;
60 | minute %= 60;
61 | return String.format("%02d:%02d:%02d.%02d", hour, minute, second, msec / 10);
62 | }
63 |
64 | /**
65 | * 毫秒转时间字符串
66 | *
67 | * @param msecTotal
68 | * @return 00:00:00
69 | */
70 | public static String parseHHMMSSString(int msecTotal) {
71 | msecTotal /= 1000;
72 | int minute = msecTotal / 60;
73 | int hour = minute / 60;
74 | int second = msecTotal % 60;
75 | minute %= 60;
76 | return String.format("%02d:%02d:%02d", hour, minute, second);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/subtitlelibrary/src/main/java/com/zlm/subtitlelibrary/widget/SubtitleView.java:
--------------------------------------------------------------------------------
1 | package com.zlm.subtitlelibrary.widget;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.AppCompatTextView;
5 | import android.util.AttributeSet;
6 |
7 | /**
8 | * @Description: 字幕视图
9 | * @author: zhangliangming
10 | * @date: 2019-01-17 22:17
11 | **/
12 | public class SubtitleView extends AppCompatTextView {
13 |
14 | public SubtitleView(Context context) {
15 | super(context);
16 | init(context);
17 | }
18 |
19 | public SubtitleView(Context context, AttributeSet attrs) {
20 | super(context, attrs);
21 | init(context);
22 | }
23 |
24 | public SubtitleView(Context context, AttributeSet attrs, int defStyleAttr) {
25 | super(context, attrs, defStyleAttr);
26 | init(context);
27 | }
28 |
29 | private void init(Context context) {
30 |
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/subtitlelibrary/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SubtitleLibrary
3 |
4 |
--------------------------------------------------------------------------------