1. 程式人生 > >android-音樂播放器實現及原始碼下載(四)

android-音樂播放器實現及原始碼下載(四)

本系列博文,詳細講述一個音樂播放器的實現,以及從網路解析資料獲取最新推薦歌曲以及歌曲下載的功能。
功能介紹如下:
1、獲取本地歌曲列表,實現歌曲播放功能。
2、利用硬體加速感應器,搖動手機實現切換歌曲的功能
3、利用jsoup解析網頁資料,從網路獲取歌曲列表,同時實現歌曲和歌詞下載到手機本地的功能。
4、通知欄提醒,實現仿QQ音樂播放器的通知欄功能.
涉及的技術有:
1、jsoup解析網路網頁,從而獲取需要的資料
2、android中訪問網路,獲取檔案到本地的網路請求技術,以及下載檔案到本地實現斷點下載
3、執行緒池
4、圖片快取
5、service一直在後臺執行
6、手機硬體加速器
7、notification通知欄設計
8、自定義廣播
9、android系統檔案管理
主要技術是這些,其中,利用jsoup解析網路網頁,從而獲取需要的資料,請參考我的博文:

android中使用JSOUP如何解析網頁資料詳述

本篇博文講述播放介面的設計和實現,以及做最後的總結。
這裡寫圖片描述
這個是播放介面的截圖,仿QQ音樂播放介面的實現,程式碼如下:

/**
 * 2015年8月15日 16:34:37
 *  博文地址:http://blog.csdn.net/u010156024
 */
public class PlayActivity extends BaseActivity implements OnClickListener {

