Android基础笔记(十七)- 多媒体编程

  • MediaPlayer播放音频的基本使用
  • 音乐播放器的完善
  • 视频播放SurfaceView
  • 视频播放VideoView
  • vitamio框架
  • 如何调用照相机和录像机

MediaPlayer播放音频的基本使用

MediaPlayer播放音频的基本使用步骤:
①创建一个MediaPlayer实例
②设置播放的数据格式
③设置数据源
④准备播放
⑤开始播放

整体的示例代码很简单:

// 1. 创建一个音频播放器实例
final MediaPlayer player = new MediaPlayer();
// 2. 设置数据格式
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setDataSource("/mnt/sdcard/zxxpg.mp3");// 此处也可以是网络路径
// player.prepare(); // 此处会阻塞主线程,建议prepareAsync
player.prepareAsync();
// 4. 当准备成功后回调
player.setOnPreparedListener(new OnPreparedListener() {

    @Override
    public void onPrepared(MediaPlayer mp) {
        // 5. 开始播放
        player.start();
    }
});

音乐播放器的完善

下面是音乐播放器案例教完善的实现,当然这也只是个玩具程序,还缺少很多很多功能。

该案例的实现分为以下几步:
①点击播放、暂停、继续按钮达到播放、暂停、继续的目的
②增加进度条,让进度随着播放进度而调整
③手指拖动进度条时,可以修改播放的位置。
④使用文字显示当前播放进度

本来想把代码直接贴出来完事的,但是感觉这样不太好,了解代码的演化过程可能是对自己最好的锻炼。

由于这个小案例是在之前案例的基础之上修改过来了,所以先把骨架代码贴出来,然后再一步一步增加功能。

IService接口如下

public interface IService {
    public void callPlay();
    public void callPause();
    public void callReplay();
}

MainActivity类如下:

public class MainActivity extends Activity implements OnSeekBarChangeListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        // 启动和绑定服务
        Intent service = new Intent(this, MusicService.class);
        startService(service);
        conn = new MyServiceConnection();
        bindService(service, conn, Context.BIND_AUTO_CREATE);
    }

    // 播放
    public void play(View v) {iService.callPlay();}
    // 暂停
    public void pause(View v) { iService.callPause();}
    // 重新播放
    public void replay(View v) {    iService.callReplay();}

    private class MyServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {iService = (IService) service; }

        @Override
        public void onServiceDisconnected(ComponentName name) {}
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}

MusicService类文件如下:

public class MusicService extends Service {
    @Override
    public void onCreate() {super.onCreate();}

    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }

    // 播放音乐
    public void play() {}
    // 暂停
    public void pause() {}
    // 继续播放
    public void replay() {}
    // 中间人
    private class MyBinder extends Binder implements IService {
        @Override
        public void callPlay() {    play(); }
        @Override
        public void callPause() {pause();}
        @Override
        public void callReplay() {replay();}
    }
}

activity_main.xml文件如下:

<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:orientation="vertical"
    tools:context=".MainActivity" >
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="play"
        android:text="播放音乐" />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="pause"
        android:text="暂停播放" />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="replay"
        android:text="继续播放音乐" />
</LinearLayout>

整体来说骨架代码还是很简单的,当应用启动时会启动并绑定服务,这样就可以通过中间人来调用服务中的方法了。

接下来就一步一步添加功能。

第一步,点击播放、暂停、继续按钮达到播放、暂停、继续的目的

首先回想一下如何播放音乐文件:初始化MediaPlay实例,配置数据源、类型,准备一下,然后就可以start播放了。

那么在这一步中,可以在服务的onCreate()方法中,实例化MediaPlay对象;当调用播放时,初始化资源并播放;暂停和继续播放就很简单了,只需要调用相应的方法就OK了。

增加的代码如下:

// 播放器实例
private MediaPlayer mediaPlayer;
@Override
public void onCreate() {
    super.onCreate();
    // 初始化实例对象
    mediaPlayer = new MediaPlayer();
}

