音乐播放器

摘要:音乐播放器,实现了播放、暂停、上一首、下一首,四个功能,并且使用了App Widget,在手机桌面添加该应用的小组件也可以实现操作。

下载地址:https://github.com/douyacai/App_Widgets_Test/tree/musicPlayer

  1. 首先定义一个Music的结构体,各变量表示音乐的各个属性:

    package com.example.musicplayer;
    
    public class Music {
    
        private String mName;
        private String mSinger;
        private String mAlbum;
        private String mUrl;
        private long mSize;
        private long mTime;
        private long mAlbumid;
        private long mSongid;
        public long getSongid() {
            return mSongid;
        }
    
        public void setSongid(long songid) {
            this.mSongid = songid;
        }
    
        private String name;
    
        public String getName() {
            return name;
        }
    
        public long getAlbumid() {
            return mAlbumid;
        }
    
        public void setAlbumid(long albumid) {
            this.mAlbumid = albumid;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getTitle() {
            return mName;
        }
    
        public void setTitle(String title) {
            this.mName = title;
        }
    
        public String getSinger() {
            return mSinger;
        }
    
        public void setSinger(String singer) {
            this.mSinger = singer;
        }
    
        public String getAlbum() {
            return mAlbum;
        }
    
        public void setAlbum(String album) {
            this.mAlbum = album;
        }
    
        public String getUrl() {
            return mUrl;
        }
    
        public void setUrl(String url) {
            this.mUrl = url;
        }
    
        public long getSize() {
            return mSize;
        }
    
        public void setSize(long size) {
            this.mSize = size;
        }
    
        public long getTime() {
            return mTime;
        }
    
        public void setTime(long time) {
            this.mTime = time;
        }
    }
  2. 在PlayMusicActivity的onCreate中进行一些初始化动作,包括实例化、注册广播、绑定监听器等:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.music_main);
        // 界面中控件初始化
        initView();
        // 注册广播
        register();
        // 绑定监听器
        buttonclick();
        service = new MusicPreference(this);
        intent = new Intent(PlayMusicActivity.this, PlayMusicService.class);
        musiclist = MusicList.getMusicData(this);
        toTime = new Tool();
        allMusic.setText(String.valueOf(musiclist.size()));
    }

    注册广播主要是用于在播放音乐时接收,用来更新音乐的名称和歌手名称,以及用户点击关闭时收到广播来finish该Activity:

    receiver = new MusicPlayer();
        IntentFilter filter = new IntentFilter(
                "com.example.musicserviceplayer");
        this.registerReceiver(receiver, filter);
    
        close = new Close();
        IntentFilter filter2 = new IntentFilter("com.sleep.close");
        this.registerReceiver(close, filter2);

    下面是当收到音乐播放广播时的处理:

    public class MusicPlayer extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Music m = musiclist.get(PlayMusicService.last_song);
            endTime.setText(toTime.toTime((int) m.getTime()));
            musicName.setText(m.getTitle());
            if (m.getSinger().equals("未知艺术家")) {
                musicSinger.setText("music");
            } else {
                musicSinger.setText(m.getSinger());
            }
            if (musiclist.size() > 0)
                currentMusic.setText(String
                        .valueOf(PlayMusicService.last_song + 1));
            else
                currentMusic.setText("0");
            imageBtnPlay.setBackgroundResource(R.drawable.pause1);
        }
    }

    下面是当收到关闭广播时的处理:

    public class Close extends BroadcastReceiver {
            @Override
            public void onReceive(Context context, Intent intent) {
                finish();
            }
        }

    绑定监听器,主要是绑定onClick的,监听按下播放、上一首、下一首时,启动PlayMusicService:

    // 按键监听器
    private class MyListener implements OnClickListener {
        @Override
        public void onClick(View v) {
            if (v == imageBtnRewind) { // 上一首
                intent.putExtra("mode", 1);
                startService(intent);
            } else if (v == imageBtnPlay) {
                if (PlayMusicService.mPlayer != null
                        && PlayMusicService.playflag) {
                    imageBtnPlay.setBackgroundResource(R.drawable.play1);
                } else {
                    imageBtnPlay.setBackgroundResource(R.drawable.pause1);
                }
                intent.putExtra("mode", 0);
                startService(intent);
            } else if (v == imageBtnForward) {
                intent.putExtra("mode", 2);
                startService(intent);
            }
        }
    }
  3. new一个Runnable,在播放音乐时用来更新进度:

    Runnable runnable = new Runnable() {
            @Override
            public void run() {
                handler.postDelayed(runnable, 100);
                if (PlayMusicService.mPlayer != null) {
                    seekBar.setProgress((int) (PlayMusicService.mPlayer
                            .getCurrentPosition() * 1000 / PlayMusicService.mPlayer
                            .getDuration()));
                    seekBar.invalidate();
                    startTime.setText(toTime.toTime((int) PlayMusicService.mPlayer
                            .getCurrentPosition()));
                }
            }
        };

    在onResume()中调用:

    handler.postDelayed(runnable, 100);
  4. PlayMusicService是实现运行在后台播放音乐的组件,同样在onCreate()中需要执行一些初始化以及注册广播等操作:

    @Override
        public void onCreate() {
            musiclist = MusicList.getMusicData(getApplicationContext());
            service = new MusicPreference(this);
            toTime = new Tool();
            register();
            new Thread(this).start();
            super.onCreate();
        }

    这里注册广播,主要是用来接收关闭时发送的广播,用来stopService及保存当前播放进度:

    private void register() {
            close = new Close();
            IntentFilter filter22 = new IntentFilter("com.sleep.close");
            this.registerReceiver(close, filter22);
        }
    public class Close extends BroadcastReceiver {
            @Override
            public void onReceive(Context context, Intent intent) {
                try {
                    if (mPlayer != null) {
                        String time = toTime.toTime(mPlayer.getCurrentPosition());
                        service.saveProgress(time,
                                Integer.valueOf(mPlayer.getCurrentPosition()));
                        widgetstop();
                        mPlayer.release();
                        mPlayer = null;
                    }
                    Music mz = musiclist.get(last_song);
                    String musicna = mz.getName();
                    service.savename(musicna, Integer.valueOf(last_song));
    
                    String Name = mz.getName();
                    service.saveMusic(Name, Integer.valueOf(last_song));
                } catch (Exception e) {
                    e.printStackTrace();
                }
                playflag = false;
            }
        }
  5. 当startService时,回调onStartCommand,在这个函数里执行相应操作,如播放:

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        int mode = intent.getIntExtra("mode", 0);
        Log.d("onStartCommand", String.valueOf(mode));
        if (0 == mode) {
            if (playflag) {// 暂停
                playflag = false;
                if (null != mPlayer) {
                    mPlayer.pause();
                    widgetstop();
                }
                try {
                    Music m = musiclist.get(last_song);
                    String Name = m.getName();
                    service.saveMusic(Name, Integer.valueOf(last_song));
                    service.saveProgress(
                            toTime.toTime(mPlayer.getCurrentPosition()),
                            Integer.valueOf(mPlayer.getCurrentPosition()));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {// 播放
                if (mPlayer != null) { // 继续播放
                    mPlayer.start();
                    playflag = true;
                } else if (mPlayer == null) { // 读取播放进度播放
                    Map<String, String> progressMap = service.getProgress();
                    progress = Integer.valueOf(progressMap.get("progress"));
                    Map<String, String> params = service.getMusic();
                    last_song = (Integer.valueOf(params.get("currentId")));
    
                    if (last_song >= musiclist.size() - 1) {
                        last_song = (musiclist.size() - 1);
                    }
                    Log.d("progress", "上次的进度是:" + String.valueOf(progress));
                    playMusic(last_song);
                    mPlayer.seekTo(progress);
                }
                playflag = true;
                widgetplay();
            }
        } else if (1 == mode) {
            last_song = (last_song + 1);
            if (last_song > musiclist.size() - 1) {
                last_song = 0;
            }
            playMusic(last_song);
            widgetUpdate();
        } else if (2 == mode) {
            last_song = (last_song - 1);
            if (last_song < 0) {
                last_song = (musiclist.size() - 1);
            }
            playMusic(last_song);
            widgetUpdate();
        }
        return super.onStartCommand(intent, flags, startId);
    }
  6. 在playMusic方法中,实现播放音乐以及发送广播的功能:

    private void playMusic(int song) {
        if (null != mPlayer) {
            mPlayer.release();
            mPlayer = null;
        }
    
        Music m = musiclist.get(song);
        Intent intent = new Intent("com.example.musicserviceplayer");
        sendBroadcast(intent);
        String url = m.getUrl();
        Uri myUri = Uri.parse(url);
        mPlayer = new MediaPlayer();
        mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        try {
            mPlayer.setDataSource(getApplicationContext(), myUri);
            mPlayer.prepare();
            mPlayer.start();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        mPlayer.setOnCompletionListener(new OnCompletionListener());
        mPlayer.setOnErrorListener(new OnErrorListener());
    
    }
  7. 当完成播放以及播放错误时,也需要进行相应的处理:

    private class OnCompletionListener implements
            android.media.MediaPlayer.OnCompletionListener {
        @Override
        public void onCompletion(MediaPlayer arg0) {
            last_song = (last_song + 1);
            if (last_song > musiclist.size() - 1) {
                last_song = (0);
            }
            playMusic(last_song);
        }
    }
    
    private final class OnErrorListener implements
            android.media.MediaPlayer.OnErrorListener {
        @Override
        public boolean onError(MediaPlayer arg0, int arg1, int arg2) {
            if (null != mPlayer) {
                mPlayer.release();
                mPlayer = null;
            }
            mPlayer = new MediaPlayer();
            Music m = musiclist.get(last_song);
            String url = m.getUrl();
            Uri myUri = Uri.parse(url);
            mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            try {
                mPlayer.setDataSource(getApplicationContext(), myUri);
                mPlayer.prepare();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return false;
        }
    }
  8. 当执行播放或者暂停操作时,也需要同步更新App Widget的ImageButton的ImageViewResource:

    public void widgetstop() {
            RemoteViews remoteViews = new RemoteViews(this.getPackageName(),
                    R.layout.music_appwidget);
            remoteViews.setImageViewResource(R.id.widget_startBtn,
                    R.drawable.widget_play);
            AppWidgetManager appWidgetManager = AppWidgetManager
                    .getInstance(getBaseContext());
            ComponentName componentName = new ComponentName(getBaseContext(),
                    MusicAppWidgetProvider.class);
            appWidgetManager.updateAppWidget(componentName, remoteViews);
        }
    
        public void widgetUpdate() {
            RemoteViews remoteViews = new RemoteViews(this.getPackageName(),
                    R.layout.music_appwidget);
            Music m = musiclist.get(last_song);
            Map<String, String> paramsui = service.getPreferencesback();
            Uri uri = Uri.parse(paramsui.get("background"));
            ContentResolver contentResolver = this.getContentResolver();
            if (String.valueOf(uri).length() > 4) {
                try {
    
                    bitmap = BitmapFactory.decodeStream(contentResolver
                            .openInputStream(uri));
                    int h = bitmap.getHeight();
                    remoteViews.setImageViewBitmap(R.id.widget_logo,
                            RoundCorner.toRoundCorner(bitmap, h / 34));
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
            String sbr = m.getName().substring(0, m.getName().length() - 4);
            remoteViews.setTextViewText(R.id.widgettitlemain, sbr);
            if (m.getSinger().equals("未知艺术家")) {
                remoteViews.setTextViewText(R.id.widgetsinger, "");
            } else {
                remoteViews.setTextViewText(R.id.widgetsinger, m.getSinger());
            }
            AppWidgetManager appWidgetManager = AppWidgetManager
                    .getInstance(getBaseContext());
            ComponentName componentName = new ComponentName(getBaseContext(),
                    MusicAppWidgetProvider.class);
            appWidgetManager.updateAppWidget(componentName, remoteViews);
        }
  9. 音乐在播放时也需要在App Widget中显示进度,这里呢,进度使用一个点在圆环中顺时针移动来表示:

    @Override
        public void run() {
            while (1 > 0) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                long current = 0;
                long total = 0;
                int degree = 0;
                try {
                    current = mPlayer.getCurrentPosition();
                    total = mPlayer.getDuration();
                    degree = (int) (current * 360 / total);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                RemoteViews remoteViews = new RemoteViews(this.getPackageName(),
                        R.layout.music_appwidget);
                if (degree >= 360) {
                    degree = 360;
                }
                try {
                    Xuanzhuan xuanzhuan = new Xuanzhuan(PlayMusicService.this);
                    remoteViews.setImageViewBitmap(R.id.thumb2,
                            xuanzhuan.rotate(degree));
                    remoteViews.setImageViewBitmap(
                            R.id.icon_panel_progress_barleft,
                            xuanzhuan.rotate2(degree));
                } catch (Exception e) {
                    e.printStackTrace();
                } catch (OutOfMemoryError e) {
                    e.printStackTrace();
                }
                if (degree >= 180) {
                    remoteViews.setViewVisibility(R.id.leftyuanquan, View.VISIBLE); // 右半绿圆出现
                    remoteViews.setViewVisibility(R.id.icon_panel_progress_bg2,
                            View.INVISIBLE);
                }
                if (degree < 180) {
                    remoteViews
                            .setViewVisibility(R.id.leftyuanquan, View.INVISIBLE); // 右半绿圆消失
                    remoteViews.setViewVisibility(R.id.icon_panel_progress_bg2,
                            View.VISIBLE);
                }
                AppWidgetManager appWidgetManager = AppWidgetManager
                        .getInstance(getBaseContext());
                ComponentName componentName = new ComponentName(getBaseContext(),
                        MusicAppWidgetProvider.class);
                appWidgetManager.updateAppWidget(componentName, remoteViews);
            }
    
        }
  10. 在MusicAppWidgetProvider中:

    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
                int[] appWidgetIds) {
            final int N = appWidgetIds.length;
            for (int i = 0; i < N; i++) {
                int appWidgetId = appWidgetIds[i];
                RemoteViews views = new RemoteViews(context.getPackageName(),
                        R.layout.music_appwidget);
    
                //play
                Intent intentPlay = new Intent(context, PlayMusicService.class);
                intentPlay.putExtra("mode", 0);
                PendingIntent pendingPlay = PendingIntent.getService(
                        context, 0, intentPlay, PendingIntent.FLAG_UPDATE_CURRENT);
                views.setOnClickPendingIntent(R.id.widget_startBtn,
                        pendingPlay);
                //next
                Intent intentNext = new Intent(context, PlayMusicService.class);
                intentNext.putExtra("mode", 1);
                PendingIntent pendingNext = PendingIntent.getService(
                        context, 1, intentNext, PendingIntent.FLAG_UPDATE_CURRENT);
                views.setOnClickPendingIntent(R.id.widget_frontBtn,
                        pendingNext);
    
                //pre
                Intent intentPre = new Intent(context, PlayMusicService.class);
                intentPre.putExtra("mode", 2);
                PendingIntent pendingPre = PendingIntent.getService(
                        context, 2, intentPre, PendingIntent.FLAG_UPDATE_CURRENT);
                views.setOnClickPendingIntent(R.id.widget_nextBtn,
                        pendingPre);
                appWidgetManager.updateAppWidget(appWidgetId, views);
            }
        }

    PendingIntent有四个参数:

    • 第一个参数,context表示上下文;
    • 第二个参数,requestCode,这里不同的请求需要用不同的requestCode,不然传递的Extra里的值都会被最新的覆盖掉;
    • 第三个参数,intent;
    • 第四个参数,操作标识,一般有FLAG_CANCEL_CURRENT和FLAG_UPDATE_CURRENT,FLAG_UPDATE_CURRENT会更新之前PendingIntent的消息
  11. 在AndroidManifest中不要忘记对PlayMusicActivity,PlayMusicService,和MusicAppWidgetProvider进行注册:

    <application
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <activity
                android:name="com.example.musicplayer.PlayMusicActivity"
                android:label="@string/app_name"
                android:screenOrientation="portrait" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <service android:name="com.example.musicplayer.PlayMusicService" />
    
            <receiver android:name="MusicAppWidgetProvider" >
                <intent-filter>
                    <action android:name="com.example.action.CLICK" />
                    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                </intent-filter>
    
                <meta-data
                    android:name="android.appwidget.provider"
                    android:resource="@xml/music_appwidget_info" />
            </receiver>
        </application>
  12. App Widgets的使用还需要在res/xml里定义music_appwidget_info.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
        android:initialLayout="@layout/music_appwidget"
        android:minHeight="300dp"
        android:minWidth="300dp"
        android:updatePeriodMillis="86400000" >
    </appwidget-provider>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值