Android小玩意儿-- 从头开发一个正经的MusicPlayer(三)

MusicService已经能够接收广播,通过广播接收的内容来做出相应的MediaPlayer对象的处理,包括播放,暂停,停止等,并当MediaPlayer对象的生命周期发生变化的时候,同样通过发送广播,让UI层产生变换。现在后台处理已经写好。下面就来实现前台的Activity。

1·构建UI布局框架

1.先构建一个RelativeLayout布局,指定一个背景。

2.我的构想是把整个平面分为三部分,第一部分用来调节音量,因为音量调节常用。第二部分是音乐列表。第三部分是音乐控制按钮和音乐进度条。

3.这三部分一步一步的做出来。先做第一部分调节音量的视图。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/relativeLayout1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" 
        android:background="@drawable/bg_mountain"
        >
        <LinearLayout 
            android:id="@+id/main_volumeLayout"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent">
            <LinearLayout 
                android:layout_height="wrap_content"
                android:layout_width="fill_parent"
                android:layout_weight="1"></LinearLayout>
            <LinearLayout 
                android:layout_height="wrap_content"
                android:layout_width="fill_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:orientation="horizontal">
                <TextView 
                    android:id="@+id/main_tv_volumeText"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:text="音量:100%"
                    android:textColor="#ffffffff"
                    android:textSize="15dp"/>
                <SeekBar 
                    android:id="@+id/main_sb_volumebar"
                    android:layout_width="82dp"
                    android:layout_height="wrap_content"
                    android:maxHeight="5dip"
                    android:minHeight="5dip"
                    android:progressDrawable="@drawable/seekbar_style"
                    />
            </LinearLayout>
        </LinearLayout>   
</RelativeLayout>

这一部分先这样布局,以后如果体验不好再重新修改。

第二部分是歌曲列表的布局

    <ListView 
            android:id="@+id/main_listview"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_above="@+id/linearLayout1"
            android:layout_below="@id/main_volumeLayout"
            android:fastScrollEnabled="true"
            android:layout_marginLeft="10dip"
            android:layout_marginRight="10dip"
            android:background="@drawable/widget_bg"
            android:cacheColorHint="#00000000"></ListView>
            

第三部分是音乐控制的布局。

               <LinearLayout 
            android:id="@+id/linearLayout1"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="10dip"
            android:layout_marginLeft="10dip"
            android:layout_marginRight="10dip"
            android:background="@drawable/widget_bg"
            android:orientation="vertical"
            >
            <LinearLayout 
                android:id="@+id/linearLayout2"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:gravity="center">
                <ImageButton 
                    android:id="@+id/main_ibtn_pre"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="10dip"
                    android:background="@drawable/button_previous"/>
                <ImageButton
                    android:id="@+id/main_ibtn_play"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="10dip"
                    android:background="@drawable/button_play" />
    
                <ImageButton
                    android:id="@+id/main_ibtn_stop"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="10dip"
                    android:background="@drawable/button_stop" />
    
                <ImageButton
                    android:id="@+id/main_ibtn_next"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="10dip"
                    android:background="@drawable/button_next" />
            </LinearLayout>
            <SeekBar
                android:id="@+id/main_seekBar"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:paddingLeft="10dip"
                android:paddingRight="10dip" />
            <RelativeLayout 
                android:id="@+id/relativeLayout2"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content">
                <TextView
                    android:id="@+id/main_tv_curtime"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentLeft="true"
                    android:text="00:00" />
                <TextView
                    android:id="@+id/main_tv_totaltime"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentRight="true"
                    android:text="00:00" />
            </RelativeLayout>
        </LinearLayout>  

这样整个UI的布局就完成了。

2在之前的MainActivity的基础上继续开发