    private LinearLayout mPlayContainer;
    private ImageView mPlayBackImageView; // back button
private TextView mMusicTitle; // music title private ViewPager mViewPager; // cd or lrc private CDView mCdView; // cd private SeekBar mPlaySeekBar; // seekbar private ImageButton mStartPlayButton; // start or pause private TextView mSingerTextView; // singer private LrcView mLrcViewOnFirstPage; // single line lrc
private LrcView mLrcViewOnSecondPage; // 7 lines lrc private PagerIndicator mPagerIndicator; // indicator // cd view and lrc view private ArrayList<View> mViewPagerContent = new ArrayList<View>(2); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.play_activity_layout); setupViews(); } /** * 初始化view */ private void setupViews() { mPlayContainer = (LinearLayout) findViewById(R.id.ll_play_container); mPlayBackImageView = (ImageView) findViewById(R.id.iv_play_back); mMusicTitle = (TextView) findViewById(R.id.tv_music_title); mViewPager = (ViewPager) findViewById(R.id.vp_play_container); mPlaySeekBar = (SeekBar) findViewById(R.id.sb_play_progress); mStartPlayButton = (ImageButton) findViewById(R.id.ib_play_start); mPagerIndicator = (PagerIndicator) findViewById(R.id.pi_play_indicator); // 動態設定seekbar的margin MarginLayoutParams p = (MarginLayoutParams) mPlaySeekBar .getLayoutParams(); p.leftMargin = (int) (App.sScreenWidth * 0.1); p.rightMargin = (int) (App.sScreenWidth * 0.1); mPlaySeekBar.setOnSeekBarChangeListener(mSeekBarChangeListener); initViewPagerContent(); // 設定viewpager的切換動畫 mViewPager.setPageTransformer(true, new PlayPageTransformer()); mPagerIndicator.create(mViewPagerContent.size()); mViewPager.setOnPageChangeListener(mPageChangeListener); mViewPager.setAdapter(mPagerAdapter); mPlayBackImageView.setOnClickListener(this); } @Override protected void onResume() { super.onResume(); allowBindService(); } @Override protected void onPause() { allowUnbindService(); super.onPause(); } private OnPageChangeListener mPageChangeListener = new OnPageChangeListener() { @Override public void onPageSelected(int position) { if (position == 0) { if (mPlayService.isPlaying()) mCdView.start(); } else { mCdView.pause(); } mPagerIndicator.current(position); } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } }; /** * 拖動進度條 */ private SeekBar.OnSeekBarChangeListener mSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { int progress = seekBar.getProgress(); mPlayService.seek(progress); mLrcViewOnFirstPage.onDrag(progress); mLrcViewOnSecondPage.onDrag(progress); } }; private PagerAdapter mPagerAdapter = new PagerAdapter() { @Override public int getCount() { return mViewPagerContent.size(); } @Override public boolean isViewFromObject(View view, Object obj) { return view == obj; } /** * 該方法是PagerAdapter的預載入方法,系統呼叫 當顯示第一個介面時, * 第二個介面已經預載入,此時呼叫的就是該方法。 */ @Override public Object instantiateItem(ViewGroup container, int position) { container.addView(mViewPagerContent.get(position)); return mViewPagerContent.get(position); } @Override public void destroyItem(ViewGroup container, int position, Object object) { ((ViewPager) container).removeView((View) object); } }; /** * 初始化viewpager的內容 */ private void initViewPagerContent() { View cd = View.inflate(this, R.layout.play_pager_item_1, null); mCdView = (CDView) cd.findViewById(R.id.play_cdview); mSingerTextView = (TextView) cd.findViewById(R.id.play_singer); mLrcViewOnFirstPage = (LrcView) cd.findViewById(R.id.play_first_lrc); View lrcView = View.inflate(this, R.layout.play_pager_item_2, null); mLrcViewOnSecondPage = (LrcView) lrcView .findViewById(R.id.play_first_lrc_2); mViewPagerContent.add(cd); mViewPagerContent.add(lrcView); } @SuppressWarnings("deprecation") private void setBackground(int position) { Music currentMusic = MusicUtils.sMusicList.get(position); Bitmap bgBitmap = MusicIconLoader.getInstance().load( currentMusic.getImage()); if (bgBitmap == null) { bgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher); } mPlayContainer.setBackgroundDrawable( new ShapeDrawable(new PlayBgShape(bgBitmap))); } /** * 上一曲 * * @param view */ public void pre(View view) { mPlayService.pre(); // 上一曲 } /** * 播放 or 暫停 * * @param view */ public void play(View view) { if (mPlayService.isPlaying()) { mPlayService.pause(); // 暫停 mCdView.pause(); mStartPlayButton .setImageResource(R.drawable.player_btn_play_normal); } else { onPlay(mPlayService.resume()); // 播放 } } /** * 上一曲 * * @param view */ public void next(View view) { mPlayService.next(); // 上一曲 } /** * 播放時呼叫 主要設定顯示當前播放音樂的資訊 * * @param position */ private void onPlay(int position) { Music music = MusicUtils.sMusicList.get(position); mMusicTitle.setText(music.getTitle()); mSingerTextView.setText(music.getArtist()); mPlaySeekBar.setMax(music.getLength()); Bitmap bmp = MusicIconLoader.getInstance().load(music.getImage()); if (bmp == null) bmp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher); mCdView.setImage(ImageTools.scaleBitmap(bmp, (int) (App.sScreenWidth * 0.8))); if (mPlayService.isPlaying()) { mCdView.start(); mStartPlayButton .setImageResource(R.drawable.player_btn_pause_normal); } else { mCdView.pause(); mStartPlayButton .setImageResource(R.drawable.player_btn_play_normal); } } private void setLrc(int position) { Music music = MusicUtils.sMusicList.get(position); String lrcPath = MusicUtils.getLrcDir() + music.getTitle() + ".lrc"; mLrcViewOnFirstPage.setLrcPath(lrcPath); mLrcViewOnSecondPage.setLrcPath(lrcPath); } @Override public void onPublish(int progress) { mPlaySeekBar.setProgress(progress); if (mLrcViewOnFirstPage.hasLrc()) mLrcViewOnFirstPage.changeCurrent(progress); if (mLrcViewOnSecondPage.hasLrc()) mLrcViewOnSecondPage.changeCurrent(progress); } @Override public void onChange(int position) { setBackground(position); onPlay(position); setLrc(position); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.iv_play_back: finish(); break; default: break; } } @Override protected void onDestroy() { super.onDestroy(); } }

整個程式碼比較簡單,需要特別說明的是,播放介面並沒有和MediaPlayer類耦合,而是和PlayService耦合,對於歌曲的播放,暫停,下一曲、上一曲等操作都和PlayService進行互動,這樣做好處非常明顯,整個專案中所有對播放歌曲的操作都通過PlayService進行,同時,通過PlayService可以更新通知欄資訊。

**

總結

**
一、
專案中,用到了比較多的回撥介面,回撥介面確實非常好用,解決之間的耦合關係。
service類通過回撥介面OnMusicEventListener中的方法,呼叫activity類中的

public void onPublish(int percent);
public void onChange(int position);

兩個方法來實現UI的更新。
而Activity類中通過基類BaseActivity中的以下程式碼

private ServiceConnection mPlayServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            L.l(TAG, "play--->onServiceDisconnected");
            mPlayService = null;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mPlayService = ((PlayService.PlayBinder) service).getService();
            mPlayService.setOnMusicEventListener(mMusicEventListener);
            onChange(mPlayService.getPlayingPosition());
        }
    };

    private ServiceConnection mDownloadServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            L.l(TAG, "download--->onServiceDisconnected");
            mDownloadService = null;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mDownloadService = ((DownloadService.DownloadBinder) service).getService();
        }
    };

    /**
     * 音樂播放服務回撥介面的實現類
     */
    private PlayService.OnMusicEventListener mMusicEventListener = 
            new PlayService.OnMusicEventListener() {
        @Override
        public void onPublish(int progress) {
            BaseActivity.this.onPublish(progress);
        }

        @Override
        public void onChange(int position) {
            BaseActivity.this.onChange(position);
        }
    };

