简单实现Android本地音乐的播放

首先整理一下思路,为了实现该功能,我们只需要主界面和音乐界面,外加一个广播接收者和service.这几个就构成了这个小项目的核心.

1主界面

public class MainActivity extends AppCompatActivity {
    TopBar mTopBar;
    private boolean isUpdate = true;//是否更新界面
    private TextView txtSongname;
    private AudioManager audioManager;//音频管理器
    private boolean isMusicPlaying = false;
    private String MusicOperateAction = StaticValues.MusicOperateAction;
    private String MusicOperateData = StaticValues.MusicOperateData;
    private MusicBroadcast musicBroadCast;
    private ImageView imgvPre, imgvPauseStart, imgvNext, imgvPlus, imgvMinus;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == StaticValues.MUSIC_PROGRESS_UPDATE) {
                String[] allDatas = msg.obj.toString().trim().split(",");
                if (allDatas.length == 5 && isUpdate)
                    updateMusicControls(allDatas);
            }
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 监听广播
        musicBroadCast = new MusicBroadcast(MainActivity.this, handler);
        musicBroadCast.registReceiver();
        audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
        txtSongname = (TextView) this.findViewById(R.id.txt_songname);
        txtSongname.setOnClickListener(ocltwo);
        imgvPre = (ImageView) this.findViewById(R.id.imgv_pre);
        imgvPre.setOnClickListener(ocl);
        imgvPauseStart = (ImageView) this.findViewById(R.id.imgv_pause_start);
         imgvPauseStart.setOnClickListener(oclthree);
        imgvNext = (ImageView) this.findViewById(R.id.imgv_next);
        imgvNext.setOnClickListener(ocl);
        imgvPlus = (ImageView) this.findViewById(R.id.imgv_plus);
        imgvPlus.setOnClickListener(ocltwo);
        imgvMinus = (ImageView) this.findViewById(R.id.imgv_minus);
        imgvMinus.setOnClickListener(ocltwo);
    }

    private void updateMusicControls(String[] allDatas) {
        String titleStr = allDatas[0];
        String isPlayTxt = allDatas[4];
        if (!titleStr.equals(txtSongname.getText()))
            txtSongname.setText(titleStr);
        if (isPlayTxt.equals("1")) {
            if (!isMusicPlaying) {
                isMusicPlaying = true;
                imgvPauseStart.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.icon_music_circle_pause1));
            }
        }else {
            if (isMusicPlaying) {
                isMusicPlaying = false;
                imgvPauseStart.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.icon_music_circle_play1));
            }
        }
    }

    /**
     * 控制播放上一首,下一首
     */
    View.OnClickListener ocl = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String str = "";
            Intent intent = new Intent(StaticValues.MusicOperateAction);
            switch (v.getId()) {
                case R.id.imgv_pre:
                    str = StaticValues.MUSIC_PRE + "," + 0;
                    break;
                case R.id.imgv_next:
                    str = StaticValues.MUSIC_NEXT + "," + 0;
                    break;
            }
            intent.putExtra(MusicOperateData, str);
            sendBroadcast(intent);
        }
    };

    /**
     * 控制音量打下以及跳转到专门的音乐界面
     */
    View.OnClickListener ocltwo = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.imgv_plus:
                    audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI);
                    break;
                case R.id.imgv_minus:
                    audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_LOWER,AudioManager.FLAG_SHOW_UI);
                    break;
                case R.id.txt_songname:
                    Intent intent=new Intent(MainActivity.this,MusicActivity.class);
                    startActivity(intent);
                    break;
            }

        }
    };
View.OnClickListener oclthree=new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String str = "";
        Intent intent = new Intent(MusicOperateAction);
        switch (v.getId()) {
            case R.id.imgv_pause_start:
                if (isMusicPlaying) {
                    isMusicPlaying = false;
                    imgvPauseStart.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.icon_music_circle_play1));
                } else {
                    isMusicPlaying = true;
                    imgvPauseStart.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.icon_music_circle_pause1));
                }
                str = StaticValues.MUSIC_START_OR_PAUSE + "," + 0;
                break;
        }
        intent.putExtra(MusicOperateData, str);
        sendBroadcast(intent);
    }
};
}