先实现最基础的功能,让音乐播放器能够播放,暂停,下一首,上一首,停止。要实现这个功能就要思考,怎么样才能让MusicService能够按照我们UI的状态变化来操纵MediaPlayer对象呢?最直观的一点就是,最起码要让我们的按钮都有响应了,所以要为我们的音乐控制按钮都加上事件监听器,比如,播放按钮,如果触发了播放按钮,就应该让监听器监听到播放按钮被按下,然后我们需要做的就是在用户按下按钮之后,在监听器下做出相应的相应。MusicService里的MediaPlayer对象有绑定了广播接收器。我们可以让按钮按下之后分发相应的广播。通过广播来通知Service。所以,要给各个按钮增加事件监听器,并分发广播。
MainActivity.java
package com.zharma.greatlovemusic;

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Timer;
    import com.zharma.data.Music;
    import com.zharma.data.MusicList;
    import android.support.v7.app.ActionBarActivity;
    import android.database.Cursor;
    import android.os.Bundle;
    import android.os.Handler;
    import android.provider.MediaStore;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.AdapterView;
    import android.widget.ImageButton;
    import android.widget.ImageView;
    import android.widget.ListView;
    import android.widget.RelativeLayout;
    import android.widget.SeekBar;
    import android.widget.SeekBar.OnSeekBarChangeListener;
    import android.widget.SimpleAdapter;
    import android.widget.TextView;
    import android.widget.AdapterView.OnItemClickListener;
    
    public class MainActivity extends ActionBarActivity {
    
        // 显示组件
        private TextView tv_current_time;
        private TextView tv_total_time;
        private ImageButton imgBtn_Previous;
        private ImageButton imgBtn_PlayOrPause;
        private ImageButton imgBtn_Stop;
        private ImageButton imgBtn_Next;
        private SeekBar seekBar;
        private ListView listView;
        private RelativeLayout root_Layout;
        
        // 当前歌曲的持续时间和当前位置,作用于进度条
        private int total_time;
        private int curent_time;
        
        //当前歌曲的序号,下标从零开始
        private int number;
        
        // 播放状态标志位
        private int status;
            
        //歌曲列表对象
        private ArrayList<Music> musicArrayList;
        
        //音量控制
        private TextView tv_vol;
        private SeekBar  seekbar_vol;
        
        // 进度条控制常量
        private static final int PROGRESS_INCREASE = 0;
        private static final int PROGRESS_PAUSE = 1;
        private static final int PROGRESS_RESET = 2;
        
        // 更新进度条的Handler
        private Handler seekBarHandler;
        
        //睡眠模式相关组件,标识常量
        private ImageView iv_sleep;
        private Timer timer_sleep ;
        private static final boolean NOTSLEEP = false;
        private static final boolean ISSLEEP = true;
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            
            findViews();
            initMusicList();
            initListView();
                            registerListeners();
                            checkMusicfile();

                            startService(new Intent(this, MusicService.class));
                            // 绑定广播接收器,可以接收广播
                    bindStatusChangedReceiver();
                            sendBroadcastOnCommand(MusicService.COMMAND_CHECK_IS_PLAYING);
                            //初始化进度条的Handler
                    initSeekBarHandler();
                            status = MusicService.COMMAND_STOP;
        }
        
        void findViews() {
            listView = (ListView) findViewById(R.id.main_listview);
            tv_current_time = (TextView) findViewById(R.id.main_tv_curtime);
            tv_total_time = (TextView) findViewById(R.id.main_tv_totaltime);
            imgBtn_Previous = (ImageButton) findViewById(R.id.main_ibtn_pre);
            imgBtn_PlayOrPause = (ImageButton) findViewById(R.id.main_ibtn_play);
            imgBtn_Previous = (ImageButton) findViewById(R.id.main_ibtn_pre);
            imgBtn_Next = (ImageButton) findViewById(R.id.main_ibtn_next);
            imgBtn_Stop = (ImageButton) findViewById(R.id.main_ibtn_stop);
            seekBar = (SeekBar) findViewById(R.id.main_seekBar);
            root_Layout = (RelativeLayout) findViewById(R.id.relativeLayout1);
            
            tv_vol = (TextView)findViewById(R.id.main_tv_volumeText);
            seekbar_vol = (SeekBar)findViewById(R.id.main_sb_volumebar);
            iv_sleep = (ImageView)findViewById(R.id.main_iv_sleep);
        }
    
        /**初始化音乐列表对象*/
        private void initMusicList() {
            musicArrayList = MusicList.getMusicList();
            //避免重复添加音乐
            if(musicArrayList.isEmpty())
            {
                Cursor mMusicCursor = this.getContentResolver().query(
                        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,
                        MediaStore.Audio.AudioColumns.TITLE);
                int indexTitle = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.TITLE);
                int indexArtist = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.ARTIST);
                int indexTotalTime = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.DURATION);
                int indexPath = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.DATA);
    
                /**通过mMusicCursor游标遍历数据库,并将Music类对象加载带ArrayList中*/
                for (mMusicCursor.moveToFirst(); !mMusicCursor.isAfterLast(); mMusicCursor
                        .moveToNext()) { 
                    String strTitle = mMusicCursor.getString(indexTitle);
                    String strArtist = mMusicCursor.getString(indexArtist);
                    String strTotoalTime = mMusicCursor.getString(indexTotalTime);
                    String strPath = mMusicCursor.getString(indexPath);
    
                    if (strArtist.equals("<unknown>"))
                        strArtist = "无艺术家";
                    Music music = new Music(strTitle, strArtist, strPath, strTotoalTime);
                    musicArrayList.add(music);
                }
            }
        }
        /**设置适配器并初始化listView*/
        private void initListView() {
            List<Map<String, String>> list_map = new ArrayList<Map<String, String>>();
            HashMap<String, String> map;
            SimpleAdapter simpleAdapter;
            for (Music music : musicArrayList) {
                map = new HashMap<String, String>();
                map.put("musicName", music.getMusicName());
                map.put("musicArtist", music.getMusicArtist());
                list_map.add(map);
            } 
    
            String[] from = new String[] { "musicName", "musicArtist" };
            int[] to = { R.id.listview_tv_title_item, R.id.listview_tv_artist_item };
    
            simpleAdapter = new SimpleAdapter(this, list_map, R.layout.listview,from, to);
            listView.setAdapter(simpleAdapter);
        }
    
        private void registerListeners() {
            imgBtn_Previous.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    sendBroadcastOnCommand(MusicService.COMMAND_PREVIOUS);
                }
            });
            
            imgBtn_PlayOrPause.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    switch (status) {
                    case MusicService.STATUS_PLAYING:
                        sendBroadcastOnCommand(MusicService.COMMAND_PAUSE);
                        break;
                    case MusicService.STATUS_PAUSED:
                        sendBroadcastOnCommand(MusicService.COMMAND_RESUME);
                        break;
                    case MusicService.COMMAND_STOP:
                        sendBroadcastOnCommand(MusicService.COMMAND_PLAY);
                        break;
                    default:
                        break;
                    }
                }
            });
            
            imgBtn_Stop.setOnClickListener(new OnClickListener() {
                public void onClick(View view) {
                    sendBroadcastOnCommand(MusicService.COMMAND_STOP);
                }
            });
            
            imgBtn_Next.setOnClickListener(new OnClickListener() {
                public void onClick(View view) {
                    sendBroadcastOnCommand(MusicService.COMMAND_NEXT);
                }
            });
            
            listView.setOnItemClickListener(new OnItemClickListener() {
    
                @Override
                public void onItemClick(AdapterView<?> parent, View view,
                        int position, long id) {
                    number = position;
                    sendBroadcastOnCommand(MusicService.COMMAND_PLAY);
                }
            });
            
            seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
                
                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
                    if (status == MusicService.STATUS_PLAYING) {
                        // 发送广播给MusicService,执行跳转 
                        sendBroadcastOnCommand(MusicService.COMMAND_SEEK_TO);
                        // 进度条恢复移动
                        seekBarHandler.sendEmptyMessageDelayed(PROGRESS_INCREASE,
                                1000);
                    }
                }
                
                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
                    // 进度条暂停移动
                    seekBarHandler.sendEmptyMessage(PROGRESS_PAUSE);
                }
                
                @Override
                public void onProgressChanged(SeekBar seekBar, int progress,
                        boolean fromUser) {
                    if (status != MusicService.STATUS_STOPPED) {
                        curent_time = progress;
                        // 更新文本
                        tv_current_time.setText(formatTime(curent_time));
                    }
                }
            });
            
        }
    }

