Android多媒體——播放音頻

在Android中播放音頻文件一般使用MediaPlayer類來實現,它對多種格式的音頻文件提供了非常全面的控制方法,從而使得播放音樂的工作變得簡單
setDataSource():設置要播放的音頻文件的位置
prepare():在開始播放之前調用這個方法完成準備工作
start():開始或繼續播放
pause():暫停播放
reset():將MediaPlayer對象重置到剛剛創建的狀態
seekTo():從指定位置開始播放
stop():停止播放音頻,調用之後MediaPlayer對象無法再播放音頻
release():釋放掉與MediaPlayer對象相關的資源
isPlaying():判斷當前MediaPlayer是否正在播放
getDuration():獲取載入的音頻文件的時長

播放

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    private Button mButton;
    private MediaPlayer player=new MediaPlayer();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton= (Button) findViewById(R.id.button_musicplay);
        mButton.setOnClickListener(this);
        mButtonPause= (Button) findViewById(R.id.button_musicpause);
        mButtonPause.setOnClickListener(this);
        player.reset();
        File path= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
        File[]files=path.listFiles();
        try {
            player.setDataSource(files[0].getAbsolutePath());
            player.prepare();
            player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mp) {
                    mp.start();
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

但是要實現一個功能比較齊全的僅僅靠在主活動上加點擊事件是不可能的,因爲還要實現音樂可以後臺播放,並可以將播放過程用進度條顯示在主活動上,通過拖拉進度條可以是音樂從所拖動的位置繼續播放。要實現這麼些功能,就需要用到Android中的服務和廣播接收器。
首先搭建一個音樂播放器的主界面

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical" tools:context=".MainActivity">
    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1">

    </ListView>
    <SeekBar
        android:id="@+id/seekBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal" />
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:id="@+id/textview_currenttime"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="當前時間"/>
        <TextView
            android:id="@+id/textview_alltime"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:text="總時間"/>
    </RelativeLayout>
    <Button
        android:id="@+id/button_musicpause"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="暫停/播放"/>
</LinearLayout>

ListView中的佈局爲

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/imageview"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:src="@mipmap/ic_launcher"/>
    <TextView
        android:id="@+id/textview_artist"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="藝術家"/>
    <TextView
        android:id="@+id/textview_line"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="——"/>
    <TextView
        android:id="@+id/textview_musicname"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="歌曲名"/>
</LinearLayout>

在主活動中添加三個點擊事件,分別是ListView、開始暫停按鈕、SeekBar進度條,還有一個廣播接收器,用來接收廣播並對UI上的空間進行處理。

package com.example.administrator.mymediaplayer;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaPlayer;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.TextView;

import com.example.administrator.mymediaplayer.adapter.MusicAdapter;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.logging.SimpleFormatter;

public class MainActivity extends AppCompatActivity{
    private Button mButton;
    private Button mButtonPause;
    private ListView mListView;
    private TextView mTextviewAll;
    private TextView mTextviewCurrent;
    private SeekBar mSeekBar;
    private File[]musics;
    private MediaPlayer player=new MediaPlayer();
    private MusicReceiver musicReceiver;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTextviewAll= (TextView) findViewById(R.id.textview_alltime);
        mTextviewCurrent= (TextView) findViewById(R.id.textview_currenttime);

        //開始暫停按鈕的點擊事件
        mButtonPause= (Button) findViewById(R.id.button_musicpause);
        mButtonPause.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(getApplicationContext(),MusicService.class);
                intent.putExtra("type",Config.STOP_MUSIC);
                startService(intent);
            }
        });
        //SeekBar的點擊事件,將進度信息傳送給後臺服務
        mSeekBar= (SeekBar) findViewById(R.id.seekBar);
        mSeekBar.setOnSeekBarChangeListener(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) {
                Intent intent=new Intent(getApplicationContext(),MusicService.class);
                intent.putExtra("type",Config.START_SEEK_TO);
                intent.putExtra("progress",seekBar.getProgress());
                startService(intent);
            }
        });

        //找到sdCard
        File sdCard=Environment.getExternalStorageDirectory();
        //找到sdCard下的存放音樂的文件夾
        File musicFiles = new File(sdCard,"Music");
        //得到音樂文件夾下的所有歌曲
        musics=musicFiles.listFiles();
        //通過點擊放置歌曲的ListView中的某一條信息使得音樂可以在後臺播放
        mListView= (ListView) findViewById(R.id.listview);
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Intent intent=new Intent(getApplicationContext(),MusicService.class);
                intent.putExtra("type",Config.START_MUSIC);
                //把某一首歌的路徑傳送給服務類
                intent.putExtra("musicname",musics[position].getAbsolutePath());
                //啓動服務
                startService(intent);
            }
        });
        //定義ListView的適配器
        MusicAdapter adapter=new MusicAdapter(getLayoutInflater(),musics);
        //將適配器加載到ListView中
        mListView.setAdapter(adapter);
        //廣播接收器的註冊
        musicReceiver=new MusicReceiver();
        IntentFilter filter=new IntentFilter("com.music");
        registerReceiver(musicReceiver,filter);
    }
    //廣播接收器的取消註冊
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(musicReceiver);
    }

    //廣播接收器類
    class MusicReceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            //接收到的廣播的格式,通過不同的格式執行不同的操作
            int type=intent.getIntExtra("type", Config.ALL_TIME);
            //定義顯示歌曲播放和總時間的格式
            SimpleDateFormat format=new SimpleDateFormat("mm:ss");
            switch (type){
                //得到歌曲的總時間,並設置進度條總的長度,作爲給播放時間進度條的位置的參考
                case Config.ALL_TIME:
                    int alltime=intent.getIntExtra("alltime",Config.ALL_TIME);
                    mSeekBar.setMax(alltime);
                    String timeall=format.format(alltime);
                   mTextviewAll.setText(timeall);
                    break;
                //得到當前播放時間,並將其設置在進度條上
                case Config.CURRENT_TIME:
                    int time=intent.getIntExtra("time",Config.CURRENT_TIME);
                    mSeekBar.setProgress(time);
                    String timecurrent=format.format(time);
                   mTextviewCurrent.setText(timecurrent);
                    break;
                default:
                    break;
            }
        }
    }

}

