音乐播放器,注意静态常量的使用命名要有明确具体的含义(查看API,若想获得音乐本身自带的歌手名以及图片等信息,可查看MediaMetadataRetrieve来获得详细信息)

另外注意Log的使用

注意权限的添加

读取存储卡的权限

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

音乐播放器带有缓存功能

主程序

package com.test.myplayer;

import android.support.v7.app.ActionBarActivity;
import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.test.service.MusicService;
import com.test.song.Song;
import com.test.song.SongAdapter;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
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.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.TextView;

public class MainActivity extends Activity implements OnClickListener {
    "静态常量是为了避免使用时拼写错误,另外因为有具体的名,所以使用时容易辨别它的作用。"
    public final static String ACTION = "com.test.myplayer";
    public final static int TYPE_PLAYER = 1;
    public final static int TYPE_PAUSE = 2;
    public final static int TYPE_SEEKTO = 3;
    public final static int TYPE_CHECKBOX_CHECKED = 4;
    // public final static int TYPE_PRE=4;
    // public final static int TYPE_NEXT=5;
    public final static String PLAY_TYPE_FLAG = "12";

    private TextView mAllTime;//获得歌曲的总时长
    private TextView mCurrentTime;//歌曲目前播放到的时长
    private ImageButton mButtonPrevious;//上一首
    private ImageButton mButtonNext;//下一首
    private Boolean mIsChecked = false;//默认checkbox的状态值
    private SeekBar seekBar1;//歌曲进度条
    private CheckBox mCheckBoxPlayer;//checkbox的实例
    private List<Song> mListSong;//Song的集合
    private LayoutInflater mInflater;
    private SongAdapter mAdapter;//song的适配器
    private ListView listView;//listView用来填充song
    private List<File> fileMp3 = new ArrayList<File>();//辅助的集合用来村完整的歌曲名
    private String songPath;//song的路径
    private int mSongNum;//当前播放的song在集合中的位置
    private MusicBroadcast mBroadcastReceiver;//广播接收者

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //给所用控件初始化
        mAllTime = (TextView) findViewById(R.id.all_time);
        mCurrentTime = (TextView) findViewById(R.id.current_time);
        mButtonPrevious = (ImageButton) findViewById(R.id.previous_button);
        mButtonNext = (ImageButton) findViewById(R.id.next_button);
        listView = (ListView) findViewById(R.id.list_song);
        mCheckBoxPlayer = (CheckBox) findViewById(R.id.play_checkbox);
        seekBar1 = (SeekBar) findViewById(R.id.seekBar1);
        //添加点击事件
        mButtonPrevious.setOnClickListener(this);
        mButtonNext.setOnClickListener(this);
        mBroadcastReceiver = new MusicBroadcast();
        //注册广播接收者
        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION);
        registerReceiver(mBroadcastReceiver, filter);
        //集合添加数据一定要在前面。
        mListSong = new ArrayList<Song>();
        mInflater = getLayoutInflater();
        //此时获得是一进入文件管理时的路径
        File customFiles = Environment.getExternalStorageDirectory();
        //然后通过新建文件则要查找的文件夹
        File file = new File(customFiles, "/netease/cloudmusic/Music");
        //下面屏蔽的是直接去存储卡自带的Music文件下查找
        // File file =
        // Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
        File[] files = file.listFiles();

        for (File innerfile : files) {
            String name = innerfile.getName();
            int index = name.lastIndexOf(".");
            String last = name.substring(index, name.length());// index包含最后一位判断最后的结尾方式
            //获取其中的全部的mp3文件
            if (last.equals(".mp3") || last.equals(".wav")) {
                fileMp3.add(innerfile);
                String songName = name.substring(0, index);
                Log.d("lala", songName);
                Song song = new Song(songName);
                mListSong.add(song);
            }

        }
        //给适配器传入值
        mAdapter = new SongAdapter(mInflater, mListSong);
        listView.setAdapter(mAdapter);
        //给每条listView添加点击事件
        listView.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                //通过position来判断当前点击的是哪一条list
                File sigleSong = fileMp3.get(position);
                //获得这是该条在集合中的位置,方便之后播放前一首或后一首时有个参照。
                mSongNum = position;
                Log.d("nihao", "" + sigleSong.getAbsolutePath());
                songPath = sigleSong.getAbsolutePath();
                //启动服务播放歌曲
                Intent intent = new Intent(getApplicationContext(), MusicService.class);
                //将所需的数据通过intent传递。
                intent.putExtra("musicpath", songPath);
                intent.putExtra(PLAY_TYPE_FLAG, TYPE_PLAYER);
                mCheckBoxPlayer.setChecked(true);
                startService(intent);
            }
        });
        mButtonPrevious.setOnClickListener(this);
        //滑动条添加拖动事件,一共有三个事件使用时注意查看API
        seekBar1.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                // fromUser判断是用户改变的滑块的值
                if (fromUser == true) {
                    Intent intent = new Intent(getApplicationContext(), MusicService.class);
                    intent.putExtra(PLAY_TYPE_FLAG, TYPE_SEEKTO);
                    intent.putExtra("SeekBar", progress);
                    startService(intent);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub
            }
        });
        //CheckBox的选择事件
        mCheckBoxPlayer.setOnCheckedChangeListener(new OnCheckedChangeListener() {

            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

                mIsChecked = isChecked;
                //isChecked是此时的状态,通过状态判断来做出相应的动作。
                if (mIsChecked) {
                    SharedPreferences sharedPreferences = getSharedPreferences(MusicService.FILE_NAME, MODE_PRIVATE);
                    songPath = sharedPreferences.getString(MusicService.PATH_PAUSE, "0");
                    int psuseTime = Integer.parseInt(sharedPreferences.getString(MusicService.PAUSE_NOTE, "0"));
                    Intent intent = new Intent(getApplicationContext(), MusicService.class);
                    intent.putExtra(PLAY_TYPE_FLAG, TYPE_CHECKBOX_CHECKED);
                    intent.putExtra("musicPausePath", songPath);
                    intent.putExtra(MusicService.PAUSE_NOTE, psuseTime);
                    startService(intent);

                } else {
                    Intent intent = new Intent(getApplicationContext(), MusicService.class);
                    intent.putExtra(PLAY_TYPE_FLAG, TYPE_PAUSE);
                    intent.putExtra("musicpath", songPath);
                    startService(intent);
                }

            }
        });

    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.previous_button:
            mSongNum -= 1;
            //都是通过intent发送给后台的服务
            File sigleSongPre = fileMp3.get(mSongNum);
            songPath = sigleSongPre.getAbsolutePath();
            Intent intent = new Intent(getApplicationContext(), MusicService.class);
            intent.putExtra("musicpath", songPath);
            //通过传入不同的类型方便后台服务根据类型来区分
            intent.putExtra(PLAY_TYPE_FLAG, TYPE_PLAYER);
            //别忘了启动服务
            startService(intent);
            break;
        case R.id.next_button:
            mSongNum += 1;
            File sigleSongNext = fileMp3.get(mSongNum);
            songPath = sigleSongNext.getAbsolutePath();
            Intent intentNext = new Intent(getApplicationContext(), MusicService.class);
            intentNext.putExtra("musicpath", songPath);
            intentNext.putExtra(PLAY_TYPE_FLAG, TYPE_PLAYER);
            startService(intentNext);
            break;
        }

    }

    @Override
    protected void onDestroy() {

        super.onDestroy();
        //销毁活动时,注意给broadcast取消注册。
        unregisterReceiver(mBroadcastReceiver);
    }
    /**
     *在类的内部创建Broadcast来接收服务传递过来的信息
     */
    class MusicBroadcast extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            //也是通过类型的判断来区分后台服务传递过来的信息
            int type = intent.getIntExtra("type", 0);
            switch (type) {
            case 1:
                int timeall = intent.getIntExtra("time", 0);
                seekBar1.setMax(timeall);
                int fen = (timeall / 1000) / 60;
                int miao = (timeall / 1000) % 60;
                String time1 = fen + ":" + miao;
                mAllTime.setText(time1);
                break;
            case 2:
                int timecurrent = intent.getIntExtra("time", 0);
                seekBar1.setProgress(timecurrent);
                int fen2 = (timecurrent / 1000) / 60;
                int miao2 = (timecurrent / 1000) % 60;
                String time2 = fen2 + ":" + miao2;
                mCurrentTime.setText(time2);
                break;

            default:
                break;
            }

        }

    }
}

