android本地音乐播放(一)
实体类Music
package cn.tedu.music_player_1;
public class Music {
private String name;private String path;public String getPath() { return path;}public void setPath(String path) { this.path = path;}public String getName() { return name;}public void setName(String name) { this.name = name;}
}
Activity代码,带注释
package cn.tedu.music_player_1;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
public class MainActivity extends Activity {
// 歌曲的数据
private List musics;
// ListView
private ListView lvMusics;
// Adapter
private MusicAdapter musicAdapter;
// MediaPlayer
private MediaPlayer player;
// ImageButton:播放/暂停
private ImageButton ibPlayOrPause;
// ImageButton:上一首
private ImageButton ibPrevious;
// ImageButton:下一首
private ImageButton ibNext;
// Button:播放模式
private Button btnPlayMode;
// TextView:当前播放的歌曲名称
private TextView tvMusicTitle;
// TextView:当前播放的歌曲的时长
private TextView tvMusicDuration;
// TextView:当前播放到的时间(播放过程中)
private TextView tvMusicCurrentPosition;
// SeekBar:歌曲的播放进度
private SeekBar sbMusicProgress;
// 暂停时播放到的位置
private int pausePosition;
// 当前播放的歌曲的索引
private int currentMusicIndex;
// 格式化时间的工具
private SimpleDateFormat sdf = new SimpleDateFormat(“mm:ss”, Locale.CHINA);
// 被格式化的时间对象
private Date date = new Date();
// 更新进度的线程
private UpdateProgressThread updateProgressThread;
// 是否是第1次点击列表项
private boolean isFirstClickListItem = true;
// 用户是否正在拖拽进度条
private boolean isUserTrackingTouch;
// 生成随机数据的工具
private Random random = new Random();
// 播放模式
private int playMode;
// 播放模式对应的字符串,用于显示在Button中
public static final String[] PLAY_MODE_TEXT = { “顺”, “随”, “单” };
// 播放模式 – 顺序播放
private static final int PLAY_MODE_ORDER = 0;
// 播放模式 – 随机播放
private static final int PLAY_MODE_RANDOM = 1;
// 播放模式 – 单曲循环
private static final int PLAY_MODE_SINGLE = 2;
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 设置歌曲数据 setMusicData(); // 初始化播放工具 player = new MediaPlayer(); // 为MediaPlayer配置监听器 player.setOnCompletionListener(new OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { next(); } }); // 初始化控件 lvMusics = (ListView) findViewById(R.id.lv_musics); ibPlayOrPause = (ImageButton) findViewById(R.id.ib_play_or_pause); ibPrevious = (ImageButton) findViewById(R.id.ib_previous); ibNext = (ImageButton) findViewById(R.id.ib_next); tvMusicTitle = (TextView) findViewById(R.id.tv_music_title); tvMusicDuration = (TextView) findViewById(R.id.tv_music_duration); tvMusicCurrentPosition = (TextView) findViewById(R.id.tv_music_current_position); sbMusicProgress = (SeekBar) findViewById(R.id.sb_music_progress); btnPlayMode = (Button) findViewById(R.id.btn_play_mode); // 创建Adapter musicAdapter = new MusicAdapter(this, musics); // 为ListView配置Adapter lvMusics.setAdapter(musicAdapter); // 为按钮配置监听器 InnerOnClickListener listener1 = new InnerOnClickListener(); ibPlayOrPause.setOnClickListener(listener1); ibPrevious.setOnClickListener(listener1); ibNext.setOnClickListener(listener1); btnPlayMode.setOnClickListener(listener1); // 为ListView配置监听器 InnerOnItemClickListener listener2 = new InnerOnItemClickListener(); lvMusics.setOnItemClickListener(listener2); // 为SeekBar配置监听器 InnerOnSeekBarChangeListener listener3 = new InnerOnSeekBarChangeListener(); sbMusicProgress.setOnSeekBarChangeListener(listener3);}@Overrideprotected void onDestroy() { // 线程运行结束 stopUpdateProgressThread(); // 释放播放器资源 player.release(); player = null; super.onDestroy();}private class UpdateProgressThread extends Thread { // 播放的进度值 private int progress; // 当前播放到的时间的字符串 private String timeString; // 线程的运行控制变量 private boolean isRunning; // 运行在主线程更新UI的对象 private Runnable runner = new Runnable() { @Override public void run() { // 判断用户是否正在拖拽进度条 if (!isUserTrackingTouch) { // 更新SeekBar sbMusicProgress.setProgress(progress); } // 更新TextView tvMusicCurrentPosition.setText(timeString); } }; // 设置线程是否循环 public void setRunning(boolean isRunning) { this.isRunning = isRunning; } @Override public void run() { while (isRunning) { // 日志 Log.i("tedu", "Thread(" + Thread.currentThread().getId() + ")尝试更新进度 ... ..."); // 判断当前是否处于播放状态 if (player.isPlaying()) { // 获取当前歌曲播放到的位置,计算得到显示的数据 progress = player.getCurrentPosition() * 100 / player.getDuration(); date.setTime(player.getCurrentPosition()); timeString = sdf.format(date); // 执行更新 runOnUiThread(runner); } // 休眠 try { Thread.sleep(450); } catch (InterruptedException e) { e.printStackTrace(); } } }}private class InnerOnSeekBarChangeListener implements OnSeekBarChangeListener { @Override public void onStartTrackingTouch(SeekBar seekBar) { // 当开始拖拽时,标识用户开始拖拽进度条 isUserTrackingTouch = true; } @Override public void onStopTrackingTouch(SeekBar seekBar) { // 获取进度 int progress = sbMusicProgress.getProgress(); // 计算播放的时间点 pausePosition = player.getDuration() * progress / 100; // 播放 play(); // 标识用户已经停止拖拽进度条 isUserTrackingTouch = false; } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // 当进度值发生变化时:可能由于程序更新了进度,或者用户正在拖拽的过程中 }}private class InnerOnItemClickListener implements OnItemClickListener { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) { // 判断点击的列表项的position是否匹配当前播放的歌曲的索引 if (isFirstClickListItem || position != currentMusicIndex) { // 确定需要播放的歌曲的索引,直接对应点击的列表项的position currentMusicIndex = position; // 清除暂停位置 pausePosition = 0; // 执行播放 play(); // 清除记录第1次点击 isFirstClickListItem = false; } }}private class InnerOnClickListener implements OnClickListener { @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_play_mode: // 计算播放模式变量的值 playMode++; playMode = playMode % 3; // 设置按钮上显示的文字 btnPlayMode.setText(PLAY_MODE_TEXT[playMode]); break; case R.id.ib_play_or_pause: // 判断当前是否正在播放 if (player.isPlaying()) { // 正在播放,则暂停 pause(); } else { // 没有播放,则播放 play(); } break; case R.id.ib_previous: // 播放上一首 previous(); break; case R.id.ib_next: // 播放下一首 next(); break; } }}// 开启线程private void startUpdateProgressThread() { if (updateProgressThread == null) { updateProgressThread = new UpdateProgressThread(); updateProgressThread.setRunning(true); updateProgressThread.start(); }}// 停止线程private void stopUpdateProgressThread() { if (updateProgressThread != null) { updateProgressThread.setRunning(false); updateProgressThread = null; }}// 播放歌曲private void play() { try { // 1. 重置 player.reset(); // 2. 设置歌曲路径 player.setDataSource(musics.get(currentMusicIndex).getPath()); // 3. 缓冲 player.prepare(); // 4. 快进 player.seekTo(pausePosition); // 5. 播放 player.start(); // 6. 将按钮显示为“暂停” ibPlayOrPause.setImageResource(android.R.drawable.ic_media_pause); // 7. 显示歌曲的名称 tvMusicTitle.setText("当前播放:" + musics.get(currentMusicIndex).getName()); // 8. 显示歌曲的时长 date.setTime(player.getDuration()); // 设置需要被格式化的时间 tvMusicDuration.setText(sdf.format(date)); // 显示 // 9. 开启线程,更新进度 startUpdateProgressThread(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }}// 暂停播放private void pause() { // 执行暂停 player.pause(); // 记录当前播放到的位置 pausePosition = player.getCurrentPosition(); // 将按钮显示为“播放” ibPlayOrPause.setImageResource(android.R.drawable.ic_media_play); // 停止更新进度的线程 stopUpdateProgressThread();}// 播放上一首private void previous() { // 计算上一首歌曲的索引 currentMusicIndex--; if (currentMusicIndex < 0) { currentMusicIndex = musics.size() - 1; } // 清除记录的暂停位置 pausePosition = 0; // 执行播放 play();}// 播放下一首private void next() { switch (playMode) { case PLAY_MODE_ORDER: // 计算下一首歌曲的索引 currentMusicIndex++; if (currentMusicIndex >= musics.size()) { currentMusicIndex = 0; } break; case PLAY_MODE_RANDOM: // 随机生成需要播放的歌曲的索引 int i; while ((i = random.nextInt(musics.size())) == currentMusicIndex) { } currentMusicIndex = i; break; case PLAY_MODE_SINGLE: break; } // 清除记录的暂停位置 pausePosition = 0; // 执行播放 play();}// 设置歌曲数据private void setMusicData() { // 初始化List集合 musics = new ArrayList<Music>(); // 检查sdcard的状态,即是否可用 if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { // 获取sdcard下Music文件夹的File对象 File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC); // 通过exists()方法检查Music文件夹是否存在 if (dir.exists()) { // 通过listFiles()方法获取Music文件夹下所有文件的File对象,获取的结果是File[] File[] files = dir.listFiles(); // 判断数组是否为null if (files != null) { // 遍历数组 for (int i = 0; i < files.length; i++) { // 检查遍历到的是否是文件,而不是文件夹 if (files[i].isFile()) { // 检查遍历到的是否是mp3文件 if (files[i].getName().toLowerCase(Locale.CHINA).endsWith(".mp3")) { // 如果是mp3文件,则创建Music对象,并添加到List集合中 Music music = new Music(); music.setPath(files[i].getAbsolutePath()); music.setName(files[i].getName().substring(0, files[i].getName().length() - 4)); musics.add(music); Log.i("tedu", "扫描到MP3文件:" + music.getName() + ",路径:" + music.getPath()); } else { Log.w("tedu", "遍历到File对象" + files[i] + "不是MP3文件!忽略!"); } } else { Log.w("tedu", "遍历到File对象" + files[i] + "是文件夹!忽略!"); } } } else { Log.w("tedu", "获取Music文件夹的内容失败!"); } } else { Log.w("tedu", "sdcard下Music文件夹不存在!"); } } else { Log.w("tedu", "sdcard不可用!"); }}
}
适配器adapter 具体代码
package cn.tedu.music_player_1;
import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class MusicAdapter extends BaseAdapter {
private Context context;
private List musics;
public MusicAdapter(Context context, List<Music> musics) { super(); this.context = context; this.musics = musics;}@Overridepublic int getCount() { return musics.size();}@Overridepublic View getView(int position, View convertView, ViewGroup parent) { // 1. 获取需要显示的数据 Music music = musics.get(position); // 2. 判断有没有可以直接使用的convertView if(convertView == null) { // 2.1 如果没有,则通过XML加载得到 LayoutInflater inflater = LayoutInflater.from(context); convertView = inflater.inflate(R.layout.music_item, null); } else { // 2.2 如果有,则直接使用 } // 3. 组装数据与模板 TextView tvName = (TextView) convertView.findViewById(R.id.tv_music_item_name); tvName.setText(music.getName()); TextView tvPath = (TextView) convertView.findViewById(R.id.tv_music_item_path); tvPath.setText(music.getPath()); // 4. 返回convertView return convertView;}@Overridepublic Object getItem(int position) { return null;}@Overridepublic long getItemId(int position) { return 0;}
}
activity_main.XML布局文件
更多相关文章
- android 调用本地音乐播放器
- Android模仿jquery异步请求
- android之seekBar
- Android(安卓)播放音乐文件与视频文
- Android(安卓)判断是否为Wifi网络
- android 判断当前网络是否可用
- android 如何判断当前进程在主页面
- Android(安卓)Asynchronous Http Client
- Android(安卓)BaseAdapter如何获得每一项并添加按钮事件