现在主体已经写好,后面就是具体的各个实现方法。把发送广播的方法与格式化时间的方法实现如下:

            /** 发送命令,控制音乐播放。参数定义在MusicService类中 */
private void sendBroadcastOnCommand(int command) {
    Intent intent = new Intent(MusicService.BROADCAST_MUSICSERVICE_CONTROL);
    intent.putExtra("command", command);
    // 根据不同命令,封装不同的数据
    switch (command) {
    case MusicService.COMMAND_PLAY:
        intent.putExtra("number", number);
        break;
    case MusicService.COMMAND_SEEK_TO:
        intent.putExtra("time", curent_time);
        break;
    case MusicService.COMMAND_PREVIOUS:
    case MusicService.COMMAND_NEXT:
    case MusicService.COMMAND_PAUSE:
    case MusicService.COMMAND_STOP:
    case MusicService.COMMAND_RESUME:
    default:
        break;
    }
            sendBroadcast(intent);
}

    /**如果列表没有歌曲,则播放按钮不可用,并提醒用户*/
private void checkMusicfile()
{
    if (musicArrayList.isEmpty()) {
        imgBtn_Next.setEnabled(false);
        imgBtn_PlayOrPause.setEnabled(false);
        imgBtn_Previous.setEnabled(false);
        imgBtn_Stop.setEnabled(false);
        Toast.makeText(getApplicationContext(), "当前没有歌曲文件",Toast.LENGTH_SHORT).show();
    } else {
        imgBtn_Next.setEnabled(true);
        imgBtn_PlayOrPause.setEnabled(true);
        imgBtn_Previous.setEnabled(true);
        imgBtn_Stop.setEnabled(true);
    }
}

    /** 绑定广播接收器 */