在这个activity中,我们主要任务,就是为他注册一个广播接收者,让他实现广播的发送和接收,定义一个handler将他赋给广播接收者,使得能够进行同时对音乐播放的状态做处理.

2音乐界面

public class MusicActivity extends AppCompatActivity {
    private Visualizer visualizer;//定义示波器
    private MusicBroadcast musicBroadCast = null;
    private LinearLayout lin_back;
    private MyVisualizerView viewSpectrum;
    private ListView listView;
    private ImageView imgvPre, imgvPauseStart, imgvNext, imgvPlus, imgvMinus;
    private TextView txtTitle, txtAlbum, txtPresentTime, txtAllTime, txtPrompt;
    private SeekBar seekBar;
    private AudioManager audioManager;
    private boolean isPlaying = false;
    private String MusicOperateAction = StaticValues.MusicOperateAction;
    private String MusicOperateData = StaticValues.MusicOperateData;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == StaticValues.MUSIC_PROGRESS_UPDATE) {
                String[] allDatas = msg.obj.toString().trim().split(",");
                if (allDatas.length == 5)
                    updateMusicControls(allDatas);
            }
        }
    };

    /**
     * 更新控件
     *
     * @param allDatas
     */
    private void updateMusicControls(String[] allDatas) {
        String titleStr = allDatas[0];
        String albumStr = allDatas[1];
        String presentTimeStr = getFormatedDateTime("mm:ss", Integer.parseInt(allDatas[2])) + "";
        String allTimeStr = getFormatedDateTime("mm:ss", Integer.parseInt(allDatas[3])) + "";
        String isPlayTxt = allDatas[4];
        if (!titleStr.equals(txtTitle.getText()))
            txtTitle.setText(titleStr);
        if (!albumStr.equals(txtAlbum.getText()))
            txtAlbum.setText(albumStr);
        if (!allTimeStr.equals(txtAllTime.getText()))
            txtAllTime.setText(allTimeStr);
        if (seekBar.getMax() != Integer.parseInt(allDatas[3]))
            seekBar.setMax(Integer.parseInt(allDatas[3]));
        if (!presentTimeStr.equals(txtPresentTime.getText())) {
            txtPresentTime.setText(presentTimeStr);
            seekBar.setProgress(Integer.parseInt(allDatas[2]));
        }
        if (isPlayTxt.equals("1")) {
            if (!isPlaying) {
                isPlaying = true;
                visualizer.setEnabled(true);
                imgvPauseStart.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.icon_music_circle_pause1));
            }
        } else {
            if (isPlaying) {
                isPlaying = false;
                visualizer.setEnabled(false);
                imgvPauseStart.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.icon_music_circle_play1));
            }
        }
    }

    private String getFormatedDateTime(String pattern, long dateTime) {
        SimpleDateFormat sDateFormat = new SimpleDateFormat(pattern);
        return sDateFormat.format(new Date(dateTime + 0));
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_music);
        initView();
        musicBroadCast = new MusicBroadcast(MusicActivity.this, handler);
        musicBroadCast.registReceiver();
        show();
    }
