Code refactor
Added new lyrics view and code refactor
This commit is contained in:
parent
e5407592c5
commit
8a2b803286
22 changed files with 1151 additions and 1065 deletions
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (C) 2017 wangchenyan
|
||||
*
|
||||
* 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 code.name.monkey.retromusic.lyrics;
|
||||
|
||||
import android.text.Layout;
|
||||
import android.text.StaticLayout;
|
||||
import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
* 一行歌词实体
|
||||
*/
|
||||
class LrcEntry implements Comparable<LrcEntry> {
|
||||
private long time;
|
||||
private String text;
|
||||
private String secondText;
|
||||
private StaticLayout staticLayout;
|
||||
/**
|
||||
* 歌词距离视图顶部的距离
|
||||
*/
|
||||
private float offset = Float.MIN_VALUE;
|
||||
public static final int GRAVITY_CENTER = 0;
|
||||
public static final int GRAVITY_LEFT = 1;
|
||||
public static final int GRAVITY_RIGHT = 2;
|
||||
|
||||
LrcEntry(long time, String text) {
|
||||
this.time = time;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
LrcEntry(long time, String text, String secondText) {
|
||||
this.time = time;
|
||||
this.text = text;
|
||||
this.secondText = secondText;
|
||||
}
|
||||
|
||||
void init(TextPaint paint, int width, int gravity) {
|
||||
Layout.Alignment align;
|
||||
switch (gravity) {
|
||||
case GRAVITY_LEFT:
|
||||
align = Layout.Alignment.ALIGN_NORMAL;
|
||||
break;
|
||||
|
||||
default:
|
||||
case GRAVITY_CENTER:
|
||||
align = Layout.Alignment.ALIGN_CENTER;
|
||||
break;
|
||||
|
||||
case GRAVITY_RIGHT:
|
||||
align = Layout.Alignment.ALIGN_OPPOSITE;
|
||||
break;
|
||||
}
|
||||
staticLayout = new StaticLayout(getShowText(), paint, width, align, 1f, 0f, false);
|
||||
|
||||
offset = Float.MIN_VALUE;
|
||||
}
|
||||
|
||||
long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
StaticLayout getStaticLayout() {
|
||||
return staticLayout;
|
||||
}
|
||||
|
||||
int getHeight() {
|
||||
if (staticLayout == null) {
|
||||
return 0;
|
||||
}
|
||||
return staticLayout.getHeight();
|
||||
}
|
||||
|
||||
public float getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public void setOffset(float offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
void setSecondText(String secondText) {
|
||||
this.secondText = secondText;
|
||||
}
|
||||
|
||||
private String getShowText() {
|
||||
if (!TextUtils.isEmpty(secondText)) {
|
||||
return text + "\n" + secondText;
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(LrcEntry entry) {
|
||||
if (entry == null) {
|
||||
return -1;
|
||||
}
|
||||
return (int) (time - entry.getTime());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* Copyright (C) 2017 wangchenyan
|
||||
*
|
||||
* 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 code.name.monkey.retromusic.lyrics;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 工具类
|
||||
*/
|
||||
class LrcUtils {
|
||||
private static final Pattern PATTERN_LINE = Pattern.compile("((\\[\\d\\d:\\d\\d\\.\\d{2,3}\\])+)(.+)");
|
||||
private static final Pattern PATTERN_TIME = Pattern.compile("\\[(\\d\\d):(\\d\\d)\\.(\\d{2,3})\\]");
|
||||
|
||||
/**
|
||||
* 从文件解析双语歌词
|
||||
*/
|
||||
static List<LrcEntry> parseLrc(File[] lrcFiles) {
|
||||
if (lrcFiles == null || lrcFiles.length != 2 || lrcFiles[0] == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File mainLrcFile = lrcFiles[0];
|
||||
File secondLrcFile = lrcFiles[1];
|
||||
List<LrcEntry> mainEntryList = parseLrc(mainLrcFile);
|
||||
List<LrcEntry> secondEntryList = parseLrc(secondLrcFile);
|
||||
|
||||
if (mainEntryList != null && secondEntryList != null) {
|
||||
for (LrcEntry mainEntry : mainEntryList) {
|
||||
for (LrcEntry secondEntry : secondEntryList) {
|
||||
if (mainEntry.getTime() == secondEntry.getTime()) {
|
||||
mainEntry.setSecondText(secondEntry.getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return mainEntryList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件解析歌词
|
||||
*/
|
||||
private static List<LrcEntry> parseLrc(File lrcFile) {
|
||||
if (lrcFile == null || !lrcFile.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<LrcEntry> entryList = new ArrayList<>();
|
||||
try {
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(lrcFile), "utf-8"));
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
List<LrcEntry> list = parseLine(line);
|
||||
if (list != null && !list.isEmpty()) {
|
||||
entryList.addAll(list);
|
||||
}
|
||||
}
|
||||
br.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Collections.sort(entryList);
|
||||
return entryList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文本解析双语歌词
|
||||
*/
|
||||
static List<LrcEntry> parseLrc(String[] lrcTexts) {
|
||||
if (lrcTexts == null || lrcTexts.length != 2 || TextUtils.isEmpty(lrcTexts[0])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String mainLrcText = lrcTexts[0];
|
||||
String secondLrcText = lrcTexts[1];
|
||||
List<LrcEntry> mainEntryList = parseLrc(mainLrcText);
|
||||
List<LrcEntry> secondEntryList = parseLrc(secondLrcText);
|
||||
|
||||
if (mainEntryList != null && secondEntryList != null) {
|
||||
for (LrcEntry mainEntry : mainEntryList) {
|
||||
for (LrcEntry secondEntry : secondEntryList) {
|
||||
if (mainEntry.getTime() == secondEntry.getTime()) {
|
||||
mainEntry.setSecondText(secondEntry.getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return mainEntryList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文本解析歌词
|
||||
*/
|
||||
private static List<LrcEntry> parseLrc(String lrcText) {
|
||||
if (TextUtils.isEmpty(lrcText)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (lrcText.startsWith("\uFEFF")) {
|
||||
lrcText = lrcText.replace("\uFEFF", "");
|
||||
}
|
||||
|
||||
List<LrcEntry> entryList = new ArrayList<>();
|
||||
String[] array = lrcText.split("\\n");
|
||||
for (String line : array) {
|
||||
List<LrcEntry> list = parseLine(line);
|
||||
if (list != null && !list.isEmpty()) {
|
||||
entryList.addAll(list);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(entryList);
|
||||
return entryList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网络文本,需要在工作线程中执行
|
||||
*/
|
||||
static String getContentFromNetwork(String url, String charset) {
|
||||
String lrcText = null;
|
||||
try {
|
||||
URL _url = new URL(url);
|
||||
HttpURLConnection conn = (HttpURLConnection) _url.openConnection();
|
||||
conn.setRequestMethod("GET");
|
||||
conn.setConnectTimeout(10000);
|
||||
conn.setReadTimeout(10000);
|
||||
if (conn.getResponseCode() == 200) {
|
||||
InputStream is = conn.getInputStream();
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
while ((len = is.read(buffer)) != -1) {
|
||||
bos.write(buffer, 0, len);
|
||||
}
|
||||
is.close();
|
||||
bos.close();
|
||||
lrcText = bos.toString(charset);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return lrcText;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析一行歌词
|
||||
*/
|
||||
private static List<LrcEntry> parseLine(String line) {
|
||||
if (TextUtils.isEmpty(line)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
line = line.trim();
|
||||
// [00:17.65]让我掉下眼泪的
|
||||
Matcher lineMatcher = PATTERN_LINE.matcher(line);
|
||||
if (!lineMatcher.matches()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String times = lineMatcher.group(1);
|
||||
String text = lineMatcher.group(3);
|
||||
List<LrcEntry> entryList = new ArrayList<>();
|
||||
|
||||
// [00:17.65]
|
||||
Matcher timeMatcher = PATTERN_TIME.matcher(times);
|
||||
while (timeMatcher.find()) {
|
||||
long min = Long.parseLong(timeMatcher.group(1));
|
||||
long sec = Long.parseLong(timeMatcher.group(2));
|
||||
String milString = timeMatcher.group(3);
|
||||
long mil = Long.parseLong(milString);
|
||||
// 如果毫秒是两位数,需要乘以10
|
||||
if (milString.length() == 2) {
|
||||
mil = mil * 10;
|
||||
}
|
||||
long time = min * DateUtils.MINUTE_IN_MILLIS + sec * DateUtils.SECOND_IN_MILLIS + mil;
|
||||
entryList.add(new LrcEntry(time, text));
|
||||
}
|
||||
return entryList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为[分:秒]
|
||||
*/
|
||||
static String formatTime(long milli) {
|
||||
int m = (int) (milli / DateUtils.MINUTE_IN_MILLIS);
|
||||
int s = (int) ((milli / DateUtils.SECOND_IN_MILLIS) % 60);
|
||||
String mm = String.format(Locale.getDefault(), "%02d", m);
|
||||
String ss = String.format(Locale.getDefault(), "%02d", s);
|
||||
return mm + ":" + ss;
|
||||
}
|
||||
|
||||
static void resetDurationScale() {
|
||||
try {
|
||||
Field mField = ValueAnimator.class.getDeclaredField("sDurationScale");
|
||||
mField.setAccessible(true);
|
||||
mField.setFloat(null, 1);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue