600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > Android实战:利用service实现简单的音乐播放器

Android实战:利用service实现简单的音乐播放器

时间:2023-10-17 00:38:23

相关推荐

Android实战:利用service实现简单的音乐播放器

目录

新建工程(Android Studio)添加、编辑资源文件添加、编辑布局文件添加音乐信息类添加MusicUtils类添加ListView适配器类添加接口MyBinderInterfaceMusicService服务类注册服务编辑MainActivity.java文件测试、运行

新建工程(Android Studio)

略。

添加、编辑资源文件

编辑res->values->strings.xml文件,内容如下:

<resources><string name="app_name">MusicBox</string><string name="title_string">本地音乐</string><string name="menu_about">关于</string><string name="menu_exit">退出</string><string name="btn_confirm">确认</string><string name="btn_cancel">取消</string><string name="str_warning">警告</string><string name="menu_detail">文件详情</string><string name="menu_play">开始播放</string><string name="str_playing">歌曲已经在播放了</string></resources>

AndroidManifest.xml文件中添加读取外部储存卡的权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

添加menu资源目录,并在其中新建一个menu_item.xml文件,作为选项菜单的布局文件。

<?xml version="1.0" encoding="utf-8"?><menu xmlns:android="/apk/res/android"><itemandroid:id="@+id/item_about"android:alphabeticShortcut="r"android:icon="@drawable/ic_launcher_foreground"android:title="@string/menu_about"/><itemandroid:id="@+id/item_exit"android:alphabeticShortcut="d"android:icon="@drawable/ic_launcher_foreground"android:title="@string/menu_exit"/></menu>

更改程序图标。(此过程简单,略)

添加、编辑布局文件

编辑activity_main.xml(主界面布局文件),内容如下:

<?xml version="1.0" encoding="utf-8"?><LinearLayoutxmlns:android="/apk/res/android"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:background="#dfebe8"android:orientation="vertical"><TextViewandroid:id="@+id/music_list_title"android:layout_width="match_parent"android:layout_height="40dp"android:layout_marginBottom="6dp"android:text="@string/title_string"android:textSize="16dp"android:paddingLeft="10dp"android:gravity="center_vertical"android:background="#e9faf6"/><ListViewandroid:id="@+id/music_list"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"/></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="70dp"android:orientation="horizontal"android:background="#e9faf6"><ImageViewandroid:id="@+id/music_thumb"android:layout_width="70dp"android:layout_height="70dp"android:padding="4dp"android:src="@drawable/gnote"/><LinearLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:orientation="vertical"><TextViewandroid:id="@+id/music_name"android:layout_width="match_parent"android:layout_height="25dp"android:textSize="14sp"android:textColor="#0b9900"android:paddingLeft="5dp"android:gravity="bottom"android:singleLine="true"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="45dp"android:orientation="horizontal"><android.support.v7.widget.AppCompatSeekBarandroid:id="@+id/music_seek_bar"android:layout_width="0dp"android:layout_height="15dp"android:layout_weight="1"android:layout_marginTop="10dp"/><ImageViewandroid:id="@+id/btn_previous"android:layout_width="40dp"android:layout_height="40dp"android:src="@drawable/previous"/><ImageViewandroid:id="@+id/btn_play"android:layout_width="40dp"android:layout_height="40dp"android:src="@drawable/play"/><ImageViewandroid:id="@+id/btn_next"android:layout_width="40dp"android:layout_height="40dp"android:src="@drawable/next"/></LinearLayout></LinearLayout></LinearLayout></LinearLayout>

添加item_layout.xml作为ListView每个item的布局文件:

