学习内容:

1.掌握Surface的使用...

2.Android中如何实现视频播放...

1.SurfaceView类的使用

  在Android中,一般播放音频时我们可以去使用Android提供的MediaPlayer类,但是想要播放视频仅仅依靠MediaPlayer类是远远不够的...这里还需要使用到一个SurfaceView这个组件来完成..为什么?因为像视频和SD图形等都需要迅速的更新...如果这个更新实在主线程内去完成,那么显然是不合理的,因为一个视频的播放,系统会首先确定视频的格式,然后得到视频的编码..然后对编码进行解码,得到一帧一帧的图像,最后在画布上进行迅速更新...这就是视频播放的机制...那么这个过程显然我们需要在另外一个线程内部去完成...这样主线程的其他内容,比如说其他的渲染操作,图片加载什么的,就不会导致主线程阻塞...这样我们就可以使用SurfaceView来完成...

  SurfaceView继承了View类,也是属于视图的一部分...SurfaceView视图内嵌了一个专门用于绘制的Surface...其实每一个Surface都在每一个窗口的后面,我们可以通过SurfaceView类来控制哪些Surface的内容可以显示出来...其实说白了它的作用就是可以直接从内存或者是DMA硬件里获取图像数据...将这些图像数据迅速的显示出来,通过启动另外一个线程可以迅速的完成画面的更新操作,不会导致主线程阻塞...这个在游戏开发中也是起着非常重要的作用...

  SurfaceView使用双缓冲机制,这个机制可以使SurfaceView同时对两张图片进行渲染操作...其实目的也是为了迅速更新图片的显示,第一个缓冲对这一帧进行解析,第二个会对下一帧进行解析...这样就可以避免在上一帧的图片显示完成后,下一帧的图片还没有进行显示的情况发生...这样就可以流畅的播放一个视频文件...

  SurfaceView的实现...

  首先需要一个类去继承这个类,然后实现SurfaceHolder.Callback()接口...使用这个接口的目的就是在Android中,SurfaceView的双缓冲机制是非常消耗资源的,因此Android规定,在SurfaceView可见的时候,SurfaceView会对文件进行解析并显示,当其不可见时,要直接销毁掉SurfaceView以节省资源...那么这个接口的目的就是形成这个过程...

需要重写的方法

 (1)public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}

     //在surface的大小发生改变时激发

 (2)public void surfaceCreated(SurfaceHolder holder){}

     //在创建时激发,一般在这里调用画图的线程。然后画图的工作开始...

 (3)public void surfaceDestroyed(SurfaceHolder holder) {}

     //销毁时激发,一般在这里将画图的线程停止、释放。

  这三个方法就形成了上述说的那种关系...在视频的播放当中,它会结合MediaPlayer完成视频的流畅播放..而在游戏的开发当中,它可以结合Canvas元素来完成游戏中的一些画面迅速更新的操作...

  总而言之,SurfaceView就是一个绘图的容器,它可以在不干扰主线程的情况下,调用另一个线程完成绘图的操作,并迅速的更新图像...以完成更好的显示效果....

这里来两个实例...一个是SurfaceView配合MediaPlayer完成视频的播放,另一个是结合Canvas完成一个图像在画布上显示出来...

1.结合Canvas完成图像在画布上的显示...

  这里做了一个摩尔斯灯塔....什么是摩尔斯灯塔...这玩意其实在以前的战争中可以使用的到,这里有一个概念就是摩尔斯码...这个码就是将数字,字母等符号以"."和"-"的形式显示出来就是对文字的一种加密操作...出了可以发报文的形式,还可以以信号灯的形式来发送密报,然后另一方根据对应的信息进行解析...扯远了...