然後既然有ListView,就需要一個Adapter類

package com.example.administrator.mymediaplayer.adapter;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMetadata;
import android.media.MediaMetadataRetriever;
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 com.example.administrator.mymediaplayer.R;

import java.io.File;

/**
 * Created by Administrator on 2015/9/10.
 */
public class MusicAdapter extends BaseAdapter {
    private LayoutInflater mInflater;
    private File[]musics;

    public MusicAdapter(LayoutInflater mInflater, File[] musics) {
        this.mInflater = mInflater;
        this.musics = musics;
    }

    @Override
    public int getCount() {
        return musics.length;
    }

    @Override
    public Object getItem(int position) {
        return position;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder=null;
        if (convertView==null){
            convertView=mInflater.inflate(R.layout.activity_music,null);
            viewHolder=new ViewHolder();
            viewHolder.textView= (TextView) convertView.findViewById(R.id.textview_musicname);
            viewHolder.textViewArtist= (TextView) convertView.findViewById(R.id.textview_artist);
            viewHolder.textViewLine= (TextView) convertView.findViewById(R.id.textview_line);
            viewHolder.imageView= (ImageView) convertView.findViewById(R.id.imageview);
            convertView.setTag(viewHolder);
        }else {
            viewHolder= (ViewHolder) convertView.getTag();
        }
        MediaMetadataRetriever metadata=new MediaMetadataRetriever();
        metadata.setDataSource(musics[position].getAbsolutePath());
        String artist=metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
        if (artist==null){
            viewHolder.textViewArtist.setText("UnKnow");
        }else {
            viewHolder.textViewArtist.setText(artist);
        }
        byte[]image=metadata.getEmbeddedPicture();
        if (image==null){
            viewHolder.imageView.setImageResource(R.mipmap.ic_launcher);
        }else {
            Bitmap bitmap= BitmapFactory.decodeByteArray(image,0,image.length);
            viewHolder.imageView.setImageBitmap(bitmap);
        }
        viewHolder.textView.setText(musics[position].getName());

        return convertView;
    }
    class ViewHolder{
        TextView textView;
        TextView textViewArtist;
        TextView textViewLine;
        ImageView imageView;
    }
}

中間獲取歌曲的作者和圖片的代碼其實放在這裏不是很合適,但是比較簡單,不需要來回傳遞信息。
然後就需要一個後臺服務類來進行後臺操作,在後臺中對活動中傳回來的信息做相應的後臺服務

package com.example.administrator.mymediaplayer;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

import java.io.IOException;

/**
 * Created by Administrator on 2015/9/10.
 */
public class MusicService extends Service {
    private MediaPlayer player;
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        int type=intent.getIntExtra("type",Config.START_MUSIC);
        switch (type){
            case Config.START_MUSIC:
                startMusic(intent);
                break;
            case Config.START_SEEK_TO:
                int progress=intent.getIntExtra("progress",0);
                player.seekTo(progress);
                break;
            case Config.STOP_MUSIC:
                if (player.isPlaying()){
                    player.pause();
                }else{
                    player.start();
                }
                new MusicPlayThread().start();
            default:
                break;
        }
        return super.onStartCommand(intent, flags, startId);
    }

    private void startMusic(Intent intent) {
        String musicpath=intent.getStringExtra("musicname");
        if (player==null){
            player=new MediaPlayer();
        }
        player.reset();
        try {
            player.setDataSource(musicpath);
            player.prepare();
            player.start();

        } catch (IOException e) {
            e.printStackTrace();
        }
        int time=player.getDuration();
        Intent intent2=new Intent("com.music");
        intent2.putExtra("type",Config.ALL_TIME);
        intent2.putExtra("alltime",time);
        sendBroadcast(intent2);

        new MusicPlayThread().start();
    }

    //用來發送當前廣播信息的線程
    class MusicPlayThread extends Thread{
        @Override
        public void run() {
            while (player.isPlaying()){
                int currenttime=player.getCurrentPosition();
                Intent intent=new Intent("com.music");
                intent.putExtra("type",Config.CURRENT_TIME);
                intent.putExtra("time",currenttime);
                sendBroadcast(intent);
                //由於Android對時間是一毫秒計算,所以需要休眠一段時間,降低發送頻率,不至於廣播積壓
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章