/**
 * 显示歌曲的ListView初始化*/
    private void show() {
        MyApplication myApplication= (MyApplication) getApplication();
        final List<Song>songs=myApplication.getSongs();
        if (songs!=null&&songs.size()>0){
            List<HashMap<String,Object>>data=new ArrayList<>();
            for (Song song:songs){
                HashMap<String,Object>map=new HashMap<>();
                map.put("title",song.getTitle());
                map.put("duration",getFormatedDateTime("mm:ss",song.getDuration()));
                map.put("singer",song.getSinger());
                data.add(map);
            }
            SimpleAdapter adapter=new SimpleAdapter(MusicActivity.this,data,R.layout.music_item,
                    new String[]{"title", "duration", "singer"},
                    new int[]{R.id.txt_title, R.id.txt_duration, R.id.txt_singer});
            listView.setAdapter(adapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Intent intent=new Intent(StaticValues.MusicOperateAction);
                String str=StaticValues.MUSIC_PLAY+","+position;
                intent.putExtra(StaticValues.MusicOperateData,str);
                sendBroadcast(intent);
                txtTitle.setText(songs.get(position).getTitle());
                txtAlbum.setText(songs.get(position).getAlbum());
                txtAllTime.setText(getFormatedDateTime("mm:ss",songs.get(position).getDuration()));
            }
        });
        }else {
            listView.setVisibility(View.GONE);
            txtPrompt.setVisibility(View.VISIBLE);
        }
    }

    private void initView() {
        audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
        listView = (ListView) this.findViewById(R.id.list_music);
        txtPrompt = (TextView) this.findViewById(R.id.txt_prompt);
        txtTitle = (TextView) this.findViewById(R.id.txt_title);
        txtAlbum = (TextView) this.findViewById(R.id.txt_album);
        txtPresentTime = (TextView) this.findViewById(R.id.txt_present_time);
        txtAllTime = (TextView) this.findViewById(R.id.txt_all_time);
        seekBar = (SeekBar) this.findViewById(R.id.sb_music);
        seekBar.setOnSeekBarChangeListener(seekBarChange);
        imgvPre = (ImageView) this.findViewById(R.id.imgv_pre);
        imgvPre.setOnClickListener(ocl);
        imgvPauseStart = (ImageView) this.findViewById(R.id.imgv_pause_start);
        imgvPauseStart.setOnClickListener(oclthree);
        imgvNext = (ImageView) this.findViewById(R.id.imgv_next);
        imgvNext.setOnClickListener(ocl);
        imgvPlus = (ImageView) this.findViewById(R.id.imgv_plus);
        imgvPlus.setOnClickListener(ocltwo);
        imgvMinus = (ImageView) this.findViewById(R.id.imgv_minus);
        imgvMinus.setOnClickListener(ocltwo);
        viewSpectrum = (MyVisualizerView) this.findViewById(R.id.view_spectrum);
        setUpVisualizer();
    }

    /**
     * 频谱数据采集
     */
    private void setUpVisualizer() {
        MyApplication myApplication = (MyApplication) getApplication();
        visualizer = new Visualizer(myApplication.getMediaObject());
        visualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
        visualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
            @Override
            public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes, int i) {
                Log.i("AAA", "bytes:" + bytes.length);
                viewSpectrum.updateVisualizer(bytes);
            }

            @Override
            public void onFftDataCapture(Visualizer visualizer, byte[] bytes, int i) {
            }
        }, Visualizer.getMaxCaptureRate() / 2, true, false);
        visualizer.setEnabled(false);//数据采样
    }

    SeekBar.OnSeekBarChangeListener seekBarChange = new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            if (fromUser) {
                Intent intent = new Intent(StaticValues.MusicOperateAction);
                String str = StaticValues.MUSIC_SEEK + "," + progress;
                intent.putExtra(StaticValues.MusicOperateData, str);
                sendBroadcast(intent);
            }
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {

        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {

        }
    };

    View.OnClickListener ocl = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String str = "";
            Intent intent = new Intent(StaticValues.MusicOperateAction);
            switch (v.getId()) {
                case R.id.imgv_pre:
                    str = StaticValues.MUSIC_PRE + "," + 0;
                    visualizer.setEnabled(true);
                    break;
                case R.id.imgv_next:
                    str = StaticValues.MUSIC_NEXT + "," + 0;
                    visualizer.setEnabled(true);
                    break;
            }
            intent.putExtra(StaticValues.MusicOperateData, str);
            sendBroadcast(intent);
        }
    };

    View.OnClickListener ocltwo = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.imgv_plus:
                    audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI);
                    break;
                case R.id.imgv_minus:
                    audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI);
                    break;
            }

        }
    };

    View.OnClickListener oclthree = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String str = "";
            Intent intent = new Intent(StaticValues.MusicOperateAction);
            if (isPlaying) {
                isPlaying = false;
                visualizer.setEnabled(false);
                imgvPauseStart.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.icon_music_circle_play1));
            } else {
                isPlaying = true;
                visualizer.setEnabled(true);
                imgvPauseStart.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.icon_music_circle_pause1));
            }
            str = StaticValues.MUSIC_START_OR_PAUSE + "," + 0;
            intent.putExtra(MusicOperateData, str);
            sendBroadcast(intent);

        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        visualizer.setEnabled(false);
        visualizer.release();
        musicBroadCast.unRegistReceiver();
    }
}