完成與兩個service服務的繫結,實現與service服務的互動。

以上是兩個非常關鍵的方法實現兩者之間的互動,應該說是非常好的處理兩者之間的資訊傳遞。
其中在獲取網路歌曲列表的過程中,也是用到了回撥介面的方法進行資料傳遞,SongsRecommendation類中的

/**
     * 回撥介面 獲取資料之後,通過該介面設定資料傳遞
     */
    public interface OnRecommendationListener {
        public void onRecommend(ArrayList<SearchResult> results);
    }

這個介面就是進行結果傳遞的回撥介面。所以大家在看程式碼的過程中,務必瞭解並看懂回撥介面到底是如何進行的。
二、

專案中使用了比較多的執行緒池問題,大家務必瞭解執行緒池的用法。其實執行緒池用法非常簡單。

private ExecutorService mThreadPool;
mThreadPool = Executors.newSingleThreadExecutor();
mThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                ArrayList<SearchResult> result = getMusicList();
                if (result == null) {
                    mHandler.sendEmptyMessage(Constants.FAILED);
                    return;
                }
                mHandler.obtainMessage(Constants.SUCCESS, result)
                        .sendToTarget();
            }
        });

以上便是執行緒池的基本用法了。easy!!

四、
本專案中的兩個service服務也是關鍵內容,關於service服務一直在後臺執行的內容,請參考我的博文:實現音樂播放器後臺Service服務一直存在的解決思路

以上四點是專案中比較重要的部分,博文寫的比較倉促,難免有什麼不好的地方,如果大家有什麼疑問或問題,歡迎給我留言,我一定儘快回覆大家!^_^【握手】

PS:
程式碼已更新,本地手機沒有MP3檔案的話,也不會出現崩潰。

相關推薦

android-音樂播放實現原始碼下載

本系列博文,詳細講述一個音樂播放器的實現,以及從網路解析資料獲取最新推薦歌曲以及歌曲下載的功能。 功能介紹如下: 1、獲取本地歌曲列表,實現歌曲播放功能。 2、利用硬體加速感應器,搖動手機實現切換歌曲的功能 3、利用jsoup解析網頁資料,從網路

android-音樂播放實現原始碼下載

從本文開始,詳細講述一個音樂播放器的實現,以及從網路解析資料獲取最新推薦歌曲以及歌曲下載的功能。 功能介紹如下: 1、獲取本地歌曲列表,實現歌曲播放功能。 2、利用硬體加速感應器,搖動手機實現切換歌曲的功能 3、利用jsoup解析網頁資料,從網路獲取歌曲

android-----音樂播放的音量控制功能開發

一、佈局檔案 在RelativeLayout佈局裡設定一個ImageButton,點選其彈出一個SeekBar(用於音量調節), 再在其下面巢狀一個RelativeLayout,裡面包含兩個ImageView元件、一個SeekBar元件。 此外, anim---push_u

Android 音樂播放實現歌詞顯示

LrcHandle.java: package com.example.welcome; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import

Android音樂播放邊播邊快取AndroidVideoCache的使用方法

最近在做音樂播放器類的需求,做了一段時間,抽出一段時間來整理一下。【前言】首先,記錄一下如何找到我們與需求相關的開源庫。可以在GitHub上進行搜尋,我的主要需求是邊播邊快取。因此我在GitHub上搜索“android”“cache”等關鍵詞,搜到了這個 AndroidVid

開源視訊播放IjkPlayer使用記錄之--多音軌的探路之旅