// 播放
public void play() {
try {
        // 重置资源
        mediaPlayer.reset();
        // 初始化配置
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        mediaPlayer.setDataSource("/mnt/sdcard/zuoqujia.mp3");
        // 准备
        mediaPlayer.prepareAsync();
        // 准备完成的回调
        mediaPlayer.setOnPreparedListener(new OnPreparedListener() {

            @Override
            public void onPrepared(MediaPlayer mp) {
                mediaPlayer.start();
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }
}

// 暂停播放
public void pause() {mediaPlayer.pause();}

// 继续播放
public void replay() {mediaPlayer.start();}

第二步,增加进度条,让进度随着播放进度而调整

其实增加一个进度条是很简单的,直接拖一个SeekBar就可以达到目的。但是怎么样在服务中达到修改UI的目的却是这一步的难点,此外,SeekBar的进度条是要实时更新的,这些都是这一步的关键。

仔细回想一下之前的知识点,我们可以使用Handler在子线程中修改UI线程的数据,但是服务中怎么获取到Handler?可以这样,把MainActivity中的Handler改变为静态变量,这样就可以在服务类中使用了。

音乐播放要求每秒更新一次进度条的位置,我们可以使用SE中的Timer和TimerTask达到此目的。

有了以上这些解决问题的技术,赶快来看看代码是怎么写的。

首先在布局文件中加入SeekBar控件,并在MainActivity的找到该控件。

<SeekBar
    android:id="@+id/sb_progress"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
private static SeekBar sbProgress; // 进度条

protected void onCreate(Bundle savedInstanceState) {
    ... ...
    sbProgress = (SeekBar) findViewById(R.id.sb_progress);
    ... ...
}

然后在MainActivity中增加静态的Handler,在MusicService类增加发送消息的逻辑。

MainActivity类增加如下内容:

private static int duration; // 歌曲总时长,毫秒单位 

private static int currentPosition; // 歌曲当前进度,毫秒单位

public static Handler handler = new Handler() {

    public void handleMessage(android.os.Message msg) {
        switch (msg.what) {

        case MusicService.CURRENTPOSITION: // 设置进度
            // 获取消息、更新进度条、更新当前播放位置
            currentPosition = msg.arg1;
            sbProgress.setProgress(currentPosition);
            break;

        case MusicService.DURATION: // 设置总时长
            duration = msg.arg1;
            sbProgress.setMax(duration);
            break;

        default:
            break;
        }
    };
};

MusicService增加如下代码

public static final int DURATION = 0; // 音乐总时长
public static final int CURRENTPOSITION = 1;//音乐当前播放位置

public void play() {
    try {
        // 重置
        mediaPlayer.reset();
        // 初始化
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        mediaPlayer.setDataSource("/mnt/sdcard/zuoqujia.mp3");
        // 准备
        mediaPlayer.prepareAsync();
        // 准备完成的回调
        mediaPlayer.setOnPreparedListener(new OnPreparedListener() {

            @Override
            public void onPrepared(MediaPlayer mp) {
                // 开始播放
                mediaPlayer.start();

                // 获取音乐总进度,并发送消息
                int duration = mediaPlayer.getDuration();
                Message msg = Message.obtain();
                msg.what = DURATION;
                msg.arg1 = duration;
                MainActivity.handler.sendMessage(msg);

                // 每隔一秒更新进度
                Timer timer = new Timer();
                TimerTask task = new TimerTask() {

                    @Override
                    public void run() {
                        //  获取当前进度,并发送消息更新UI
                        int currentPosition = mediaPlayer.getCurrentPosition();
                        Message msg = Message.obtain();
                        msg.what = CURRENTPOSITION;
                        msg.arg1 = currentPosition;
                        MainActivity.handler.sendMessage(msg);
                    }
                };
                // 每隔一秒钟调用一次
                timer.schedule(task, 0, 1000);

            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }
}

做完这些,音乐播放时,进度到就会“动”了。

第三步,手指拖动进度条时,可以修改播放的位置。

达到这个目的,可以利用MediaPlayer中的seekTo()方法,定位播放位置。

那么我们需要做的很简单了,在IService中增加一个callSeekToProgress()方法,并在MainActivity和MusicService调用和编写。

IService增加如下内容:

public void callSeekToProgress(int currentPosition);

MainActivity增加如下内容:

// 把播放进度移动到指定位置
public void seekToProgress(int currentPosition) {
    iService.callSeekToProgress(currentPosition);
}

在MusicService增加如下内容:

// 中间人
private class MyBinder extends Binder implements IService {
    ... ...
    @Override
    public void callSeekToProgress(int currentPosition) {
        seekToProgress(currentPosition);
    }
}

// 修改播放位置
public void seekToProgress(int currentPosition) {
    mediaPlayer.seekTo(currentPosition);
}

第四步,使用文字显示当前播放进度

写完上面这些,增加这个功能就略显容易了。在布局文件中增加一个TextView,然后再Handler消息处理时,设置其内容就可以了。

tvCurrentProgress.setText("音乐共" + (int)(duration / 1000.0f) + "秒/当前" + (int)(currentPosition / 1000.0f) + "秒");

视频播放(SurfaceView)

Android中的视频播放使用的还是MediaPlayer类,同时还借助了SurfaceView控件,而且遗憾的是它只支持3GP和MP4格式的文件。

代码也很简单,简单的看看一下吧

// 获取到控件
SurfaceView sfv = (SurfaceView) findViewById(R.id.sfv);

// 播放器
final MediaPlayer mediaPlayer = new MediaPlayer();

SurfaceHolder holder = sfv.getHolder();

// 添加回调,当SurfaceView准备好时才播放
holder.addCallback(new Callback() {

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            // 设置Holder
            mediaPlayer.setDisplay(holder);

            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mediaPlayer.setDataSource("/mnt/sdcard/proto.3gp");
            mediaPlayer.prepare(); // might take long! (for buffering, etc)
            mediaPlayer.start();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    }
});

视频播放(VideoView)

上面的SurfaceView略显麻烦,Google为我们提供了另外一个封装的控件VideoView,只需要几行代码就可以轻松展示一个3GP或者MP4文件。

VideoView vv = (VideoView) findViewById(R.id.vv);
vv.setVideoPath("/mnt/sdcard/proto.3gp");
vv.start();

vitamio框架

Android原生的SurfaceView和VideoView,在播放视频格式上都有限制,只能播放3GP和MP4。

Vitamio是一款开源框架,由一大堆C大神开发的,其中提供了Andoird版本的类库支持。

其中有一个VideoView类,和Andoird中原生的VideoView方法差不多,但是完美支持市面上的各种视频格式。

<io.vov.vitamio.widget.VideoView
      android:id="@+id/vv"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content" />
io.vov.vitamio.widget.VideoView vv = (VideoView) findViewById(R.id.vv);
        vv.setVideoPath("/mnt/sdcard/test.3gp");

使用方法也是超级简单。

如何调用照相机和录像机

//就是利用隐式意图 去开启手机的自带的相机应用
//如果是照相
//创建意图对象
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment.getExternalStorageDirectory().getPath(),"paizhao.png"))); // set the image file name
//开启意图   获取结果
startActivityForResult(intent, 0);

//如果是录像
//创建意图对象
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);

intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment.getExternalStorageDirectory().getPath(),"luxiang.3gp"))); // set the image file name