音乐界面的主体功能和MainActivity并没有太多的区别,也是注册一个广播接收者秉将handler对象赋给广播接收者来实现数据的传输.但是在这个基础上定义了歌单列表和一个自定义控件来实现音乐播放的频谱数据采集

3 广播接收者

public class MusicBroadcast extends BroadcastReceiver {
    private Context mContext;
    private Handler mHandler;
    private IntentFilter filter = new IntentFilter();
    private String MusicOperateAction = StaticValues.MusicOperateAction;
    private String ProgressUpdateAction = StaticValues.ProgressUpdateAction;

    public MusicBroadcast(Context context, Handler handler) {
        mContext = context;
        mHandler = handler;
        filter.addAction(MusicOperateAction);
        filter.addAction(ProgressUpdateAction);
    }

    public Intent registReceiver() {
        //把原始的注册方法封装进这,供其他地方调用
        return mContext.registerReceiver(this, filter);
    }

    public void unRegistReceiver() {
        mContext.unregisterReceiver(this);
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        if (mHandler == null)
            return;
        String action = intent.getAction();
        Message msg = new Message();
        if (action.equals(MusicOperateAction)) {
            String str = intent.getStringExtra(StaticValues.MusicOperateData);
            msg.what = StaticValues.MUSIC_OPERATE;
            msg.obj = str;
        } else if (action.equals(ProgressUpdateAction)) {
            String str = intent.getStringExtra(StaticValues.UIUpdateData);
            msg.what = StaticValues.MUSIC_PROGRESS_UPDATE;
            msg.obj = str;
        }
        mHandler.sendMessage(msg);
    }
}

讲对广播的注册方法也放进了广播接收者本身,使得在创建广播接收者对象的时候可以方便的调用注册和注销.将对接收到的广播放在onReceive中进行处理并进行对应的消息转发

4 service