a .-
b -...
c -.-.
d -..
e .
f ..-.
g --.
h ....
i ..
j .---
k -.-
l .-..
m --
n -.
o ---
p .--.
q --.-
r .-.
s ...
t -
u ..-
v ...-
w .--
x -..-
y -.--
z --..
0 -----
1 .----
2 ..---
3 ...--
4 ....-
5 .....
6 -....
7 --...
8 ---..
9 ----.
. .-.-.-
- -....-
, ..--..
/ -..-
; -.-.-.
( -.--.
) -.--.
@ .--.-
* ...-.-
+ .-.-.
% .-...
! =---.
$ ...-..-

  这上面的就是摩尔斯码...我写在了txt文档当中...为了方便...但是我们需要注意一个地方就是我们在读取txt文档的时候,一定要把txt文档的对应内容拷贝到res/asset文件夹下面...或者是放在模拟器的内存卡中...千万别放在本地文件...因为Android的资源加载全在res文件夹下面或者是内存卡中,本地文件在正常的java程序中是可以读到的,但是在Android是一定读不到的..这个我已经试过,被坑3小时...因此这点需要注意....

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_1"
xmlns:tools="http://schemas.android.com/tools"
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=".MainActivity" >
<EditText
android:id="@+id/input"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:hint=""/>
<Button
android:id="@+id/translate"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/input"
android:text="转换"/>
<TextView
android:id="@+id/show"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_below="@id/translate"
android:text="输出"/>
<EditText
android:id="@+id/output"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_below="@id/show"
android:enabled="false"/>
<Button
android:id="@+id/send"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_below="@id/output"
android:text="发送"/>
</RelativeLayout>

  布局文件非常简单..看看就行....然后就是如何使用SurfaceView和Canvas的结合使用....在没有Canvas的基础下,还是去脑补一下Canvas。。。

package com.example.mouse;

import java.util.HashMap;
import java.util.Scanner; import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.Menu;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.EditText;
import android.widget.RelativeLayout; public class MainActivity extends Activity implements View.OnClickListener { public static HashMap<String, String> map = new HashMap<String, String>();
private EditText input;
private EditText output;
private char chars[];
private RelativeLayout layout;
private boolean flag = false;
private boolean loop = false;
int count; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
input = (EditText) findViewById(R.id.input);
output = (EditText) findViewById(R.id.output);
findViewById(R.id.translate).setOnClickListener(this);
findViewById(R.id.send).setOnClickListener(this);
layout= (RelativeLayout) findViewById(R.id.layout_1);//获取布局的id...
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
} @Override
public void onClick(View v) {
// TODO Auto-generated method stub switch (v.getId()) {
case R.id.translate:
try {
Scanner in = new Scanner(getResources().getAssets().open(
"morce.txt"));//获取资源文件morce.txt文件内部的内容...
while (in.hasNextLine()) {
String str = in.nextLine();
String abc[] = str.trim().split("[\\p{Space}]+");
map.put(abc[0], abc[1]);//定义了一个HashMap<String,String>,以键值对的形式来保存内容...
}
map.put(" ", "/");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String text = Mouse.morcecode(input.getText().toString(), map);//对我们输入的字符进行匹配..来完成加密操作...
output.setText(text);
break;
case R.id.send:
if (output.getText() != null
&& output.getText().toString().length() > 0) {
chars = output.getText().toString().toCharArray();
count = chars.length;
LightView light = new LightView(MainActivity.this);
layout.addView(light);//将画布的绘画内容加载到布局中....
}
}
} class LightView extends SurfaceView {//定义一个类继承SurfaceView类... SurfaceHolder holder; public LightView(Context context) {
// TODO Auto-generated constructor stub
super(context);
holder = this.getHolder();//调用getHolder()来获取SurfaceHolder对象..
holder.addCallback(new SurfaceHolder.Callback() {//实现接口... class LightThread implements Runnable {//内部定义异步线程来完成绘画操作... @Override
public void run() {
// TODO Auto-generated method stub
while (loop) {
if (count > 0) {
String s = String.valueOf(chars[chars.length
- count]);
Canvas canvas = holder.lockCanvas(null);//锁定画布...
Paint paint = new Paint();
paint.setAntiAlias(true);//去掉锯齿..
paint.setColor(Color.BLACK);//画布的颜色为黑色...
canvas.drawRect(0, 0, 480, 480, paint);
//下面这个是对报文的每一个字节解析以信号灯的形式显示出来...
if (flag) {
paint.setColor(Color.BLACK);
} else {
if (s.equalsIgnoreCase(".")) {
sleep(2);
paint.setColor(Color.YELLOW);
} else if (s.equalsIgnoreCase("-")) {
sleep(4);
paint.setColor(Color.YELLOW);
} else if (s.equalsIgnoreCase(" ")) {
sleep(2);
paint.setColor(Color.BLACK);
} else if (s.equalsIgnoreCase("/")) {
sleep(2);
paint.setColor(Color.BLACK);
}
count--;
}
canvas.drawCircle(250.0f, 200.0f, 100, paint);//画出一个圆用来显示信号...
flag = !flag;
holder.unlockCanvasAndPost(canvas);//解除锁定
}
}
} public void sleep(int time) {
try {
Thread.sleep(time * 80);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} @Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
loop = false;
} @Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
new Thread(new LightThread()).start();
} @Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
// TODO Auto-generated method stub }
});
loop = true;
}
}
}