//开启意图   获取结果
startActivityForResult(intent, 0);
时间: 2024-10-17 21:24:29

Android基础笔记(十七)- 多媒体编程的相关文章

Java for Android 基础笔记-数据类型

Java的基本数据类型 布尔类型 boolean true | false java是一个强类型的语言,与JS中的布尔类型的自由转换相比,JAVA的Boolean类型只有两种true和false,JS中相应的只可以自行转换. 字符类型 char 可以存储一个汉字,和其他语言非常类似不再赘述. 整型 byte, 占用一个字节 -128`-127 short 2字节 -2^15~2^15-1 int 4字节 -2^31~2^31-1 long 8字节  -2^63~2^63-1 整型常量默认为int

Android基础之十七使用网络技术

Android基础之十七使用网络技术 本章主要会讲述如何在手机端使用HTTP协议和服务器端进行网络交互,并对服务器返回的数据进行解析,这也是Android中最常使用到的网络技术了,下面就让我们一起来学习一下吧. 1 WebView的用法 借助它我们就可以在自己的应用程序里嵌入一个浏览器,从而非常轻松地展示各种各样的网页 WebView的用法也是相当简单,下面我们就通过一个例子来学习一下吧.新建一个WebViewTest项目,然后修改activity_main.xml中的代码,如下所示 <Line

Bootstrap&lt;基础二十七&gt; 多媒体对象(Media Object)

Bootstrap 中的多媒体对象(Media Object).这些抽象的对象样式用于创建各种类型的组件(比如:博客评论),我们可以在组件中使用图文混排,图像可以左对齐或者右对齐.媒体对象可以用更少的代码来实现媒体对象与文字的混排. 媒体对象轻量标记.易于扩展的特性是通过向简单的标记应用 class 来实现的.你可以在 HTML 标签中添加以下两种形式来设置媒体对象: .media:该 class 允许将媒体对象里的多媒体(图像.视频.音频)浮动到内容区块的左边或者右边. .media-list

Android基础知识(1)——UI编程

我学习android一段时间了,看着大牛们的各种神博客,心里也痒痒的,希望有一天也能成为他们一样. 我知道,这些都是一滴一滴的积累,所有我也需要找到我的学习方法,打好基础,做出属于我自己的成果. 当然,作为一名菜鸟程序员,更加要懂得把知识整理归类,方便记忆. ----------------------------------------------------------- 在学校里听老师讲课,总会让学生误会程序员的主要工作不是界面美化,那都是美工做得事情.但随着移动开发的不断发展,在软件开发

android开发笔记之网络编程—使用HTTP进行网络编程

上次我们讲到了使用URLConnection的网络编程,URLConnection已经可以非常方便地与指定站点交换信息,URLConnection下还有一个子类:HttpURLConnection. HttpURLConnection在URLConnection的基础上进行改进,增加了一些用于操作HTTP资源的便捷方法. setRequestMethod(String):设置发送请求的方法 getResponseCode():获取服务器的响应代码 getResponseMessage():获取服

Android基础笔记(十一)- Service基础和注意事项以及Activity与Service的通信

Service的基本概念 为什么要有Service Service的基本用法 电话窃听器的小案例 Service和Activity通信 Service和Thread的关系 向光明而行! Service的基本概念 Service是Android的四大组件之一,在每一个应用程序中都扮演者非常重要的角色. 它主要用于在后台处理一些耗时的逻辑,或者去执行某些需要长期运行的任务.必要的时候,我们甚至可以在程序退出的情况下,让Service在后台继续保持运行状态. 既然都是被用于处理耗时的操作,那么我们什么

android开发笔记之网络编程—使用TCP协议和URL进行网络编程

手机本身是作为手机终端使用的,因此它的计算能力,存储能力都是有限的.它的主要优势是携带方便,可以随时打开,而且手机通常总是处于联网状态.因此网络支持对于手机应用非常重要. Android完全支持JDK本身的TCP,UDP网络通信API,也可以使用ServerSocket,Socket来建立基于TCP/IP协议的网络通信,也可以使用DatagramSocket,Datagrampacket来建立基于UDP协议的网络通信. Android也支持JDK提供的URL,URLConnection等网络通信

Android基础笔记(十八)- Fragment

博客的感悟终点-开始 什么是Fragment 添加fragment到Activity的两种方式 Fragment的生命周期 Fragment的向下兼容 Fragment之间的通信 博客的感悟,终点-开始 这个是基础的最后一篇博客了,学习了很多,也有很多感触. 就在这里大致总结一下. 坚持往往很难,完美的坚持下去更难.这是写这十八篇博客的感悟. 时间流失的很快,总是感觉时间不够用.慢慢的就会让自己博客的质量下降.今天反思了一下,我这样不就是在制造"破窗户"吗?(破窗户理论不知道的可以去看

Android基础笔记(一)

1G - 5G的介绍 Android的操作系统的介绍 Android版本 Android系统的架构 两种虚拟机的不同 ART模式 模拟器的简介 SDK目录 Android工程目录结构 打包的过程 ADB常用指令 案例电话拨号器 四种点击事件 Android的常用布局 Andriod中的单位 1G - 5G的介绍 1st - Generation 代表作是:大哥大,只能进行语音通信,不能够发短信.更不能上网. 2nd - Generation 代表作是:小灵通,可以收发短信.可以上网,但是速度几十