音乐部分程序

MusicService服务程序

package com.test.service;
import java.io.IOException;
import com.test.myplayer.MainActivity;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;

public class MusicService extends Service {
    private MediaPlayer player;
    private String songPath;
    public final static String FILE_NAME = "pauseNotes";
    public final static String PATH_PAUSE = "PATH";
    public final static String PAUSE_NOTE = "PAUSE";//暂停时存入缓存的时间标志。
    public final static int ALL_TIME = 1;
    public final static int CURRENT_TIME = 2;

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("123", 156 + "");
        得到intent传递的值
        int playType = intent.getIntExtra(MainActivity.PLAY_TYPE_FLAG, 0);
        switch (playType) {
        case MainActivity.TYPE_PLAYER:
            if (player == null) {
                player = new MediaPlayer();
            }
            player.reset();
            //得到文件的路径
            songPath = intent.getStringExtra("musicpath");
            try {

                Log.d("123", songPath);
                player.setDataSource(songPath);
                player.prepare();
                int time = player.getDuration();
                player.start();
                Intent intent1 = new Intent(MainActivity.ACTION);
                intent1.putExtra("type", ALL_TIME);
                intent1.putExtra("time", time);
                //发送广播
                sendBroadcast(intent1);
                //开启线程
                MyMusicThread thread = new MyMusicThread();
                thread.start();
                // player.setOnPreparedListener(new OnPreparedListener() {
                //
                // @Override
                // public void onPrepared(MediaPlayer mp) {
                // if (mp.isPlaying()) {
                //
                // } else {
                //
                // mp.start();
                // int time = player.getDuration();
                // Intent intent = new Intent();
                // intent.putExtra("type", ALL_TIME);
                // intent.putExtra("time", time);
                // sendBroadcast(intent);
                // MyMusicThread thread = new MyMusicThread();
                // thread.start();
                // }
                //
                // }
                // });
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalStateException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            break;
        case MainActivity.TYPE_PAUSE:
            //得到此时的播放进度,然后暂停,并将进度存入缓存
            int pausePosition = player.getCurrentPosition();
            player.pause();

            SharedPreferences sharedPreferences = getSharedPreferences(FILE_NAME, MODE_PRIVATE);
            SharedPreferences.Editor editor = sharedPreferences.edit();
            editor.putString(PAUSE_NOTE, "" + pausePosition);

            editor.putString(PATH_PAUSE, songPath);
            editor.commit();
            break;
        case MainActivity.TYPE_SEEKTO:
            //拖动seekbar的响应事件
            int progress = intent.getIntExtra("SeekBar", 0);
            if (player != null) {
                player.seekTo(progress);
            }
            break;
        case MainActivity.TYPE_CHECKBOX_CHECKED:
            if (player == null) {
                    //防止文件为空
                    player = new MediaPlayer();

                if (!player.isPlaying()) {
                    songPath = intent.getStringExtra("musicPausePath");// 默认文件中所存储的音乐
                    int pauseTime = intent.getIntExtra(PAUSE_NOTE, 0);// 初始时要seekto到的时间。
                    Log.d("lalalla", songPath+pauseTime);
                    try {
                        player.setDataSource(songPath);
                        player.prepare();
                    } catch (IllegalArgumentException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (SecurityException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (IllegalStateException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                    int time = player.getDuration();
                    player.start();
                    player.seekTo(pauseTime);
                    Intent intent1 = new Intent(MainActivity.ACTION);
                    intent1.putExtra("type", ALL_TIME);
                    intent1.putExtra("time", time);
                    sendBroadcast(intent1);
                    MyMusicThread thread = new MyMusicThread();
                    thread.start();
                }
            }
            break;
        default:
            break;
        }

        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {

        super.onDestroy();
    }
    /**
     *创建一个线程不断调整时间然后发送广播给主程序。
     */
    class MyMusicThread extends Thread {
        @Override
        public void run() {
            while (player.isPlaying()) {
                int time = player.getCurrentPosition();
                Intent intent = new Intent(MainActivity.ACTION);
                intent.putExtra("type", CURRENT_TIME);
                intent.putExtra("time", time);
                sendBroadcast(intent);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

song程序,里面含有未开发部分

package com.test.song;

public class Song {
    private String name;
    private String author;
    private int Image;
    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public int getImage() {
        return Image;
    }

    public void setImage(int image) {
        Image = image;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Song(String name) {

        this.name = name;
    }

}

SongAdapter适配器程序

package com.test.song;

import java.io.File;
import java.util.List;

import com.test.myplayer.R;
import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMetadata;
import android.media.MediaMetadataRetriever;
import android.os.Build;
import android.os.Environment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;


@TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1)
public class SongAdapter extends BaseAdapter{
    private LayoutInflater mInflater;
    private List<Song> mListSong;
    public SongAdapter(LayoutInflater mInflater, List<Song> mListSong) {

        this.mInflater = mInflater;
        this.mListSong = mListSong;
    }

    @Override
    public int getCount() {

        return mListSong.size();
    }

    @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.list_song, null);
            viewHolder=new ViewHolder();
//          viewHolder.author=(TextView) convertView.findViewById(R.id.author);
//          viewHolder.image=(ImageView) convertView.findViewById(R.id.image);
            viewHolder.name=(TextView) convertView.findViewById(R.id.song_name);
            convertView.setTag(viewHolder);
        }else{
            viewHolder=(ViewHolder) convertView.getTag();
        }
        Song song=mListSong.get(position);
        /**
         * 当传入完整路径时使用该部分,通过路径使用MediaMetadataRetriever
         */
         "这里可以获得歌手名,但是同样需要传入的是完整的路径名"
//      File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
//      File[] files = file.listFiles();
//  
//      MediaMetadataRetriever mediaRetraiver=new MediaMetadataRetriever();
//      mediaRetraiver.setDataSource(files[0].getAbsolutePath());
//      String author=mediaRetraiver.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
//      if(author!=null){
//          viewHolder.author.setText(author);
//          
//      }else{
//          viewHolder.author.setText("<未知>");
//      }
        viewHolder.name.setText(song.getName());
//      byte[] image=mediaRetraiver.getEmbeddedPicture();
"这些屏蔽程序可以画一个图片添加到单条布局中,但是需要传入完整的文件路径"
//      if(image!=null){
//          Bitmap bitmap=BitmapFactory.decodeByteArray(image, 0, image.length);
//          viewHolder.image.setImageBitmap(bitmap);
//      }else{
//          viewHolder.image.setImageResource(R.drawable.ic_launcher);
//      }
        return convertView;
    }

    class ViewHolder{
        ImageView image;
        TextView author;

        TextView name;
    }



}

布局文件

主布局文件

<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:background="@drawable/playpage_background"
    android:orientation="vertical" >

    <ListView
        android:id="@+id/list_song"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:cacheColorHint="#ffffffff"
        android:divider="#55ff0000" >
    </ListView>
    <LinearLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center">
    <TextView 
        android:id="@+id/all_time"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
          android:textSize="15sp"
        android:text="00:00"/>

    <SeekBar
        android:id="@+id/seekBar1"
        android:layout_width="254dp"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_marginBottom="10dp" />
    <TextView 
        android:id="@+id/current_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"

        android:textSize="15sp"
        android:text="00:00"/>
</LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:orientation="horizontal" >

        <ImageButton
            android:id="@+id/previous_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="20dp"
            android:scaleX="2"
            android:scaleY="2"
            android:background="@drawable/previousbackground" />

        <CheckBox
            android:id="@+id/play_checkbox"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:scaleX="2"
            android:scaleY="2"
            android:background="@drawable/playback"
            android:button="@null" />

        <ImageButton
            android:id="@+id/next_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:scaleX="2"
            android:scaleY="2"
            android:background="@drawable/nextbackground" />
    </LinearLayout>

</LinearLayout>

单条Song的布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    android:gravity="center" >
    <ImageView 
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
    <TextView 
        android:id="@+id/song_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:text="ni"/>

    <TextView
        android:id="@+id/author"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        />

</LinearLayout>

checkbox的按压文件

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
       <item android:drawable="@drawable/desk_pause" android:state_checked="true"/>
    <!--没有加状态的放在后面-->
    <item android:drawable="@drawable/desk_play"/>

</selector>

button的按压文件

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:drawable="@drawable/desk_next_prs" android:state_pressed="true"/>
    <!--没有加状态的放在后面-->
    <item android:drawable="@drawable/desk_next"/>

</selector>

从MP3中获取图片需要用到一个jar包,可在云盘中找到,不过这个也是需要传绝对路径。

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值