<?xml version="1.0" encoding="utf-8"?><LinearLayoutxmlns:android="/apk/res/android"android:layout_width="match_parent"android:layout_height="70dp"android:padding="5dp"android:orientation="horizontal"><ImageViewandroid:id="@+id/rand_icon"android:layout_width="60dp"android:layout_height="60dp"android:padding="2dp"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="60dp"android:layout_marginLeft="6dp"android:orientation="vertical"><TextViewandroid:id="@+id/item_music_name"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1.5"android:textSize="18sp"android:gravity="center_vertical"android:singleLine="true"/><TextViewandroid:id="@+id/item_music_singer"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:textSize="16sp"android:gravity="center_vertical"android:singleLine="true"/></LinearLayout></LinearLayout>

添加音乐信息类

在工程中新建一个类MusicInfo用于记录音乐文件的信息:

package com.zys.musicbox;public class MusicInfo {private String music_title;private String music_name;private String music_path;private String music_artist;private int music_duration;//getter and setter}

添加MusicUtils类

在工程中新建一个类MusicUtils用于操作音乐文件信息(此例中只有一个方法,利用ContentResolver类读取音乐信息):

package com.zys.musicbox;import android.content.ContentResolver;import android.content.Context;import android.database.Cursor;import android.provider.MediaStore;import java.util.ArrayList;import java.util.List;public class MusicUtils {/** 用于获取本地Music目录下的所有音乐信息,并封装成List后返回* 需要一个Context对象* */public static List<MusicInfo> ResolveMusicToList(Context context){String selection = MediaStore.Audio.Media.IS_MUSIC + "!=0";String sortOrder = MediaStore.MediaColumns.DISPLAY_NAME+"";List<MusicInfo> musicList = new ArrayList<MusicInfo>();String[] projection = {MediaStore.Audio.Media.TITLE,MediaStore.Audio.Media.ARTIST,MediaStore.Audio.Media.DISPLAY_NAME,MediaStore.Audio.Media.DATA,MediaStore.Audio.Media.DURATION};ContentResolver contentResolver = context.getContentResolver();Cursor cursor = contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,projection,selection,null,sortOrder);if (cursor != null){for (cursor.moveToFirst(); cursor.isAfterLast() != true; cursor.moveToNext()){MusicInfo musicInfo = new MusicInfo();musicInfo.setMusic_title(cursor.getString(0));musicInfo.setMusic_artist(cursor.getString(1));musicInfo.setMusic_name(cursor.getString(2));musicInfo.setMusic_path(cursor.getString(3));musicInfo.setMusic_duration(Integer.parseInt(cursor.getString(4)));musicList.add(musicInfo);}}return musicList;}}

添加ListView适配器类

在工程中新建一个类ListAdapter作为主界面中ListView的适配器类。