public class MusicService extends Service {
    private MyApplication myApplication = null;
    private MusicBroadcast musicBroadCast = null;
    private MediaPlayer myPlayer;
    private int musicIndex = 0;
    private int musicMax = 0;
    private String songName = "", albumName = "";
    private int presentTime = 0, allTime = 0;
    private String isPlay = "0";//0:停止,1:播放
    private List<Song> songs = null;
    private Timer timerMusic;//定时线程
    /**
     * 进行对音乐播放的实际操作
     */
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == StaticValues.MUSIC_OPERATE) {
                String[] datas = msg.obj.toString().split(",");
                String a=datas[0];
                int info = Integer.parseInt(datas[0]);
                if (info == StaticValues.MUSIC_PLAY) {
                    musicIndex = Integer.parseInt(datas[1]);
                    playMusic();
                } else if (info == StaticValues.MUSIC_START_OR_PAUSE) {
                    playOrPauseMusic();
                } else if (info == StaticValues.MUSIC_PRE) {
                    preMusic();
                } else if (info == StaticValues.MUSIC_NEXT) {
                    nextMusic();
                } else if (info == StaticValues.MUSIC_STOP) {
                    stopMusic();
                } else if (info == StaticValues.MUSIC_SEEK) {
                    seekTo(Integer.parseInt(datas[1]));
                }
            }
        }
    };

    public MusicService() {
        myPlayer = new MediaPlayer();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        musicBroadCast = new MusicBroadcast(MusicService.this, handler);
        musicBroadCast.registReceiver();
        myApplication = (MyApplication) getApplication();
        myApplication.setMediaObject(myPlayer.getAudioSessionId());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        songs = AudioUtils.getAllSongs(MusicService.this);
        if (songs != null && songs.size() > 0) {
            musicMax = songs.size();
            myApplication.setSongs(songs);
            playMusic();
            playOrPauseMusic();
        }
        timerMusic = new Timer();
        timerMusic.schedule(new TimerTask() {
            @Override
            public void run() {
                sendSongData();
            }
        }, 0, 1000);
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 发送歌曲数据
     */
    private void sendSongData() {
        try {
            if (songs != null && songs.size() > 0 && myPlayer != null) {
                presentTime = myPlayer.getCurrentPosition();//获取当前播放进度
                allTime = songs.get(musicIndex).getDuration();//获取当前播放歌曲的总时长
                songName = songs.get(musicIndex).getTitle();
                albumName = songs.get(musicIndex).getAlbum();//获取专辑名
                if (myPlayer.isPlaying())
                    isPlay = "1";
                else
                    isPlay = "0";
                String allData = songName + ",——" + albumName + "——," + presentTime + "," + allTime + "," + isPlay;
                Intent intent = new Intent(StaticValues.ProgressUpdateAction);
                intent.putExtra(StaticValues.UIUpdateData, allData);
                sendBroadcast(intent);
            }
        } catch (Exception e) {

        }
    }

    /**
     * 设置当前播放点
     */
    private void seekTo(int position) {
        if (myPlayer != null) {
            myPlayer.seekTo(position);
        }
    }

    /**
     * 停止播放音乐
     */
    private void stopMusic() {
        try {
            if (songs != null && songs.size() > 0 && myPlayer != null) {
                myPlayer.stop();
            }
        } catch (Exception e) {

        }
    }

    private void nextMusic() {
        try {
            if (songs != null && songs.size() > 0 && myPlayer != null) {
                stopMusic();
                if (musicIndex < musicMax - 1) {
                    musicIndex++;
                } else {
                    musicIndex = 0;
                }
                playMusic();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void preMusic() {
        try {
            if (songs != null && songs.size() > 0 && myPlayer != null) {
                stopMusic();
                if (musicIndex > 0) {
                    musicIndex--;
                } else {
                    musicIndex = musicMax - 1;
                }
                playMusic();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 暂停或开始播放音乐
     */
    private void playOrPauseMusic() {
        try {
            if (myPlayer.isPlaying()) {
                myPlayer.pause();
            } else {
                myPlayer.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void playMusic() {
        try {
            if (songs != null && songs.size() > 0 && myPlayer != null) {
                songs = myApplication.getSongs();
                myPlayer.reset();//重新设置要播放的音乐
                myPlayer.setDataSource(songs.get(musicIndex).getFileUrl());
                myPlayer.prepare();//预加载音频
                myPlayer.seekTo(0);
                myPlayer.start();
                myPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                    @Override
                    public void onCompletion(MediaPlayer mp) {
                        nextMusic();
                    }
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 更新播放列表
     */
    public void updateMusicData() {
        stopMusic();
        songs = myApplication.getSongs();
        musicMax = songs.size();
        musicIndex = 0;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        try {
            if (timerMusic != null) {
                timerMusic.cancel();
                timerMusic = null;
            }
            musicBroadCast.unRegistReceiver();
            stopMusic();
            myPlayer = null;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

对音乐进行实际操作的对象,接收从广播接收者那里进过处理的数据,然后进行对应的操作,并且定时通过广播发送歌曲信息,使界面信息与实际播放信息实时更新

5自定义控件

public class MyVisualizerView extends View {
    private byte[] bytes;
    private float[] points;
    private Paint paint = new Paint();
    private Rect rect = new Rect();
    private byte type = 1;

    public MyVisualizerView(Context context) {
        super(context);
        init();
    }

    public MyVisualizerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        bytes = null;
        paint.setStrokeWidth(1F);
        paint.setAntiAlias(true);
        paint.setColor(Color.GREEN);
        paint.setStyle(Paint.Style.FILL);
    }

    public void updateVisualizer(byte[] ftt) {
        bytes = ftt;
        //通知组件重绘
        invalidate();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //当触碰该组件时,切换波形类型
        if (event.getAction() != MotionEvent.ACTION_DOWN) {
            return false;
        }
        type++;
        if (type >= 3) {
            type = 0;
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (bytes == null) return;
        //绘制白色背景
        canvas.drawColor(Color.WHITE);
        //记录组件的宽高
        rect.set(0, 0, getWidth(), getHeight());
        switch (type) {
            case 0:
                //绘制块型波形图
                for (int i = 0; i < bytes.length - 1; i++) {
                    float left = getWidth() * i / (bytes.length - 1);
                    float top = rect.height() - (byte) (bytes[i + 1] + 128) * rect.height() / 128;
                    float right = left + 1;
                    float bottom = rect.height();
                    canvas.drawRect(left, top, right, bottom, paint);
                }
                break;
            case 1:
                //绘制柱型波形图
                for (int i = 0; i < bytes.length - 1; i += 18) {
                    float left = rect.width() * i / (bytes.length - 1);
                    float top = rect.height() - (byte) (bytes[i + 1] + 128) * rect.height() / 128;
                    float right = left + 6;
                    float bottom = rect.height();
                    canvas.drawRect(left, top, right, bottom, paint);
                }
                break;
            case 2:
                //绘制曲线波形图
                if (points == null || points.length < bytes.length * 4) {
                    points = new float[bytes.length * 4];
                }
                for (int i = 0; i < bytes.length - 1; i++) {
                    points[i * 4] = rect.width() * i / (bytes.length - 1);
                    points[i * 4 + 1] = (rect.height() / 2) + ((byte) (bytes[i] + 128)) * 128 / (rect.height() / 2);
                    points[i * 4 + 2] = rect.width() * (i + 1) / (bytes.length - 1);
                    points[i * 4 + 3] = (rect.height() / 2) + ((byte) (bytes[i + 1] + 128)) * 128 / (rect.height() / 2);
                }
                canvas.drawLines(points, paint);
                break;
        }
    }
}

该自定义工具用来实现频谱绘制,因为将该大小设置为全屏,因此只需要重写onDraw方法就好.定义一个点击事件,通过点击实现三种频谱图的切换.

6获取设备外部SD卡的歌曲信息的工具类

public static ArrayList<Song> getAllSongs(Context context) {
        ArrayList<Song> songs = new ArrayList<>();
        //Cursor是每行的集合,context.getContentResolver();查询媒体数据库
        Cursor cursor = context.getContentResolver().query(
                MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
                new String[]{MediaStore.Audio.Media._ID,
                        MediaStore.Audio.Media.DISPLAY_NAME,
                        MediaStore.Audio.Media.TITLE,
                        MediaStore.Audio.Media.DURATION,
                        //歌曲的歌手名MediaStore.Audio.Media.ARTIST
                        MediaStore.Audio.Media.ARTIST,
                        //歌曲的专辑名MediaStore.Audio.Media.ALBUM
                        MediaStore.Audio.Media.ALBUM,
                        MediaStore.Audio.Media.YEAR,
                        MediaStore.Audio.Media.MIME_TYPE,
                        MediaStore.Audio.Media.SIZE,
                        //歌曲文件的路径MediaStore.Audio.Media.DATA
                        MediaStore.Audio.Media.DATA},
                MediaStore.Audio.Media.MIME_TYPE + "=? or " + MediaStore.Audio.Media.MIME_TYPE + "=?", new String[]{"audio/mpeg", "audio/x-ms-wma"}, null);
        if (cursor.moveToFirst()) {
            do {
                Song song = new Song();
                // 文件名
                song.setFileName(cursor.getString(1));
                // 歌曲名
                song.setTitle(cursor.getString(2));
                // 时长
                song.setDuration(cursor.getInt(3));
                // 歌手名
                song.setSinger(cursor.getString(4));
                // 专辑名
                song.setAlbum(cursor.getString(5));
                // 年代
                if (cursor.getString(6) != null) {
                    song.setYear(cursor.getString(6));
                } else {
                    song.setYear("未知");
                }
                // 歌曲格式
                if ("audio/mpeg".equals(cursor.getString(7).trim())) {
                    song.setType("mp3");
                } else if ("audio/x-ms-wma".equals(cursor.getString(7).trim())) {
                    song.setType("wma");
                }
                // 文件大小
                if (cursor.getString(8) != null) {
                    float size = cursor.getInt(8) / 1024f / 1024f;
                    song.setSize((size + "").substring(0, 4) + "M");
                } else {
                    song.setSize("未知");
                }
                // 文件路径
                if (cursor.getString(9) != null) {
                    song.setFileUrl(cursor.getString(9));
                }
                songs.add(song);
            } while (cursor.moveToNext());
            cursor.close();
        }
        return songs;
    }
}

这个类用来拿到SD存放的音频文件,手机内部存储的应该拿不到(因为我没拿到.)具体方法的分析

7 自定义的歌曲对象

public class Song {
    private String fileName;// 文件名
    private String title;// 歌曲名
    private int duration;// 时长
    private String singer;// 歌手名
    private String album;// 专辑名
    private String year;// 年代
    private String type;//歌曲格式
    private String size;// 文件大小
    private String fileUrl;// 文件路径

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getDuration() {
        return duration;
    }

    public void setDuration(int duration) {
        this.duration = duration;
    }

    public String getSinger() {
        return singer;
    }

    public void setSinger(String singer) {
        this.singer = singer;
    }

    public String getAlbum() {
        return album;
    }

    public void setAlbum(String album) {
        this.album = album;
    }

    public String getYear() {
        return year;
    }

    public void setYear(String year) {
        this.year = year;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getSize() {
        return size;
    }

    public void setSize(String size) {
        this.size = size;
    }

    public String getFileUrl() {
        return fileUrl;
    }

    public void setFileUrl(String fileUrl) {
        this.fileUrl = fileUrl;
    }
}

8 静态全局变量类

public class StaticValues {
    public static final String MusicOperateAction = "android.hardware.action.MUSIC_OPERATE_ACTION";// 音乐相关操作广播标示
    public static final String ProgressUpdateAction = "android.hardware.action.PROGRESS_UPDATE_ACTION";// 音乐UI界面相关进度更新广播标示
    public static final int MUSIC_OPERATE = 0X10001;//音乐播放相关操作
    public static final int MUSIC_PROGRESS_UPDATE = 0X10003;//音乐界面进度更新
    public static final String UIUpdateData = "UIUPDATEDATA";//音乐UI界面更新相关信息标示
    public static final int MUSIC_START_OR_PAUSE = 0X00001;//音乐开始或者暂停播放
    public static final int MUSIC_STOP = 0X00002;//音乐停止播放
    public static final int MUSIC_NEXT = 0X00003;//下一首音乐
    public static final int MUSIC_PRE = 0X00004;//上一首音乐
    public static final int MUSIC_PLAY = 0X00005;//指定音乐播放
    public static final int MUSIC_SEEK = 0X00006;//指定音乐进度
    public static final String MusicOperateData = "MUSICOPERATEDATA";//音乐操作相关信息标示
}

9 xml文件
main界面的

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"

    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="m520it.com.mselfview.MainActivity"
    android:orientation="vertical">

    <!--   <m520it.com.mselfview.myView.MyTextView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="Hello World!"/>-->
<!--    <m520it.com.mselfview.myView.TopBar
        android:id="@+id/tb"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:leftBackground="#ffffff"
        app:leftText="左边"
        app:leftTextColor="#111122"
        app:rightBackground="#449988"
        app:rightText="右边"
        app:rightTextColor="#887898"
        app:title="自定义咯"
        app:titleTextColor="#666666ff"
        app:titleTextSize="18sp"/>-->
<!--    <m520it.com.mselfview.myView.AudioView android:layout_width="match_parent"
                                           android:layout_height="match_parent">


    </m520it.com.mselfview.myView.AudioView>-->
    <TextView
        android:id="@+id/txt_songname"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="20dp"
        android:maxLines="1"
        android:text="歌曲名"
        android:textSize="25sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp">

        <include layout="@layout/music_interface" />
    </LinearLayout>
</LinearLayout>

music界面

<?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:orientation="vertical">



    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginBottom="20dp"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="10dp"
            android:layout_marginTop="20dp"
            android:layout_weight="2"
            android:orientation="vertical">

            <ListView
                android:id="@+id/list_music"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:divider="#00ffffff"
                android:dividerHeight="6dp" />

            <TextView
                android:id="@+id/txt_prompt"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@drawable/grey_gridlines"
                android:gravity="center"
                android:text="不存在音乐"
                android:textSize="25sp"
                android:visibility="gone" />

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginBottom="20dp"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="20dp"
            android:layout_marginTop="20dp"
            android:layout_weight="1"
            android:background="@drawable/grey_gridlines"
            android:orientation="vertical">

            <TextView
                android:id="@+id/txt_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text="歌曲名"
                android:textColor="#7cce7a"
                android:textSize="25sp" />

            <TextView
                android:id="@+id/txt_album"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text="专辑"
                android:textColor="#7cce7a"
                android:textSize="20sp" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1">

                <m520it.com.mselfview.MusicMedial.MyVisualizerView
                    android:id="@+id/view_spectrum"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />
            </LinearLayout>

            <View
                android:layout_width="match_parent"
                android:layout_height="3dp"
                android:background="#337cce7a" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center">

                <TextView
                    android:id="@+id/txt_present_time"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="00:00"
                    android:textColor="#7cce7a" />

                <SeekBar
                    android:id="@+id/sb_music"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="5dp"
                    android:layout_marginRight="5dp"
                    android:layout_weight="1" />

                <TextView
                    android:id="@+id/txt_all_time"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="00:00"
                    android:textColor="#7cce7a" />

            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp">

                <include layout="@layout/music_interface" />

            </LinearLayout>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

music的列表界面

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

    <TextView
        android:id="@+id/txt_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1">

            <TextView
                android:id="@+id/txt_singer"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1">

            <TextView
                android:id="@+id/txt_duration"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

        </LinearLayout>
    </LinearLayout>


</LinearLayout>

通用的 点击停止 播放下一首 上一首 按键布局

<?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="wrap_content"
    android:gravity="center">

    <ImageView
        android:id="@+id/imgv_plus"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_marginLeft="20dp"
        android:src="@drawable/icon_volume_plus_img" />

    <ImageView
        android:id="@+id/imgv_pre"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_marginLeft="20dp"
        android:src="@drawable/icon_up_music_img" />

    <ImageView
        android:id="@+id/imgv_pause_start"
        android:layout_width="72dp"
        android:layout_height="72dp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:src="@drawable/icon_music_circle_play1" />

    <ImageView
        android:id="@+id/imgv_next"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_marginRight="20dp"
        android:src="@drawable/icon_down_music_img" />

    <ImageView
        android:id="@+id/imgv_minus"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_marginRight="20dp"
        android:src="@drawable/icon_volume_minus_img" />
</LinearLayout>
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值