From 90564a274c3fbe312b42a56c9bf1fac2793c6961 Mon Sep 17 00:00:00 2001 From: lihongwei Date: Mon, 30 Sep 2024 10:49:55 +0800 Subject: [PATCH] =?UTF-8?q?A=E9=9D=A2=E5=89=8D=E5=8F=B0=E6=92=AD=E6=94=BE?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../player/adapter/A_InstrumentAdapter.java | 10 +- .../music/player/adapter/A_NatureAdapter.java | 9 +- .../music/player/adapter/A_WhiteAdapter.java | 9 +- .../service/MusicPlayerForegroundService.java | 227 ++++++++++++------ .../player/ui/activity/A_HomeActivity.java | 171 +++++++++---- .../player/ui/activity/A_PlayActivity.java | 130 ++++++---- .../ui/activity/viewmodel/A_VMPlay.java | 120 +-------- app/src/main/res/drawable/pause.xml | 18 +- app/src/main/res/drawable/pause_flat.xml | 10 +- app/src/main/res/drawable/pause_shadow.xml | 18 +- app/src/main/res/drawable/play.xml | 18 +- .../drawable/{pause_big.xml => play_big.xml} | 0 app/src/main/res/drawable/play_flat.xml | 10 +- app/src/main/res/drawable/play_shadow.xml | 18 +- app/src/main/res/layout/activity_ahome.xml | 7 +- app/src/main/res/layout/item_a_home_1.xml | 2 +- app/src/main/res/layout/item_a_home_3.xml | 2 +- 17 files changed, 450 insertions(+), 329 deletions(-) rename app/src/main/res/drawable/{pause_big.xml => play_big.xml} (100%) diff --git a/app/src/main/java/com/hi/music/player/adapter/A_InstrumentAdapter.java b/app/src/main/java/com/hi/music/player/adapter/A_InstrumentAdapter.java index 28f6f3c..0e043fd 100644 --- a/app/src/main/java/com/hi/music/player/adapter/A_InstrumentAdapter.java +++ b/app/src/main/java/com/hi/music/player/adapter/A_InstrumentAdapter.java @@ -7,6 +7,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.DiffUtil; @@ -43,6 +44,9 @@ public class A_InstrumentAdapter extends RecyclerView.Adapter isPlaying = new MutableLiveData<>(false); + private MediaPlayer mediaPlayer; // MediaPlayer实例,用于播放音频 + private final IBinder binder = new MusicBinder(); // 用于绑定服务的Binder + private final MutableLiveData isPlaying = new MutableLiveData<>(false); // 用于跟踪播放状态的LiveData + private final MutableLiveData fileName = new MutableLiveData<>(); // 保存当前播放的文件名 + private String currentAudioPath = null; // 用于保存当前正在播放的音频路径 + // 自定义Binder类,允许客户端绑定到服务 public class MusicBinder extends Binder { public MusicPlayerForegroundService getService() { - return MusicPlayerForegroundService.this; + return MusicPlayerForegroundService.this; // 返回当前服务实例 } } @Nullable @Override public IBinder onBind(Intent intent) { - return binder; + return binder; // 返回用于绑定服务的Binder } + // 当服务启动时调用该方法 @Override public int onStartCommand(Intent intent, int flags, int startId) { - String audioPath = intent.getStringExtra("Path"); - if (audioPath != null) { - initializePlayer(audioPath); + // 从Intent中获取传入的音频路径 + String newAudioPath = intent.getStringExtra("Path"); + + if (newAudioPath != null) { + // 如果传入的音频路径与当前播放路径不同,则重新初始化播放器 + if (currentAudioPath == null || !newAudioPath.equals(currentAudioPath)) { + Log.d("MusicPlayerService", "检测到新的音频路径,初始化播放器"); + currentAudioPath = newAudioPath; // 更新当前播放路径 + stopAndResetMediaPlayer(); // 停止并重置当前的播放器状态 + initializePlayer(newAudioPath); // 初始化播放器并开始播放新的音频 + } else { + Log.d("MusicPlayerService", "音频路径相同,无需重新初始化播放器"); + } + } else { + stopSelf(); // 如果路径为空,停止服务 } - startForeground(1, createNotification("Playing Audio", "Your audio is playing")); - - return START_NOT_STICKY; + // 启动前台服务并显示通知 + startForeground(1, createNotification()); + return START_NOT_STICKY; // 返回START_NOT_STICKY,表示服务不会在被系统杀死后自动重启 } - private Notification createNotification(String title, String content) { - createNotificationChannel(); - Intent notificationIntent = new Intent(this, A_PlayActivity.class); - int flags = PendingIntent.FLAG_UPDATE_CURRENT; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - flags |= PendingIntent.FLAG_IMMUTABLE; - } - PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, flags); + // 创建通知,用于前台服务显示 + private Notification createNotification() { + createNotificationChannel(); // 创建通知渠道(适用于Android O及以上版本) + // 创建点击通知时打开的Intent + Intent notificationIntent = new Intent(this, A_PlayActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, + Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_IMMUTABLE : 0); // 适配Android 12 + + // 构建通知,设置标题、内容和小图标 return new NotificationCompat.Builder(this, CHANNEL_ID) - .setContentTitle(title) - .setContentText(content) - .setSmallIcon(R.drawable.home_select) - .setContentIntent(pendingIntent) - .setPriority(NotificationCompat.PRIORITY_LOW) + .setContentTitle("Playing Audio") // 通知标题 + .setContentText("Your audio is playing") // 通知内容 + .setSmallIcon(R.drawable.home_select) // 通知的小图标 + .setContentIntent(pendingIntent) // 点击通知后的Intent + .setPriority(NotificationCompat.PRIORITY_LOW) // 低优先级,避免干扰用户 .build(); } + // 创建通知渠道,适用于Android O及以上版本 private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel serviceChannel = new NotificationChannel( CHANNEL_ID, "Music Player Channel", NotificationManager.IMPORTANCE_LOW); NotificationManager manager = getSystemService(NotificationManager.class); - manager.createNotificationChannel(serviceChannel); + if (manager != null) { + manager.createNotificationChannel(serviceChannel); // 创建通知渠道 + } } } + // 停止并重置播放器,确保在播放新的音频时播放器状态正确 + private void stopAndResetMediaPlayer() { + if (mediaPlayer != null) { + if (mediaPlayer.isPlaying()) { + mediaPlayer.stop(); // 停止正在播放的音频 + } + mediaPlayer.reset(); // 重置播放器状态,使其可以播放新的音频 + } + } + + // 初始化媒体播放器并设置数据源 private void initializePlayer(String path) { try { - Log.d("MediaPlayerError","path: "+path); + Log.d("MusicPlayerService", "正在初始化播放器,音频路径: " + path); if (mediaPlayer == null) { - mediaPlayer = new MediaPlayer(); - } else { - mediaPlayer.reset(); + mediaPlayer = new MediaPlayer(); // 如果MediaPlayer为空,则创建一个新的实例 } - if (path.startsWith("content://")) { - Uri contentUri = Uri.parse(path); - ContentResolver contentResolver = getContentResolver(); - AssetFileDescriptor afd = contentResolver.openAssetFileDescriptor(contentUri, "r"); - if (afd != null) { - mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); - afd.close(); - } - } else { - AssetFileDescriptor afd = getAssets().openFd(path); - mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); - afd.close(); - } + // 设置当前播放的文件名 + fileName.setValue(getFileNameFromPath(path)); - mediaPlayer.prepare(); - mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start()); - isPlaying.postValue(true); - } catch (IOException e) { - Log.e("MediaPlayerError", "Could not open file: " + path, e); + // 根据传入的音频路径类型设置数据源 + setDataSource(path); + + mediaPlayer.prepareAsync(); // 异步准备播放器 + mediaPlayer.setOnPreparedListener(mp -> { + mediaPlayer.start(); // 播放器准备好后开始播放 + isPlaying.postValue(true); // 更新播放状态为“播放中” + }); + + // 设置播放完成后的监听器 + mediaPlayer.setOnCompletionListener(mp -> { + isPlaying.postValue(false); // 更新播放状态为“未播放” + stopSelf(); // 播放完成后停止服务 + }); + + // 设置播放错误的监听器 + mediaPlayer.setOnErrorListener((mp, what, extra) -> { + Log.e("MusicPlayerService", "播放时发生错误: " + what + ", 额外信息: " + extra); + isPlaying.postValue(false); // 更新播放状态为“未播放” + stopSelf(); // 播放出错后停止服务 + return true; // 返回true表示错误已经被处理 + }); + + } catch (IOException | IllegalArgumentException | IllegalStateException e) { + Log.e("MusicPlayerService", "初始化播放器失败,路径: " + path, e); + isPlaying.postValue(false); // 如果初始化失败,更新状态为“未播放” + stopSelf(); // 初始化失败后停止服务 } } + // 设置数据源,根据音频路径的类型来加载音频 + private void setDataSource(String path) throws IOException { + if (path.startsWith("content://")) { + setDataSourceFromContentUri(path); // 如果是内容URI,使用内容URI加载 + } else { + setDataSourceFromFileOrAsset(path); // 否则尝试从文件或资产目录中加载 + } + } + + // 使用内容URI加载音频数据源 + private void setDataSourceFromContentUri(String path) throws IOException { + Uri contentUri = Uri.parse(path); // 将字符串路径解析为URI + try (AssetFileDescriptor afd = getContentResolver().openAssetFileDescriptor(contentUri, "r")) { + if (afd != null) { + // 使用文件描述符和偏移量设置MediaPlayer的数据源 + mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); + } else { + throw new IOException("无法打开内容URI: " + path); + } + } + } + + // 尝试从文件或assets目录加载音频数据源 + private void setDataSourceFromFileOrAsset(String path) throws IOException { + try { + // 尝试从assets目录加载音频 + AssetFileDescriptor afd = getAssets().openFd(path); + mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); + } catch (IOException e) { + // 如果assets加载失败,则尝试从文件系统加载音频 + File file = new File(path); + if (file.exists()) { + mediaPlayer.setDataSource(file.getAbsolutePath()); // 从文件路径加载 + } else { + throw new IOException("文件未找到: " + path); + } + } + } + + // 根据路径获取文件名 + private String getFileNameFromPath(String path) { + try { + return new File(path).getName(); // 从路径中提取文件名 + } catch (Exception e) { + Log.e("MusicPlayerService", "获取文件名失败", e); + return "未知文件"; // 如果失败,返回默认文件名 + } + } + + // 暂停音频播放 public void pauseAudio() { if (mediaPlayer != null && mediaPlayer.isPlaying()) { - mediaPlayer.pause(); - isPlaying.postValue(false); + mediaPlayer.pause(); // 如果当前正在播放,则暂停音频 + isPlaying.postValue(false); // 更新播放状态为“暂停” } } + // 恢复音频播放 public void resumeAudio() { if (mediaPlayer != null && !mediaPlayer.isPlaying()) { - mediaPlayer.start(); - isPlaying.postValue(true); + mediaPlayer.start(); // 如果当前未播放,则恢复音频播放 + isPlaying.postValue(true); // 更新播放状态为“播放中” } } + // 获取当前播放状态 public MutableLiveData getIsPlaying() { return isPlaying; } - // 添加获取当前播放位置的方法 + // 获取当前播放的文件名 + public MutableLiveData getFileName() { + return fileName; + } + + // 获取当前播放位置(毫秒) public int getCurrentPosition() { - if (mediaPlayer != null) { - return mediaPlayer.getCurrentPosition(); - } - return 0; // 如果 mediaPlayer 为 null,返回 0 + return mediaPlayer != null ? mediaPlayer.getCurrentPosition() : 0; } - // 添加获取总时长的方法 + // 获取音频总时长(毫秒) public int getDuration() { - if (mediaPlayer != null) { - return mediaPlayer.getDuration(); - } - return 0; // 如果 mediaPlayer 为 null,返回 0 + return mediaPlayer != null ? mediaPlayer.getDuration() : 0; } + // 跳转到指定播放位置 public void seekTo(int position) { if (mediaPlayer != null) { - mediaPlayer.seekTo(position); + mediaPlayer.seekTo(position); // 将播放器跳转到指定位置 } } + // 当服务销毁时释放MediaPlayer资源 @Override public void onDestroy() { super.onDestroy(); if (mediaPlayer != null) { - mediaPlayer.release(); - mediaPlayer = null; + mediaPlayer.release(); // 释放MediaPlayer资源 + mediaPlayer = null; // 将播放器设置为null,防止再次使用 } } } diff --git a/app/src/main/java/com/hi/music/player/ui/activity/A_HomeActivity.java b/app/src/main/java/com/hi/music/player/ui/activity/A_HomeActivity.java index 7fe4375..9fdf64d 100644 --- a/app/src/main/java/com/hi/music/player/ui/activity/A_HomeActivity.java +++ b/app/src/main/java/com/hi/music/player/ui/activity/A_HomeActivity.java @@ -1,35 +1,48 @@ package com.hi.music.player.ui.activity; -import android.util.Log; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; import android.view.LayoutInflater; import android.view.View; -import androidx.lifecycle.ViewModelProvider; - import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayoutMediator; import com.hi.music.player.R; import com.hi.music.player.adapter.A_HomeViewPagerAdapter; import com.hi.music.player.databinding.ActivityAhomeBinding; import com.hi.music.player.databinding.HomeTabCustomBinding; -import com.hi.music.player.helper.CircularProgressBar; -import com.hi.music.player.ui.activity.viewmodel.A_VMPlay; +import com.hi.music.player.service.MusicPlayerForegroundService; -import java.util.Objects; +import java.util.Locale; public class A_HomeActivity extends BaseActivity { - private final int[] defaultIcons = { - R.drawable.home_unselect, - R.drawable.import_unselect, - }; + private final int[] defaultIcons = { R.drawable.home_unselect, R.drawable.import_unselect }; + private final int[] selectedIcons = { R.drawable.home_select, R.drawable.import_select }; - private final int[] selectedIcons = { - R.drawable.home_select, - R.drawable.import_select, - }; + private MusicPlayerForegroundService musicService; + private boolean isBound = false; + private Handler progressHandler; - private A_VMPlay viewModel; + private final ServiceConnection serviceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + musicService = ((MusicPlayerForegroundService.MusicBinder) service).getService(); + isBound = true; + setupObservers(); + startUpdatingProgress(); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + isBound = false; + } + }; @Override protected ActivityAhomeBinding getViewBinding() { @@ -38,9 +51,10 @@ public class A_HomeActivity extends BaseActivity { @Override protected void onCreateInit() { - viewModel = new ViewModelProvider(this).get(A_VMPlay.class); + Intent serviceIntent = new Intent(this, MusicPlayerForegroundService.class); + startService(serviceIntent); + bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE); initData(); - setupObservers(); } @Override @@ -50,7 +64,7 @@ public class A_HomeActivity extends BaseActivity { @Override public boolean isFullScreen() { - return true; + return false; } @Override @@ -58,11 +72,20 @@ public class A_HomeActivity extends BaseActivity { return false; } - public void initData() { + private void initData() { + setupViewPager(); + setupTabLayout(); + setupProgressBar(); + setupPauseButton(); + } + + private void setupViewPager() { A_HomeViewPagerAdapter adapter = new A_HomeViewPagerAdapter(this); vb.homeViewpager.setAdapter(adapter); vb.homeViewpager.setUserInputEnabled(false); + } + private void setupTabLayout() { new TabLayoutMediator(vb.homeTabLayout, vb.homeViewpager, (tab, position) -> { HomeTabCustomBinding tabBinding = HomeTabCustomBinding.inflate(LayoutInflater.from(this)); tab.setCustomView(tabBinding.getRoot()); @@ -81,60 +104,112 @@ public class A_HomeActivity extends BaseActivity { } @Override - public void onTabReselected(TabLayout.Tab tab) { - } + public void onTabReselected(TabLayout.Tab tab) {} }); + // 默认选择第一个标签 TabLayout.Tab firstTab = vb.homeTabLayout.getTabAt(0); if (firstTab != null) { firstTab.select(); updateTabIcon(firstTab, true); } + } - vb.circularProgressBar.setMaxProgress(100); // 假设最大值为100 - - vb.circularProgressBar.setOnProgressChangeListener(new CircularProgressBar.OnProgressChangeListener() { - @Override - public void onProgressChanged(CircularProgressBar progressBar, int progress) { - viewModel.seekTo(progress); // 当用户改变进度时,调用 ViewModel 方法 + private void setupProgressBar() { + vb.circularProgressBar.setMaxProgress(100); + vb.circularProgressBar.setOnProgressChangeListener((progressBar, progress) -> { + if (isBound && musicService != null) { + musicService.seekTo((int) ((progress / 100.0) * musicService.getDuration())); } }); + } + private void setupPauseButton() { vb.pause.setOnClickListener(v -> { - viewModel.togglePlay(); + if (isBound && musicService != null) { + if (Boolean.TRUE.equals(musicService.getIsPlaying().getValue())) { + musicService.pauseAudio(); + } else { + musicService.resumeAudio(); + } + } }); } private void setupObservers() { - viewModel.getCurrentTime().observe(this, currentTime -> { - vb.bottomText.setText(currentTime); - }); + if (musicService == null || isBound == false) return; // 确保只在服务绑定时设置观察者 - viewModel.getTotalTime().observe(this, totalTime -> { - vb.topText.setText(totalTime); - }); - - viewModel.getProgress().observe(this, progress -> { - if (progress >= 0 && progress <= 100) { - vb.circularProgressBar.setProgress(progress); // 更新进度条 - } else { - Log.e("A_HomeActivity", "Progress out of range: " + progress); + musicService.getIsPlaying().observe(this, isPlaying -> { + if (isPlaying != null) { + vb.pause.setImageResource(isPlaying ? R.drawable.pause : R.drawable.play); + if (isPlaying) { + startUpdatingProgress(); // 只有在播放时才开始更新进度 + } else { + stopUpdatingProgress(); // 暂停更新 + } } }); - - viewModel.isPlaying().observe(this, isPlaying -> { - vb.pause.setImageResource(isPlaying ? R.drawable.play : R.drawable.pause); - }); + } + + private void startUpdatingProgress() { + if (progressHandler != null) return; // 确保只有一个 Handler 在工作 + progressHandler = new Handler(Looper.getMainLooper()); + progressHandler.postDelayed(updateProgressRunnable, 1000); + } + + private void stopUpdatingProgress() { + if (progressHandler != null) { + progressHandler.removeCallbacks(updateProgressRunnable); + progressHandler = null; + } + } + + private final Runnable updateProgressRunnable = new Runnable() { + @Override + public void run() { + if (isBound && musicService != null && Boolean.TRUE.equals(musicService.getIsPlaying().getValue())) { + updateProgressAndDuration(); + progressHandler.postDelayed(this, 1000); + } + } + }; + + private void updateProgressAndDuration() { + int currentPosition = musicService.getCurrentPosition(); + int duration = musicService.getDuration(); + if (duration > 0) { + vb.circularProgressBar.setProgress((int) ((currentPosition / (float) duration) * 100)); + vb.bottomText.setText(formatTime(currentPosition)); + vb.topText.setText(formatTime(duration)); + } + } + + private String formatTime(int milliseconds) { + int minutes = (milliseconds / 1000) / 60; + int seconds = (milliseconds / 1000) % 60; + return String.format(Locale.getDefault(), "%d:%02d", minutes, seconds); } private void updateTabIcon(TabLayout.Tab tab, boolean isSelected) { - HomeTabCustomBinding tabBinding = HomeTabCustomBinding.bind(Objects.requireNonNull(tab.getCustomView())); - int position = tab.getPosition(); - tabBinding.homeIcon.setImageResource(isSelected ? selectedIcons[position] : defaultIcons[position]); + View customView = tab.getCustomView(); + if (customView != null) { + HomeTabCustomBinding tabBinding = HomeTabCustomBinding.bind(customView); + tabBinding.homeIcon.setImageResource(isSelected ? selectedIcons[tab.getPosition()] : defaultIcons[tab.getPosition()]); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (isBound) { + unbindService(serviceConnection); + isBound = false; + } + stopUpdatingProgress(); // 确保停止更新进度 } @Override public void onClick(View v) { - + // Handle additional click events if needed } } diff --git a/app/src/main/java/com/hi/music/player/ui/activity/A_PlayActivity.java b/app/src/main/java/com/hi/music/player/ui/activity/A_PlayActivity.java index 3c9357d..13d5b19 100644 --- a/app/src/main/java/com/hi/music/player/ui/activity/A_PlayActivity.java +++ b/app/src/main/java/com/hi/music/player/ui/activity/A_PlayActivity.java @@ -4,7 +4,9 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.util.Log; import android.view.View; @@ -13,12 +15,14 @@ import com.hi.music.player.databinding.ActivityAplayBinding; import com.hi.music.player.javabean.A_data.AudioItem; import com.hi.music.player.service.MusicPlayerForegroundService; +import java.lang.ref.WeakReference; import java.util.Locale; public class A_PlayActivity extends BaseActivity { private MusicPlayerForegroundService musicService; private boolean isBound = false; + private Handler progressHandler; private final ServiceConnection serviceConnection = new ServiceConnection() { @Override @@ -27,12 +31,10 @@ public class A_PlayActivity extends BaseActivity { musicService = binder.getService(); isBound = true; - // 观察服务中的播放状态 - musicService.getIsPlaying().observe(A_PlayActivity.this, isPlaying -> { - vb.playButton.setBackgroundResource(isPlaying ? R.drawable.play : R.drawable.pause); - }); + // 只设置一次观察者 + musicService.getIsPlaying().observe(A_PlayActivity.this, this::updatePlayButton); + musicService.getFileName().observe(A_PlayActivity.this, vb.songTitle::setText); - // 更新进度和总时长 startUpdatingProgress(); } @@ -40,6 +42,12 @@ public class A_PlayActivity extends BaseActivity { public void onServiceDisconnected(ComponentName name) { isBound = false; } + + private void updatePlayButton(Boolean isPlaying) { + if (isPlaying != null) { + vb.playButton.setBackgroundResource(isPlaying ? R.drawable.pause : R.drawable.play); + } + } }; @Override @@ -52,29 +60,13 @@ public class A_PlayActivity extends BaseActivity { Intent intent = getIntent(); AudioItem audioItem = (AudioItem) intent.getSerializableExtra("Path"); if (audioItem == null) { - finish(); // 如果没有音频项目,结束活动 + finish(); return; } - // 启动前台服务 - Intent serviceIntent = new Intent(this, MusicPlayerForegroundService.class); - serviceIntent.putExtra("Path", audioItem.getFile()); - Log.d("A_PlayActivity", "Path: " + audioItem.getFile()); - startService(serviceIntent); - bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE); - - vb.playButton.setOnClickListener(v -> { - if (isBound) { - if (musicService.getIsPlaying().getValue() != null && musicService.getIsPlaying().getValue()) { - musicService.pauseAudio(); - } else { - musicService.resumeAudio(); - } - } - }); - - // 启动定期更新播放进度 - startUpdatingProgress(); + startMusicService(audioItem.getFile()); + setupPlayButtonClickListener(); + vb.songSeekbar.setOnSeekBarChangeListener(new SeekBarListener()); } @Override @@ -82,25 +74,51 @@ public class A_PlayActivity extends BaseActivity { } - private void startUpdatingProgress() { - new Thread(() -> { - while (isBound && musicService != null) { - if (musicService.getIsPlaying().getValue() != null && musicService.getIsPlaying().getValue()) { - int currentPosition = musicService.getCurrentPosition(); // 获取当前播放位置 - int duration = musicService.getDuration(); // 获取总时长 - runOnUiThread(() -> { - vb.songSeekbar.setProgress((int) ((currentPosition / (float) duration) * 100)); // 更新进度条 - vb.current.setText(formatTime(currentPosition)); // 更新当前时间文本 - vb.time.setText(formatTime(duration)); // 更新总时间文本 - }); - } - try { - Thread.sleep(1000); // 每秒更新一次 - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); + private void startMusicService(String path) { + Intent serviceIntent = new Intent(this, MusicPlayerForegroundService.class); + serviceIntent.putExtra("Path", path); + Log.d("A_PlayActivity", "Path: " + path); + startService(serviceIntent); + bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE); + } + + private void setupPlayButtonClickListener() { + vb.playButton.setOnClickListener(v -> { + if (isBound && musicService != null) { + if (Boolean.TRUE.equals(musicService.getIsPlaying().getValue())) { + musicService.pauseAudio(); + } else { + musicService.resumeAudio(); } } - }).start(); + }); + + vb.backButton.setOnClickListener(v -> finish()); + } + + private void startUpdatingProgress() { + if (progressHandler == null) { + progressHandler = new Handler(Looper.getMainLooper()); + progressHandler.postDelayed(new Runnable() { + @Override + public void run() { + updateSeekBarAndTime(); + progressHandler.postDelayed(this, 1000); + } + }, 1000); + } + } + + private void updateSeekBarAndTime() { + if (isBound && musicService != null && Boolean.TRUE.equals(musicService.getIsPlaying().getValue())) { + int currentPosition = musicService.getCurrentPosition(); + int duration = musicService.getDuration(); + if (duration > 0) { + vb.songSeekbar.setProgress((int) ((currentPosition / (float) duration) * 100)); + vb.current.setText(formatTime(currentPosition)); + vb.time.setText(formatTime(duration)); + } + } } private String formatTime(int milliseconds) { @@ -116,6 +134,10 @@ public class A_PlayActivity extends BaseActivity { unbindService(serviceConnection); isBound = false; } + if (progressHandler != null) { + progressHandler.removeCallbacksAndMessages(null); + progressHandler = null; // 释放引用 + } } @Override @@ -132,4 +154,28 @@ public class A_PlayActivity extends BaseActivity { public void onClick(View v) { // 处理其他点击事件 } + + private class SeekBarListener implements android.widget.SeekBar.OnSeekBarChangeListener { + @Override + public void onProgressChanged(android.widget.SeekBar seekBar, int progress, boolean fromUser) { + if (fromUser && musicService != null && isBound) { + int duration = musicService.getDuration(); + if (duration > 0) { + int newPosition = (int) ((progress / 100.0) * duration); + musicService.seekTo(newPosition); + } + } + } + + @Override + public void onStartTrackingTouch(android.widget.SeekBar seekBar) { + // 用户开始拖动进度条时触发 + } + + @Override + public void onStopTrackingTouch(android.widget.SeekBar seekBar) { + // 用户停止拖动进度条时触发 + startUpdatingProgress(); // 拖动结束后重新启动进度更新 + } + } } diff --git a/app/src/main/java/com/hi/music/player/ui/activity/viewmodel/A_VMPlay.java b/app/src/main/java/com/hi/music/player/ui/activity/viewmodel/A_VMPlay.java index 6111335..7eaa3f3 100644 --- a/app/src/main/java/com/hi/music/player/ui/activity/viewmodel/A_VMPlay.java +++ b/app/src/main/java/com/hi/music/player/ui/activity/viewmodel/A_VMPlay.java @@ -1,130 +1,14 @@ package com.hi.music.player.ui.activity.viewmodel; import android.app.Application; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.IBinder; +import androidx.annotation.NonNull; import androidx.lifecycle.AndroidViewModel; -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; - -import com.hi.music.player.service.MusicPlayerForegroundService; - -import java.util.Locale; public class A_VMPlay extends AndroidViewModel { - private final MutableLiveData currentTime = new MutableLiveData<>(); - private final MutableLiveData totalTime = new MutableLiveData<>(); - private final MutableLiveData fileName = new MutableLiveData<>(); - private final MutableLiveData isPlaying = new MutableLiveData<>(false); - private final MutableLiveData progress = new MutableLiveData<>(0); // 添加进度 LiveData - private MusicPlayerForegroundService musicService; - private boolean isBound = false; - - public A_VMPlay(Application application) { + public A_VMPlay(@NonNull Application application) { super(application); - bindToMusicService(); - } - - // 获取进度的 LiveData - public LiveData getProgress() { - return progress; - } - - public LiveData getCurrentTime() { - return currentTime; - } - - public LiveData getTotalTime() { - return totalTime; - } - - public LiveData isPlaying() { - return isPlaying; - } - - public LiveData getFileName() { - return fileName; - } - - private void bindToMusicService() { - Intent serviceIntent = new Intent(getApplication(), MusicPlayerForegroundService.class); - getApplication().bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE); - } - - private final ServiceConnection serviceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - MusicPlayerForegroundService.MusicBinder binder = (MusicPlayerForegroundService.MusicBinder) service; - musicService = binder.getService(); - isBound = true; - - // 观察服务中的播放状态 - musicService.getIsPlaying().observeForever(isPlaying::setValue); - - // 开始监听播放进度 - startUpdatingProgress(); - } - - @Override - public void onServiceDisconnected(ComponentName name) { - isBound = false; - } - }; - - public void togglePlay() { - if (isBound) { - if (musicService.getIsPlaying().getValue() != null && musicService.getIsPlaying().getValue()) { - musicService.pauseAudio(); - } else { - musicService.resumeAudio(); - } - } - } - - private void startUpdatingProgress() { - new Thread(() -> { - while (isBound && musicService != null) { - if (musicService.getIsPlaying().getValue() != null && musicService.getIsPlaying().getValue()) { - int currentPosition = musicService.getCurrentPosition(); // 从服务获取当前播放位置 - currentTime.postValue(formatTime(currentPosition)); - int duration = musicService.getDuration(); // 从服务获取音频总时长 - progress.postValue((int) ((currentPosition / (float) duration) * 100)); // 更新进度 - totalTime.postValue(formatTime(duration)); - } - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - }).start(); - } - - public void seekTo(int progress) { - if (isBound && musicService != null) { - int duration = musicService.getDuration(); - musicService.seekTo((int) (duration * progress / 100.0)); - } - } - - private String formatTime(int milliseconds) { - int minutes = (milliseconds / 1000) / 60; - int seconds = (milliseconds / 1000) % 60; - return String.format(Locale.getDefault(), "%d:%02d", minutes, seconds); - } - - @Override - protected void onCleared() { - super.onCleared(); - if (isBound) { - getApplication().unbindService(serviceConnection); - isBound = false; - } } } diff --git a/app/src/main/res/drawable/pause.xml b/app/src/main/res/drawable/pause.xml index 6ece7e6..3382d72 100644 --- a/app/src/main/res/drawable/pause.xml +++ b/app/src/main/res/drawable/pause.xml @@ -6,13 +6,21 @@ + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="#ffffff" + android:strokeLineCap="round"/> + diff --git a/app/src/main/res/drawable/pause_flat.xml b/app/src/main/res/drawable/pause_flat.xml index 043dfa9..98f79ff 100644 --- a/app/src/main/res/drawable/pause_flat.xml +++ b/app/src/main/res/drawable/pause_flat.xml @@ -6,11 +6,7 @@ - - - - + diff --git a/app/src/main/res/drawable/pause_shadow.xml b/app/src/main/res/drawable/pause_shadow.xml index 2c16085..4ab0bd7 100644 --- a/app/src/main/res/drawable/pause_shadow.xml +++ b/app/src/main/res/drawable/pause_shadow.xml @@ -9,14 +9,22 @@ + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="#ffffff" + android:strokeLineCap="round"/> + diff --git a/app/src/main/res/drawable/play.xml b/app/src/main/res/drawable/play.xml index 3382d72..6ece7e6 100644 --- a/app/src/main/res/drawable/play.xml +++ b/app/src/main/res/drawable/play.xml @@ -6,21 +6,13 @@ - + android:strokeWidth="1.66667" + android:fillColor="#ffffff" + android:strokeColor="#ffffff"/> diff --git a/app/src/main/res/drawable/pause_big.xml b/app/src/main/res/drawable/play_big.xml similarity index 100% rename from app/src/main/res/drawable/pause_big.xml rename to app/src/main/res/drawable/play_big.xml diff --git a/app/src/main/res/drawable/play_flat.xml b/app/src/main/res/drawable/play_flat.xml index 98f79ff..043dfa9 100644 --- a/app/src/main/res/drawable/play_flat.xml +++ b/app/src/main/res/drawable/play_flat.xml @@ -6,7 +6,11 @@ - + + + + diff --git a/app/src/main/res/drawable/play_shadow.xml b/app/src/main/res/drawable/play_shadow.xml index 4ab0bd7..2c16085 100644 --- a/app/src/main/res/drawable/play_shadow.xml +++ b/app/src/main/res/drawable/play_shadow.xml @@ -9,22 +9,14 @@ - + android:strokeWidth="1.66667" + android:fillColor="#151718" + android:strokeColor="#151718"/> diff --git a/app/src/main/res/layout/activity_ahome.xml b/app/src/main/res/layout/activity_ahome.xml index 0bfe410..d11b637 100644 --- a/app/src/main/res/layout/activity_ahome.xml +++ b/app/src/main/res/layout/activity_ahome.xml @@ -12,7 +12,7 @@ android:id="@+id/home_viewpager" android:layout_width="match_parent" android:layout_height="0dp" - app:layout_constraintBottom_toTopOf="@+id/home_tab_layout" + app:layout_constraintBottom_toTopOf="@+id/home_container" app:layout_constraintTop_toTopOf="parent" /> + app:tabRippleColor="@android:color/transparent" + tools:layout_editor_absoluteX="0dp" /> diff --git a/app/src/main/res/layout/item_a_home_1.xml b/app/src/main/res/layout/item_a_home_1.xml index 34c14b9..fe4ec7b 100644 --- a/app/src/main/res/layout/item_a_home_1.xml +++ b/app/src/main/res/layout/item_a_home_1.xml @@ -56,7 +56,7 @@ android:layout_width="32dp" android:layout_height="32dp" android:layout_gravity="center_vertical" - android:src="@drawable/pause_flat" /> + android:src="@drawable/play_flat" /> diff --git a/app/src/main/res/layout/item_a_home_3.xml b/app/src/main/res/layout/item_a_home_3.xml index 2dce966..a378e6e 100644 --- a/app/src/main/res/layout/item_a_home_3.xml +++ b/app/src/main/res/layout/item_a_home_3.xml @@ -54,7 +54,7 @@ android:layout_width="20dp" android:layout_height="20dp" android:layout_marginStart="8dp" - android:src="@drawable/pause_shadow" + android:src="@drawable/play_shadow" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" />