具體資訊可以見下:ffprobe -i test.mkv ffprobe version N-82166-g894e7ef Copyright (c) 2007-2016 the FFmpeg developers built with Apple LLVM version 8.0.0 (clang-80

Android音樂播放的簡單實現

1、MusicService 音樂播放器的Service,裡面獲取音樂檔案,封裝了MediaPlayer,實現播放上一首和下一首,播放,停止,封裝成方法供Activity呼叫,獲取音樂的當前進度,總長度、名字,通過傳送廣播的方式發給Activity pa

Android 自定義音樂播放實現

Android自定義音樂播放器一:首先介紹用了哪些Android的知識點:1 MediaPlayer工具來播放音樂2 Handle。因為存在定時任務(歌詞切換,動畫,歌詞進度條變換等)需要由Handle來處理Ui相關內容3 動態許可權申請(該應用程式讀取本地歌曲,並且設定音質

android 音樂播放最簡單的實現

package com.example.mouse.laymen; import android.app.Activity; import android.media.MediaPlayer; import android.os.Bundle; import androi

Android 音樂播放實現自定義按鈕的實現

Android 系統提供了MediaPlayer控制元件,讓我們能夠利用它實現音訊的播放。 而從學Android開始,在看教程的時候,我就想,我要自己做一個音樂播放器,因為一個完整的音樂播放器是有很多功能的,它涉及到很多方面的知識,可以幫助我們更好地學習和掌握關於Andro

android音樂播放開發 SweetMusicPlayer 載入歌曲列表

路徑 本地 exc tao near import 設置 優先 特殊 上一篇寫了播放器的總體實現思路,http://blog.csdn.net/huweigoodboy/article/details/39855653,如今來總結下載入歌曲列表。 代碼地址

[嵌入式Linux專案實戰開發]基於QT4.7.4的音樂播放實現與設計【2018年給力專案】

[嵌入式Linux專案實戰開發]基於QT4.7.4的音樂播放器實現與設計【2018年給力專案】是【創科之龍】團隊aiku嵌入式視訊教程系列製作的現有的音樂播放器。 主要功能實現: 1.新建工程,基類選擇Qwidget。雙擊開啟介面檔案,在介面檔案中建立label顯示時間、若干個tool

android 音樂播放介面

一、團隊成員 姓名 學號 部落格地址 成凱 1600802002 http://www.cnblogs.com/ck03/ 黨康 1600802004 http://www.cnblogs.com/lxxxy/ 趙樂 1600802034 http://www.cnblogs.com/Z-y-H/ 二、

Android音樂播放

主要功能介紹         實現音樂暫停,播放,下一首,上一首; 程式執行截圖 核心程式碼解析     功能鍵的實現 public void onClick(View v) { switch(v.getId()){

音樂播放——實現後臺播放、搖搖切歌等功能

前言 首先宣告,小白一隻,android完全自學,若程式碼中有不妥或更簡便的方法求指教(大佬帶帶我)。。。 APP 歡迎介面 主介面 音樂介面 實現功能 1.遍歷本地音樂 2.音樂後臺播放 3.音樂封面之黑礁唱

go學習筆記(6)——音樂播放實現

宣告:首先說明這個專案來自於許式偉的《Go語言程式設計》,書中也給出了詳盡的原始碼描述,不過程式碼中還是存在一些問題,首先說明一下所存在的問題問題一:音樂的播放結構體中定義了五個屬性欄位,在後面賦值的時候又變成了六個欄位的賦值問題二:Play函式在呼叫的時候多傳遞了兩個引數,

個人APP樂逗專案(內部音樂播放實現,開源MaskProgressView使用)

1.所需工具 進度條: GitHub 獲取: 也可以免積分下載 音樂播放器進度條library : 2.效果演示 實現功能: (1) 音樂播放 (音樂播放地址URL) (2) 進度條 (MaskProgressVi

android 音樂播放小案例

案例主目的是為了複習一下Service服務 Handler訊息機制 學習一下mediaplayer類 和了解自定義控制元件 MainAcitivity.java package com.alleged.musicPlay; import android

android音樂播放播放音樂

自己在練習時寫了一個音樂播放器,但是放音樂的時候會卡 經過調查,才知道是在設定seekbar監聽的onProgressChanged這個方法中設定了player.seekTo(seekBar.getProgress());這樣就造成了音樂播放進度條改變,然後進度條的改變又會

Android音樂播放中的歌詞同步學習分析

在網上查了一下資料,感謝 http://www.cr173.com/html/20184_1.html 給了我思路,可以說他提供了最基本的歌詞同步的功能,我在其上面添加了自己的修改的程式碼。 主要是自己為了實現歌詞同步,並且通過移動seekbar,改變歌曲的歌詞位置。當然還