package com.zys.musicbox;import android.content.Context;import android.graphics.BitmapFactory;import android.graphics.Color;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import java.util.List;class ViewHolder{public ImageView itemIcon;public TextView itemMusicName;public TextView itemMusicSinger;public int defaultTextColor;View itemView;public ViewHolder(View itemView) {if (itemView == null){throw new IllegalArgumentException("item View can not be null!");}this.itemView = itemView;itemIcon = itemView.findViewById(R.id.rand_icon);itemMusicName = itemView.findViewById(R.id.item_music_name);itemMusicSinger = itemView.findViewById(R.id.item_music_singer);defaultTextColor = itemMusicName.getCurrentTextColor();}}public class ListAdapter extends BaseAdapter {private List<MusicInfo> musicList;private LayoutInflater layoutInflater;private Context context;private int currentPos = -1;private ViewHolder holder = null;public ListAdapter(Context context,List<MusicInfo> musicList) {this.musicList = musicList;this.context = context;layoutInflater = LayoutInflater.from(context);}public void setFocusItemPos(int pos){currentPos = pos;notifyDataSetChanged();}@Overridepublic int getCount() {return musicList.size();}@Overridepublic Object getItem(int position) {return musicList.get(position).getMusic_title();}@Overridepublic long getItemId(int position) {return position;}public void remove(int index){musicList.remove(index);}public void refreshDataSet(){notifyDataSetChanged();}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {if (convertView == null){convertView = layoutInflater.inflate(R.layout.item_layout,null);holder = new ViewHolder(convertView);convertView.setTag(holder);}else {holder = (ViewHolder)convertView.getTag();}//如果是正在播放的音乐 就改变图片、字体颜色if (position == currentPos){holder.itemIcon.setImageBitmap(BitmapFactory.decodeResource(context.getResources(),R.drawable.arrow));holder.itemMusicName.setTextColor(Color.RED);holder.itemMusicSinger.setTextColor(Color.RED);}//否则使用默认图片、字体颜色else{holder.itemIcon.setImageBitmap(BitmapFactory.decodeResource(context.getResources(),R.drawable.music));holder.itemMusicName.setTextColor(holder.defaultTextColor);holder.itemMusicSinger.setTextColor(holder.defaultTextColor);}holder.itemMusicName.setText(musicList.get(position).getMusic_title());holder.itemMusicSinger.setText(musicList.get(position).getMusic_artist());return convertView;}}

添加接口MyBinderInterface

在工程中新建一个接口MyBinderInterface作为调用Service中方法的媒介、中间人:

package com.zys.musicbox;public interface MyBinderInterface {//暂停void Pause();//恢复void Resume();//播放void Play();//播放下一首void PlayNext();//播放上一首void PlayPrev();//释放void Release();//是否正在播boolean isPlaying();//获取时长int getDuration();//当前位置int getCurrentPosition();//拖动位置void seekTo(int length);//获取当前索引int getCurrentIndex();//设置当前索引void setCurrentIndex(int currentIdx);}

MusicService服务类

package com.zys.musicbox;import android.app.Service;import android.content.Intent;import android.media.AudioManager;import android.media.MediaPlayer;import .Uri;import android.os.Binder;import android.os.IBinder;import java.io.IOException;import java.util.List;public class MusicService extends Service {private MediaPlayer mPlayer;private int seekLength = 0;private int currentIndex = -1;private List<MusicInfo> musicList;@Overridepublic IBinder onBind(Intent intent) {return new MyBinder();}@Overridepublic void onCreate() {super.onCreate();InitPlayer();//通过工具类MusicUtils获取音乐信息列表musicList = MusicUtils.ResolveMusicToList(getApplicationContext());}private void InitPlayer() {mPlayer = new MediaPlayer();mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);}private class MyBinder extends Binder implements MyBinderInterface{@Overridepublic void Pause() {if (mPlayer.isPlaying()){mPlayer.pause();seekLength = mPlayer.getCurrentPosition();}}@Overridepublic void Resume() {mPlayer.seekTo(seekLength);mPlayer.start();}@Overridepublic void Play() {mPlayer.reset();Uri path = Uri.parse(musicList.get(currentIndex).getMusic_path());try {mPlayer.setDataSource(String.valueOf(path));mPlayer.prepare();} catch (IOException e) {e.printStackTrace();}mPlayer.seekTo(seekLength);mPlayer.start();}@Overridepublic void PlayNext() {currentIndex += 1;if (currentIndex >= musicList.size()){currentIndex = 0;}seekLength = 0;Play();}@Overridepublic void PlayPrev() {currentIndex -= 1;if (currentIndex <= 0){currentIndex = musicList.size() - 1;}seekLength = 0;Play();}@Overridepublic void Release() {mPlayer.reset();mPlayer.stop();mPlayer.release();}@Overridepublic boolean isPlaying() {return mPlayer.isPlaying();}@Overridepublic int getDuration() {return mPlayer.getDuration();}@Overridepublic int getCurrentPosition() {return mPlayer.getCurrentPosition();}@Overridepublic void seekTo(int length) {seekLength = length;mPlayer.seekTo(length);}@Overridepublic int getCurrentIndex() {return currentIndex;}@Overridepublic void setCurrentIndex(int currentIdx) {currentIndex = currentIdx;}}}

注册服务

修改AndroidManifest.xml文件,内容如下:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="/apk/res/android"package="com.zys.musicbox"><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><!-- 注册服务 --><service android:name=".MusicService"></service></application></manifest>

主要是注册服务那一行代码。

编辑MainActivity.java文件

package com.zys.musicbox;import android.app.AlertDialog;import ponentName;import android.content.Context;import android.content.DialogInterface;import android.content.Intent;import android.content.ServiceConnection;import android.graphics.BitmapFactory;import android.os.Handler;import android.os.IBinder;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.ContextMenu;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.AdapterView;import android.widget.ImageView;import android.widget.ListView;import android.widget.SeekBar;import android.widget.TextView;import android.widget.Toast;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity {private List<MusicInfo> musicList = new ArrayList<MusicInfo>();private ListAdapter mListAdapter;private ListView mListView;//记录长按的列表项坐标private int currentSel;//按钮private ImageView btnPrevious;private ImageView btnNext;private ImageView btnPlay;//文本private TextView listTitle;private TextView playingName;//进度条private SeekBar musicSeekBar;//自定义Binder对象 用于调用服务中的方法private MyBinderInterface myBinder;//自定义服务连接对象private MyServiceConnection conn;//是否正在播放private boolean isPlaying = false;private Handler handler = new Handler();//更新线程用于更新进度条private Runnable updateThread = new Runnable() {@Overridepublic void run() {if (myBinder != null){try {if (myBinder.isPlaying()){int duration = myBinder.getDuration();int currentPos = myBinder.getCurrentPosition();musicSeekBar.setMax(duration);musicSeekBar.setProgress(currentPos);int prg_sec = currentPos/1000;int max_sec = duration/1000;if (prg_sec == max_sec){myBinder.PlayNext();updateState();}}}catch (Exception e){e.printStackTrace();}}handler.post(updateThread);}};//定义服务连接private class MyServiceConnection implements ServiceConnection{@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {myBinder = (MyBinderInterface)service;}@Overridepublic void onServiceDisconnected(ComponentName name) {}};//更新播放状态private void updateState() {int index = myBinder.getCurrentIndex();mListAdapter.setFocusItemPos(index);String currentMusicName = musicList.get(index).getMusic_title();playingName.setText(currentMusicName);btnPlay.setImageBitmap(BitmapFactory.decodeResource(getResources(),R.drawable.pause));isPlaying = true;}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//通过工具类MusicUtils获取音乐信息列表musicList = MusicUtils.ResolveMusicToList(getApplicationContext());//获取视图initView();//设置列表标题String title = getResources().getString(R.string.title_string).toString();title += "(总数:"+ musicList.size() + ")";listTitle.setText(title);//为mListView注册上下文菜单registerForContextMenu(mListView);conn = new MyServiceConnection();//绑定服务bindService(new Intent(this,MusicService.class),conn, Context.BIND_AUTO_CREATE);handler.post(updateThread);}//初始化视图private void initView(){listTitle = (TextView)findViewById(R.id.music_list_title);playingName = (TextView)findViewById(R.id.music_name);musicSeekBar = (SeekBar)findViewById(R.id.music_seek_bar);btnPrevious = (ImageView)findViewById(R.id.btn_previous);btnNext = (ImageView)findViewById(R.id.btn_next);btnPlay = (ImageView)findViewById(R.id.btn_play);mListView = (ListView)findViewById(R.id.music_list);mListAdapter = new ListAdapter(MainActivity.this,musicList);mListView.setAdapter(mListAdapter);setListener();}//设置监听事件private void setListener(){mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {@Overridepublic boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {mListView.showContextMenu();currentSel = position;return true;}});mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {myBinder.setCurrentIndex(position);myBinder.Play();mListAdapter.setFocusItemPos(position);updateState();}});btnPrevious.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (isPlaying == true){myBinder.PlayPrev();updateState();}}});btnNext.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (isPlaying == true){myBinder.PlayNext();updateState();}}});btnPlay.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (isPlaying == true){btnPlay.setImageBitmap(BitmapFactory.decodeResource(getResources(),R.drawable.play));isPlaying = false;myBinder.Pause();return;}if (isPlaying == false){if (myBinder.getCurrentIndex() == -1){myBinder.setCurrentIndex(0);mListAdapter.setFocusItemPos(0);myBinder.Play();updateState();}btnPlay.setImageBitmap(BitmapFactory.decodeResource(getResources(),R.drawable.pause));isPlaying = true;myBinder.Resume();}}});musicSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {if (myBinder != null){try {myBinder.seekTo(seekBar.getProgress());}catch (Exception e){e.printStackTrace();}}}});}@Overridepublic void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo){menu.add(0,0,0,R.string.menu_detail);menu.add(0,1,1,R.string.menu_play);super.onCreateContextMenu(menu,view,menuInfo);}@Overridepublic boolean onContextItemSelected(MenuItem item) {switch (item.getItemId()){case 0:StringBuilder msgBuilder = new StringBuilder();msgBuilder.append("文件名:" + musicList.get(currentSel).getMusic_name() + "\n");msgBuilder.append("路 径:" + musicList.get(currentSel).getMusic_path() + "\n");msgBuilder.append("时 长:" + musicList.get(currentSel).getMusic_duration()/1000 + " s\n");String title = "文件详情";new AlertDialog.Builder(MainActivity.this).setIcon(R.drawable.note).setTitle(title).setMessage(msgBuilder.toString()).setPositiveButton(R.string.btn_confirm, new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {}}).create().show();break;case 1://不处于播放状态 或者 选择的歌曲和正在播放的歌曲不是同一首 则更新状态且播放if (isPlaying == false || currentSel != myBinder.getCurrentIndex()){myBinder.setCurrentIndex(currentSel);updateState();myBinder.Play();}//提示选择的歌曲已经在播放了else{Toast.makeText(MainActivity.this,R.string.str_playing,Toast.LENGTH_SHORT).show();}break;default:break;}return super.onContextItemSelected(item);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {boolean retValue = super.onCreateOptionsMenu(menu);getMenuInflater().inflate(R.menu.menu_item,menu);return retValue;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {if (item.getItemId() == R.id.item_about){StringBuilder msgBuilder = new StringBuilder();msgBuilder.append("MusicBox V1.0.0\n");msgBuilder.append("作者:Leo_Elegant\n");msgBuilder.append("(C) ......");String title = "关于";new AlertDialog.Builder(MainActivity.this).setIcon(R.drawable.note).setTitle(title).setMessage(msgBuilder.toString()).setPositiveButton(R.string.btn_confirm, new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {}}).create().show();}if (item.getItemId() == R.id.item_exit){onBackPressed();}return super.onOptionsItemSelected(item);}@Overridepublic void onBackPressed() {String title = "提示";new AlertDialog.Builder(MainActivity.this).setIcon(R.drawable.note).setTitle(title).setMessage("确定要退出吗?").setPositiveButton(R.string.btn_confirm, new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {myBinder.Release();finish();}}).setNegativeButton(R.string.btn_cancel, new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {}}).create().show();}}

测试、运行

冷启动模拟器,并上传几首音乐到SD卡的Music目录下。在启动程序后会闪退,给程序设好权限。先打开模拟器自带的音乐播放软件,帮助我们索引音乐文件并存入数据库,之后我们的程序在读取的到。 启动界面,并点击播放按钮:

下一首:

选项菜单

选项菜单“关于”

长按歌曲弹出上下文菜单

上下文菜单“文件详情”

注:此例子部分内容为《移动软件开发》课程老师布置实验。

老师博客:/

我的更多文章尽在:我的个人博客

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。