这样就完成了对输入的文本以摩尔斯码的形式进行加密操作....很简单的一个小东西....

2.SurfaceView与MediaPlayer的结合操作....

<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" >
<TextView
android:id="@+id/info"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:text="等待播放"/>
<LinearLayout
android:orientation="horizontal"
android:layout_height="wrap_content"
android:layout_width="wrap_content">
<ImageButton
android:id="@+id/play"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/play"/>
<ImageButton
android:id="@+id/pause"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/pause"/>
<ImageButton
android:id="@+id/stop"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/stop"/>
</LinearLayout>
<SeekBar
android:id="@+id/seekbar"
android:layout_height="wrap_content"
android:layout_width="fill_parent"/>
<SurfaceView
android:id="@+id/surface_1"
android:layout_height="fill_parent"
android:layout_width="fill_parent"/>
</LinearLayout>

一个简单的布局文件...三个图片按钮...一个进度条和一个表面视图....

package com.example.exam7_2;

import java.io.IOException;

import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView; public class MainActivity extends Activity implements View.OnClickListener, SeekBar.OnSeekBarChangeListener{
private MediaPlayer media;
private boolean playflag=true;
private boolean pauseflag=false;
private SeekBar seekbar=null;
private TextView tv;
private SurfaceView suf=null;
private SurfaceHolder sufh=null;
@SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv=(TextView) findViewById(R.id.info);
findViewById(R.id.play).setOnClickListener(this);
findViewById(R.id.pause).setOnClickListener(this);
findViewById(R.id.stop).setOnClickListener(this);
suf=(SurfaceView) findViewById(R.id.surface_1);
sufh=suf.getHolder(); //获取SurfaceHolder。。。
seekbar=(SeekBar) findViewById(R.id.seekbar);
sufh.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//设置SurfaceView的类型...
media=new MediaPlayer();
try {
media.setDataSource("/sdcard/test.3gp");//这里需要把想要播放的文件拷贝到模拟器的sd卡中去...
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
private class updateseekbar extends AsyncTask<Integer, Integer, String>{//AsyncTask的使用... protected void onPostExecute(String result){}
protected void onProgressUpdate(Integer...progress){
seekbar.setProgress(progress[0]);
}
@Override
protected String doInBackground(Integer... params) {
// TODO Auto-generated method stub
while(MainActivity.this.playflag){
try {
Thread.sleep(params[0]);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.publishProgress(MainActivity.this.media.getCurrentPosition());
}
return null;
} }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
} @Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()){
case R.id.play:
{
MainActivity.this.media.setOnCompletionListener(new OnCompletionListener() { @Override
public void onCompletion(MediaPlayer arg0) {
// TODO Auto-generated method stub
playflag=false;
media.release();
}
});
seekbar.setMax(MainActivity.this.media.getDuration());
updateseekbar update=new updateseekbar();
//执行者execute
update.execute(1000);
seekbar.setOnSeekBarChangeListener(this);
if(MainActivity.this.media!=null){
MainActivity.this.media.stop();
}
try {
MainActivity.this.media.prepare();
MainActivity.this.media.start();
tv.setText("正在播放文件...");
} catch (Exception e) {
// TODO Auto-generated catch block
tv.setText("文件出现异常...");
e.printStackTrace();
}
break;
}
case R.id.pause:
if(media!=null){
if(pauseflag){
media.start();
pauseflag=false;
}else{
media.pause();
pauseflag=true;
}
}
break;
case R.id.stop:
if(media!=null){
media.stop();
tv.setText("停止播放文件...");
}
break;
}
} @Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub
} @Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
} @Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
media.seekTo(seekbar.getProgress());
} }

这样就完成了MediaPlayer和SurfaceView的配合使用...完成了视频的播放...同时添加了一个进度条来控制播放的进度...

 