private void bindStatusChangedReceiver() {
    receiver = new StatusChangedReceiver();
    IntentFilter filter = new IntentFilter(
            MusicService.BROADCAST_MUSICSERVICE_UPDATE_STATUS);
    registerReceiver(receiver, filter);
}

    /** 内部类,用于播放器状态更新的接收广播 */
 class StatusChangedReceiver extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
        // 获取播放器状态
        status = intent.getIntExtra("status", -1);
        switch (status) {
        case MusicService.STATUS_PLAYING:
            String musicName = intent.getStringExtra("musicName");
            String musicArtist = intent.getStringExtra("musicArtist");
            seekBarHandler.removeMessages(PROGRESS_INCREASE);
            curent_time = intent.getIntExtra("time", 0);
            total_time = intent.getIntExtra("duration", 0);
            number = intent.getIntExtra("number", number);
            listView.setSelection(number);

            seekBar.setProgress(curent_time);
            seekBar.setMax(total_time);
            seekBarHandler.sendEmptyMessageDelayed(PROGRESS_INCREASE, 1000);

            tv_total_time.setText(formatTime(total_time));
            imgBtn_PlayOrPause.setBackgroundResource(R.drawable.pause);
        
            // 设置Activity的标题栏文字,提示正在播放的歌曲
            MainActivity.this.setTitle("正在播放:" + musicName + " "+ musicArtist);
            break;
        case MusicService.STATUS_PAUSED:
            seekBarHandler.sendEmptyMessage(PROGRESS_PAUSE);
            String string = MainActivity.this.getTitle().toString().replace("正在播放:", "已暂停:");
            MainActivity.this.setTitle(string);
            imgBtn_PlayOrPause.setBackgroundResource(R.drawable.play);
            break;
        case MusicService.STATUS_STOPPED:
            curent_time = 0;
            total_time = 0;
            tv_current_time.setText(formatTime(curent_time));
            tv_total_time.setText(formatTime(total_time));
            seekBarHandler.sendEmptyMessage(PROGRESS_RESET);
            MainActivity.this.setTitle("GracePlayer");
            imgBtn_PlayOrPause.setBackgroundResource(R.drawable.play);
            break;
        case MusicService.STATUS_COMPLETED:
            number = intent.getIntExtra("number", 0);
               //顺序模式:到达列表末端时发送停止命令,否则播放下一首
            if(playmode == MainActivity.MODE_LIST_SEQUENCE)
            {
                if(number == MusicList.getMusicList().size()-1)                                             
                    sendBroadcastOnCommand(MusicService.STATUS_STOPPED);
                else
                    sendBroadcastOnCommand(MusicService.COMMAND_NEXT);
                }
                //单曲循环
            else if(playmode == MainActivity.MODE_SINGLE_CYCLE)
                sendBroadcastOnCommand(MusicService.COMMAND_PLAY);
                //列表循环:到达列表末端时,把要播放的音乐设置为第一首
            else if(playmode == MainActivity.MODE_LIST_CYCLE)
            {
                    //然后发送播放命令。
                if(number == musicArrayList.size()-1)
                {
                    number = 0;
                    sendBroadcastOnCommand(MusicService.COMMAND_PLAY);
                }
                else sendBroadcastOnCommand(MusicService.COMMAND_NEXT);
            }
                //随机播放
           else if (playmode == MainActivity.MODE_LIST_RANDOM)
            {
               Random random = new Random();
               int randomnum = random.nextInt(listView.getCount());
               number = randomnum;
               sendBroadcastOnCommand(MusicService.COMMAND_PLAY);
            }
                
           seekBarHandler.sendEmptyMessage(PROGRESS_RESET);
            MainActivity.this.setTitle("GracePlayer");
            imgBtn_PlayOrPause.setBackgroundResource(R.drawable.play);
            break;
        default:
            break;
        }
    }
}

    private void initSeekBarHandler() {
    seekBarHandler = new Handler() {
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            switch (msg.what) {
            case PROGRESS_INCREASE:
                if (seekBar.getProgress() < total_time) {
                    // 进度条前进1秒
                    seekBar.incrementProgressBy(1000);
                    seekBarHandler.sendEmptyMessageDelayed(
                            PROGRESS_INCREASE, 1000);
                    // 修改显示当前进度的文本
                    tv_current_time.setText(formatTime(curent_time));
                    curent_time += 1000;
                }
                break;
            case PROGRESS_PAUSE:
                seekBarHandler.removeMessages(PROGRESS_INCREASE);
                break;
            case PROGRESS_RESET:
                // 重置进度条界面
                seekBarHandler.removeMessages(PROGRESS_INCREASE);
                seekBar.setProgress(0);
                tv_current_time.setText("00:00");
                break;
            }
        }
    };
}

3·AndroidManifest.xml文件配置

最后还要把我们的Service加到配置文件里。

   <service
        android:name="com.zharma.greatlovemusic.MusicService"
        android:exported="true" >
        <intent-filter>
            <action android:name="VideoService.START_Video_SERVICE" />

            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </service>

到目前为止,这个播放器已经实现了基本的音乐播放功能,后面有时间就会再加一些网络歌词获取,放到一个Activity里。搞个华丽的侧滑界面。弄个睡眠模式,播放模式之类的东西,让这个播放器看起来更正儿八经。

转载于:https://www.cnblogs.com/zharma/p/4576309.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值