From e7c02abeaed1322b9cccd02ab889335c155e8408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E5=B7=A5?= Date: Tue, 22 Oct 2019 16:54:57 +0800 Subject: [PATCH] fix: https://github.com/wangchenyan/lrcview/issues/37 --- README.md | 21 ++- gradle.properties | 4 +- lrcview/build.gradle | 5 + lrcview/src/main/AndroidManifest.xml | 7 +- .../main/java/me/wcy/lrcview/LrcUtils.java | 27 +-- .../src/main/java/me/wcy/lrcview/LrcView.java | 175 +++++++++--------- sample/build.gradle | 5 + sample/src/main/AndroidManifest.xml | 6 +- .../me/wcy/lrcviewsample/MainActivity.java | 58 +++--- 9 files changed, 158 insertions(+), 150 deletions(-) diff --git a/README.md b/README.md index 2133628..1d96eaa 100644 --- a/README.md +++ b/README.md @@ -76,17 +76,18 @@ implementation 'me.wcy:lrcview:latestVersion' ## 方法 | 方法 | 描述 | | ---- | ---- | -| loadLrc(File) | 加载歌词文件 | -| loadLrc(File, File) | 加载双语歌词文件,两种语言的歌词时间戳需要一致 | -| loadLrc(String) | 加载歌词文本 | -| loadLrc(String, String) | 加载双语歌词文本,两种语言的歌词时间戳需要一致 | -| loadLrcByUrl(String) | 加载在线歌词文本 | +| loadLrc(File lrcFile) | 加载歌词文件 | +| loadLrc(File mainLrcFile, File secondLrcFile) | 加载双语歌词文件,两种语言的歌词时间戳需要一致 | +| loadLrc(String lrcText) | 加载歌词文本 | +| loadLrc(String mainLrcText, String secondLrcText) | 加载双语歌词文本,两种语言的歌词时间戳需要一致 | +| loadLrcByUrl(String lrcUrl) | 加载在线歌词文本,默认使用 utf-8 编码 | +| loadLrcByUrl(String lrcUrl, String charset) | 加载在线歌词文本 | | hasLrc() | 歌词是否有效 | -| setLabel(String) | 设置歌词为空时视图中央显示的文字,如“暂无歌词” | -| updateTime(long) | 刷新歌词 | -| ~~onDrag(long)~~ | ~~将歌词滚动到指定时间。已弃用,请使用 updateTime(long) 代替~~ | -| ~~setOnPlayClickListener(OnPlayClickListener)~~ | ~~设置拖动歌词时,播放按钮点击监听器。如果为非 null ,则激活歌词拖动功能,否则将将禁用歌词拖动功能。已弃用,请使用 setDraggable 代替~~ | -| setDraggable(Boolean, OnPlayClickListener) | 设置歌词是否允许拖动。如果允许拖动,则 OnPlayClickListener 不能为 null | +| setLabel(String label) | 设置歌词为空时视图中央显示的文字,如“暂无歌词” | +| updateTime(long time) | 刷新歌词 | +| ~~onDrag(long time)~~ | ~~将歌词滚动到指定时间。已弃用,请使用 updateTime(long) 代替~~ | +| ~~setOnPlayClickListener(OnPlayClickListener onPlayClickListener)~~ | ~~设置拖动歌词时,播放按钮点击监听器。如果为非 null ,则激活歌词拖动功能,否则将将禁用歌词拖动功能。已弃用,请使用 setDraggable 代替~~ | +| setDraggable(Boolean draggable, OnPlayClickListener onPlayClickListener) | 设置歌词是否允许拖动。如果允许拖动,则 OnPlayClickListener 不能为 null | | setNormalColor | 设置非当前行歌词字体颜色 | | setCurrentColor | 设置当前行歌词字体颜色 | | setTimelineTextColor | 设置拖动歌词时选中歌词的字体颜色 | diff --git a/gradle.properties b/gradle.properties index 7b8bb40..9c557ad 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,5 +16,5 @@ COMPILE_SDK_VERSION=26 BUILD_TOOLS_VERSION=26.0.3 MIN_SDK_VERSION=14 TARGET_SDK_VERSION=22 -VERSION_CODE=211 -VERSION_NAME=2.1.1 \ No newline at end of file +VERSION_CODE=212 +VERSION_NAME=2.1.2 \ No newline at end of file diff --git a/lrcview/build.gradle b/lrcview/build.gradle index c980970..666f200 100644 --- a/lrcview/build.gradle +++ b/lrcview/build.gradle @@ -20,6 +20,11 @@ android { versionCode Integer.parseInt(VERSION_CODE) versionName VERSION_NAME } + + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } } apply from: 'jcenter.gradle' diff --git a/lrcview/src/main/AndroidManifest.xml b/lrcview/src/main/AndroidManifest.xml index c43efca..124dd0c 100644 --- a/lrcview/src/main/AndroidManifest.xml +++ b/lrcview/src/main/AndroidManifest.xml @@ -1 +1,6 @@ - + + + + + diff --git a/lrcview/src/main/java/me/wcy/lrcview/LrcUtils.java b/lrcview/src/main/java/me/wcy/lrcview/LrcUtils.java index 439bbf7..392d7a3 100644 --- a/lrcview/src/main/java/me/wcy/lrcview/LrcUtils.java +++ b/lrcview/src/main/java/me/wcy/lrcview/LrcUtils.java @@ -19,6 +19,7 @@ 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; @@ -42,7 +43,7 @@ class LrcUtils { /** * 从文件解析双语歌词 */ - public static List parseLrc(File[] lrcFiles) { + static List parseLrc(File[] lrcFiles) { if (lrcFiles == null || lrcFiles.length != 2 || lrcFiles[0] == null) { return null; } @@ -94,7 +95,7 @@ private static List parseLrc(File lrcFile) { /** * 从文本解析双语歌词 */ - public static List parseLrc(String[] lrcTexts) { + static List parseLrc(String[] lrcTexts) { if (lrcTexts == null || lrcTexts.length != 2 || TextUtils.isEmpty(lrcTexts[0])) { return null; } @@ -140,23 +141,27 @@ private static List parseLrc(String lrcText) { /** * 获取网络文本,需要在工作线程中执行 */ - public static String getContentFromNetwork(String url) { + 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(); - int size = conn.getContentLength(); - byte[] buffer = new byte[size]; - is.read(buffer); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len; + while ((len = is.read(buffer)) != -1) { + bos.write(buffer, 0, len); + } is.close(); - lrcText = new String(buffer); + bos.close(); + lrcText = bos.toString(charset); } - } catch (IOException e) { + } catch (Exception e) { e.printStackTrace(); } return lrcText; @@ -201,7 +206,7 @@ private static List parseLine(String line) { /** * 转为[分:秒] */ - public static String formatTime(long milli) { + 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); @@ -209,7 +214,7 @@ public static String formatTime(long milli) { return mm + ":" + ss; } - public static void resetDurationScale() { + static void resetDurationScale() { try { Field mField = ValueAnimator.class.getDeclaredField("sDurationScale"); mField.setAccessible(true); diff --git a/lrcview/src/main/java/me/wcy/lrcview/LrcView.java b/lrcview/src/main/java/me/wcy/lrcview/LrcView.java index 312aadb..08d5ff0 100644 --- a/lrcview/src/main/java/me/wcy/lrcview/LrcView.java +++ b/lrcview/src/main/java/me/wcy/lrcview/LrcView.java @@ -44,6 +44,7 @@ * 歌词 * Created by wcy on 2015/11/9. */ +@SuppressLint("StaticFieldLeak") public class LrcView extends View { private static final long ADJUST_DURATION = 100; private static final long TIMELINE_KEEP_TIME = 4 * DateUtils.SECOND_IN_MILLIS; @@ -235,13 +236,10 @@ public void setOnPlayClickListener(OnPlayClickListener onPlayClickListener) { /** * 设置歌词为空时屏幕中央显示的文字,如“暂无歌词” */ - public void setLabel(final String label) { - runOnUi(new Runnable() { - @Override - public void run() { - mDefaultLabel = label; - invalidate(); - } + public void setLabel(String label) { + runOnUi(() -> { + mDefaultLabel = label; + invalidate(); }); } @@ -250,7 +248,7 @@ public void run() { * * @param lrcFile 歌词文件 */ - public void loadLrc(final File lrcFile) { + public void loadLrc(File lrcFile) { loadLrc(lrcFile, null); } @@ -260,35 +258,31 @@ public void loadLrc(final File lrcFile) { * @param mainLrcFile 第一种语言歌词文件 * @param secondLrcFile 第二种语言歌词文件 */ - public void loadLrc(final File mainLrcFile, final File secondLrcFile) { - runOnUi(new Runnable() { - @SuppressLint("StaticFieldLeak") - @Override - public void run() { - reset(); - - StringBuilder sb = new StringBuilder("file://"); - sb.append(mainLrcFile.getPath()); - if (secondLrcFile != null) { - sb.append("#").append(secondLrcFile.getPath()); + public void loadLrc(File mainLrcFile, File secondLrcFile) { + runOnUi(() -> { + reset(); + + StringBuilder sb = new StringBuilder("file://"); + sb.append(mainLrcFile.getPath()); + if (secondLrcFile != null) { + sb.append("#").append(secondLrcFile.getPath()); + } + String flag = sb.toString(); + setFlag(flag); + new AsyncTask>() { + @Override + protected List doInBackground(File... params) { + return LrcUtils.parseLrc(params); } - final String flag = sb.toString(); - setFlag(flag); - new AsyncTask>() { - @Override - protected List doInBackground(File... params) { - return LrcUtils.parseLrc(params); - } - @Override - protected void onPostExecute(List lrcEntries) { - if (getFlag() == flag) { - onLrcLoaded(lrcEntries); - setFlag(null); - } + @Override + protected void onPostExecute(List lrcEntries) { + if (getFlag() == flag) { + onLrcLoaded(lrcEntries); + setFlag(null); } - }.execute(mainLrcFile, secondLrcFile); - } + } + }.execute(mainLrcFile, secondLrcFile); }); } @@ -297,7 +291,7 @@ protected void onPostExecute(List lrcEntries) { * * @param lrcText 歌词文本 */ - public void loadLrc(final String lrcText) { + public void loadLrc(String lrcText) { loadLrc(lrcText, null); } @@ -307,51 +301,56 @@ public void loadLrc(final String lrcText) { * @param mainLrcText 第一种语言歌词文本 * @param secondLrcText 第二种语言歌词文本 */ - public void loadLrc(final String mainLrcText, final String secondLrcText) { - runOnUi(new Runnable() { - @SuppressLint("StaticFieldLeak") - @Override - public void run() { - reset(); - - StringBuilder sb = new StringBuilder("file://"); - sb.append(mainLrcText); - if (secondLrcText != null) { - sb.append("#").append(secondLrcText); + public void loadLrc(String mainLrcText, String secondLrcText) { + runOnUi(() -> { + reset(); + + StringBuilder sb = new StringBuilder("file://"); + sb.append(mainLrcText); + if (secondLrcText != null) { + sb.append("#").append(secondLrcText); + } + String flag = sb.toString(); + setFlag(flag); + new AsyncTask>() { + @Override + protected List doInBackground(String... params) { + return LrcUtils.parseLrc(params); } - final String flag = sb.toString(); - setFlag(flag); - new AsyncTask>() { - @Override - protected List doInBackground(String... params) { - return LrcUtils.parseLrc(params); - } - @Override - protected void onPostExecute(List lrcEntries) { - if (getFlag() == flag) { - onLrcLoaded(lrcEntries); - setFlag(null); - } + @Override + protected void onPostExecute(List lrcEntries) { + if (getFlag() == flag) { + onLrcLoaded(lrcEntries); + setFlag(null); } - }.execute(mainLrcText, secondLrcText); - } + } + }.execute(mainLrcText, secondLrcText); }); } /** - * 加载在线歌词 + * 加载在线歌词,默认使用 utf-8 编码 * * @param lrcUrl 歌词文件的网络地址 */ - @SuppressLint("StaticFieldLeak") - public void loadLrcByUrl(final String lrcUrl) { - final String flag = "url://" + lrcUrl; + public void loadLrcByUrl(String lrcUrl) { + loadLrcByUrl(lrcUrl, "utf-8"); + } + + /** + * 加载在线歌词 + * + * @param lrcUrl 歌词文件的网络地址 + * @param charset 编码格式 + */ + public void loadLrcByUrl(String lrcUrl, String charset) { + String flag = "url://" + lrcUrl; setFlag(flag); new AsyncTask() { @Override protected String doInBackground(String... params) { - return LrcUtils.getContentFromNetwork(params[0]); + return LrcUtils.getContentFromNetwork(params[0], params[1]); } @Override @@ -360,7 +359,7 @@ protected void onPostExecute(String lrcText) { loadLrc(lrcText); } } - }.execute(lrcUrl); + }.execute(lrcUrl, charset); } /** @@ -377,22 +376,19 @@ public boolean hasLrc() { * * @param time 当前播放时间 */ - public void updateTime(final long time) { - runOnUi(new Runnable() { - @Override - public void run() { - if (!hasLrc()) { - return; - } + public void updateTime(long time) { + runOnUi(() -> { + if (!hasLrc()) { + return; + } - int line = findShowLine(time); - if (line != mCurrentLine) { - mCurrentLine = line; - if (!isShowTimeline) { - smoothScrollTo(line); - } else { - invalidate(); - } + int line = findShowLine(time); + if (line != mCurrentLine) { + mCurrentLine = line; + if (!isShowTimeline) { + smoothScrollTo(line); + } else { + invalidate(); } } }); @@ -457,7 +453,7 @@ protected void onDraw(Canvas canvas) { float y = 0; for (int i = 0; i < mLrcEntryList.size(); i++) { if (i > 0) { - y += (mLrcEntryList.get(i - 1).getHeight() + mLrcEntryList.get(i).getHeight()) / 2 + mDividerHeight; + y += ((mLrcEntryList.get(i - 1).getHeight() + mLrcEntryList.get(i).getHeight()) >> 1) + mDividerHeight; } if (i == mCurrentLine) { mLrcPaint.setTextSize(mCurrentTextSize); @@ -479,7 +475,7 @@ protected void onDraw(Canvas canvas) { */ private void drawText(Canvas canvas, StaticLayout staticLayout, float y) { canvas.save(); - canvas.translate(mLrcPadding, y - staticLayout.getHeight() / 2); + canvas.translate(mLrcPadding, y - (staticLayout.getHeight() >> 1)); staticLayout.draw(canvas); canvas.restore(); } @@ -653,12 +649,9 @@ private void smoothScrollTo(int line, long duration) { mAnimator = ValueAnimator.ofFloat(mOffset, offset); mAnimator.setDuration(duration); mAnimator.setInterpolator(new LinearInterpolator()); - mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - mOffset = (float) animation.getAnimatedValue(); - invalidate(); - } + mAnimator.addUpdateListener(animation -> { + mOffset = (float) animation.getAnimatedValue(); + invalidate(); }); LrcUtils.resetDurationScale(); mAnimator.start(); @@ -720,7 +713,7 @@ private float getOffset(int line) { if (mLrcEntryList.get(line).getOffset() == Float.MIN_VALUE) { float offset = getHeight() / 2; for (int i = 1; i <= line; i++) { - offset -= (mLrcEntryList.get(i - 1).getHeight() + mLrcEntryList.get(i).getHeight()) / 2 + mDividerHeight; + offset -= ((mLrcEntryList.get(i - 1).getHeight() + mLrcEntryList.get(i).getHeight()) >> 1) + mDividerHeight; } mLrcEntryList.get(line).setOffset(offset); } diff --git a/sample/build.gradle b/sample/build.gradle index 540dda4..6a35b22 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -18,6 +18,11 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } } dependencies { diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 0cbecf6..6d6e64f 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -1,14 +1,14 @@ - - + android:theme="@style/AppTheme" + tools:ignore="GoogleAppIndexingWarning"> diff --git a/sample/src/main/java/me/wcy/lrcviewsample/MainActivity.java b/sample/src/main/java/me/wcy/lrcviewsample/MainActivity.java index 3834794..396c2b3 100644 --- a/sample/src/main/java/me/wcy/lrcviewsample/MainActivity.java +++ b/sample/src/main/java/me/wcy/lrcviewsample/MainActivity.java @@ -5,7 +5,6 @@ import android.os.Bundle; import android.os.Handler; import android.support.v7.app.AppCompatActivity; -import android.view.View; import android.widget.Button; import android.widget.SeekBar; @@ -35,53 +34,48 @@ protected void onCreate(Bundle savedInstanceState) { AssetFileDescriptor fileDescriptor = getAssets().openFd("send_it.m4a"); mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(), fileDescriptor.getStartOffset(), fileDescriptor.getLength()); mediaPlayer.prepareAsync(); - mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { - @Override - public void onPrepared(MediaPlayer mp) { - seekBar.setMax(mediaPlayer.getDuration()); - seekBar.setProgress(0); - } + mediaPlayer.setOnPreparedListener(mp -> { + seekBar.setMax(mediaPlayer.getDuration()); + seekBar.setProgress(0); }); - mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { - @Override - public void onCompletion(MediaPlayer mp) { - lrcView.updateTime(0); - seekBar.setProgress(0); - } + mediaPlayer.setOnCompletionListener(mp -> { + lrcView.updateTime(0); + seekBar.setProgress(0); }); } catch (IOException e) { e.printStackTrace(); } + // 加载歌词文本 String mainLrcText = getLrcText("send_it_en.lrc"); String secondLrcText = getLrcText("send_it_cn.lrc"); lrcView.loadLrc(mainLrcText, secondLrcText); + + // 加载歌词文件 // File mainLrcFile = new File("/sdcard/Download/send_it_cn.lrc"); // File secondLrcFile = new File("/sdcard/Download/send_it_en.lrc"); // lrcView.loadLrc(mainLrcFile, secondLrcFile); - lrcView.setDraggable(true, new LrcView.OnPlayClickListener() { - @Override - public boolean onPlayClick(long time) { - mediaPlayer.seekTo((int) time); - if (!mediaPlayer.isPlaying()) { - mediaPlayer.start(); - handler.post(runnable); - } - return true; + // 加载在线歌词 + // String url = "http://pz6twp8s0.bkt.clouddn.com/%E6%AD%8C%E8%AF%8D.txt"; + // lrcView.loadLrcByUrl(url, "gb2312"); + + lrcView.setDraggable(true, time -> { + mediaPlayer.seekTo((int) time); + if (!mediaPlayer.isPlaying()) { + mediaPlayer.start(); + handler.post(runnable); } + return true; }); - btnPlayPause.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (!mediaPlayer.isPlaying()) { - mediaPlayer.start(); - handler.post(runnable); - } else { - mediaPlayer.pause(); - handler.removeCallbacks(runnable); - } + btnPlayPause.setOnClickListener(v -> { + if (!mediaPlayer.isPlaying()) { + mediaPlayer.start(); + handler.post(runnable); + } else { + mediaPlayer.pause(); + handler.removeCallbacks(runnable); } });