Android 学习笔记之SurfaceView的使用+如何实现视频播放...的更多相关文章

  1. Android 学习笔记之Volley(七)实现Json数据加载和解析...

    学习内容: 1.使用Volley实现异步加载Json数据...   Volley的第二大请求就是通过发送请求异步实现Json数据信息的加载,加载Json数据有两种方式,一种是通过获取Json对象,然后 ...

  2. Android学习笔记进阶之在图片上涂鸦(能清屏)

    Android学习笔记进阶之在图片上涂鸦(能清屏) 2013-11-19 10:52 117人阅读 评论(0) 收藏 举报 HandWritingActivity.java package xiaos ...

  3. android学习笔记36——使用原始XML文件

    XML文件 android中使用XML文件,需要开发者手动创建res/xml文件夹. 实例如下: book.xml==> <?xml version="1.0" enc ...

  4. Android学习笔记之JSON数据解析

    转载:Android学习笔记44:JSON数据解析 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,为Web应用开发提供了一种 ...

  5. udacity android 学习笔记: lesson 4 part b

    udacity android 学习笔记: lesson 4 part b 作者:干货店打杂的 /titer1 /Archimedes 出处:https://code.csdn.net/titer1 ...

  6. Android学习笔记36:使用SQLite方式存储数据

    在Android中一共提供了5种数据存储方式,分别为: (1)Files:通过FileInputStream和FileOutputStream对文件进行操作.具体使用方法可以参阅博文<Andro ...

  7. Android学习笔记之Activity详解

    1 理解Activity Activity就是一个包含应用程序界面的窗口,是Android四大组件之一.一个应用程序可以包含零个或多个Activity.一个Activity的生命周期是指从屏幕上显示那 ...

  8. Pro Android学习笔记 ActionBar(1):Home图标区

     Pro Android学习笔记(四八):ActionBar(1):Home图标区 2013年03月10日 ⁄ 综合 ⁄ 共 3256字 ⁄ 字号 小 中 大 ⁄ 评论关闭 ActionBar在A ...

  9. 【转】Pro Android学习笔记(九八):BroadcastReceiver(2):接收器触发通知

    文章转载只能用于非商业性质,且不能带有虚拟货币.积分.注册等附加条件.转载须注明出处:http://blog.sina.com.cn/flowingflying或作者@恺风Wei-傻瓜与非傻瓜 广播接 ...

随机推荐

  1. 【Android】wifi开发

    WIFI就是一种无线联网技术,常见的是使用无线路由器.那么在这个无线路由器的信号覆盖的范围内都可以采用WIFI连接的方式进行联网.如果无线路由器连接了一个ADSL线路或其他的联网线路,则又被称为“热点 ...

  2. js获取gridview模板列中textbox行列的值

    下面一个例子:在gridview中第一列输入数值,第二列输入数值,点击第三列的时候进行计算 求和,如果不符合标记为红色字体. 如图: 代码 : <html xmlns="http:// ...

  3. @Html.DropDownList 设置选中值无效

    有时候在ASP.NET  MVC中用@Html.DropDownList 设置选中值无效,如图: 具体原因说不清,反正只要改个名字就行了!!!,如图:::

  4. 【Cocos2d-Js基础教学(2)类的使用和面向对象】

    类的使用和面向对象 大家都知道在cocos2d-x 底层是C++编写的,那么就有类的概念和继承机制. 但是在JS中,是没有类这个概念的,没有提供类,没有C++的类继承机制. 那么JS是通过什么方式实现 ...

  5. Embeding Python & Extending Python with FFPython

    Introduction ffpython is a C++ lib, which is to simplify tasks that embed Python and extend Python. ...

  6. Spring3系列2 -- 松耦合的实现

    Spring3系列2 -- 松耦合的实现 一.      环境 spring-framework-3.2.4.RELEASE jdk1.7.0_11 Maven3.0.5 eclipse-jee-ju ...

  7. 使用Xcode6.1.1打包出现Your account already has a valid iOS Distribution certificate问题

    1.问题描述: 使用客户证书在Xcode6.1.1上进行打包测试,出现如下问题,查看网上也很多类似错误且解决办法各异. 2.我的解决办法: 让客户将开发.发布证书重新revoke掉之后重新创新并给到p ...

  8. JS - IE中没有console定义

    由于IE中没有Console相关定义,所以不能使用它输出打印信息,且会出现脚本中断. 所以在IE中务必去掉(注释掉)console相关脚本代码.

  9. encfs创建时fuse: failed to exec fusermount: Permission denied错误解决

    今天用encfs创建加密文件夹时碰到提示错误fuse: failed to exec fusermount: Permission denied fuse failed. Common problem ...

  10. CSS3动画进度条

    CSS3动画进度条   CSS CODE: @-webkit-keyframes move{ 0%{ background-position: 0 0; } 100%{ background-posi ...