安卓第十六天笔记-音频与视频播放
音频与视频播放
1.音频 播放应用资源中的音乐
应用中的音乐一般放在res/raw目录下
/**
* 播放应用资源中的音乐
*
* @param v
*/
public void player1(View v) {
// 设置播放数据源
MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.gm);
// 不需要准备,create,创建完成直接可以使用播放
mediaPlayer.start();
}
2.播放本地其它地方的音乐文件如SD卡上的
/**
* 播放系统中的音乐
*
* @param v
*/
public void player2(View v) {
//文件地址
File file =new File(Environment.getExternalStorageDirectory(),"man.mp3");
//转换为uri
Uri uri = Uri.fromFile(file);
// 设置播放数据源
MediaPlayer mediaPlayer = MediaPlayer.create(this,uri);
// 不需要准备,create,创建完成直接可以使用播放
mediaPlayer.start();
}
以上两个方法用起来非常方便,便这两个方法每次都会返回新创建的Mediaplayer对象,同时也不方便控件播放状态.如果需要播放多个音频,使用这种方法就不方便.
可以使用MedaiPlayer.setDataSource()方法来装载指定的音频文件
3.使用MedaiPlayer.setDataSource()来播放音乐
播放
private MediaPlayer mediaPlayer ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mediaPlayer = new MediaPlayer();
}
public void player(View v) {
try {
//初始化
mediaPlayer.reset();
//设置资源
mediaPlayer.setDataSource("/mnt/sdcard/man.mp3");
//准备声音本地
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
//播放
mediaPlayer.start();
}
暂停与继承播放
private void init(){
mediaPlayer = new MediaPlayer();
}
public void player(View v) {
try {
init();
//初始化
mediaPlayer.reset();
//设置资源
mediaPlayer.setDataSource("/mnt/sdcard/man.mp3");
//准备声音本地
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
//播放
mediaPlayer.start();
}
/**
* 暂停与继承播放
* @param v
*/
public void pause(View v){
//判断状态
if(mediaPlayer!=null&&mediaPlayer.isPlaying()){
//暂停
mediaPlayer.pause();
}else if(mediaPlayer!=null) {
mediaPlayer.start();
}
}
/**
* 停止
* @param v
*/
public void stop(View v){
//判断状态
if(mediaPlayer!=null){
//停止播放资源
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer=null;
}
}
4.播放网络音乐
private void init(){
mediaPlayer = new MediaPlayer();
}
public void player(View v) {
//本地Tomcat服务器的地址
String url="http://188.188.3.79:8080/itest/gm.mp3";
//播放,网络音乐
try {
init();
//初始化 http://188.188.3.79:8080/itest/gm.mp3
mediaPlayer.reset();
//设置资源
mediaPlayer.setDataSource(url);
final ProgressDialog dialog = ProgressDialog.show(this, "加载中", "拼命加载中...........");
//准备声音远程音乐
mediaPlayer.prepareAsync();
/**
* 设置监听器什么时候缓冲,准备好音乐,
*/
mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
//准备好了,就对话框消失
dialog.dismiss();
//开始播放
mediaPlayer.start();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
添加权限
<uses-permission android:name="android.permission.INTERNET"/>
5.一些音乐特效
- AcousticEchoCanceler:取消回声控制器
- AtutomaticGainControl:自动增益控制器
- NoiseSupperssor:噪音压制控制器
- BassBoost:重低音控制器
- Equalizer:均衡控制器
- PresetReverb:预设音声控制器
- Visualizer:示波器
1,2,3只要调用它们的静态方法create()方法创建相应的实例,
然后调用穹们的isAvailabel()方法判断是否可用,
再调用setEnable(boolean enabled)方法启用相应的效果即可
/*
定义系统回声控制器
*/
private void setAcousticEchoCanceler(){
mAcousticEchoCanceler = AcousticEchoCanceler.create(mPlayer.getAudioSessionId());
if(mAcousticEchoCanceler.isAvailable()){
//设置取消回声功能
mAcousticEchoCanceler.setEnabled(true);
}
}
/*
自动增益控制器
*/
private void setAutomaticGainControl(){
mAutomaticGainControl = AutomaticGainControl.create(mPlayer.getAudioSessionId());
if(mAutomaticGainControl.isAvailable()){
//启用自动增益功能
mAutomaticGainControl.setEnabled(true);
}
}
/*
噪音压制控制器
*/
private void setNoiseSuppressor(){
mNoiseSuppressor = NoiseSuppressor.create(mPlayer.getAudioSessionId());
if(mNoiseSuppressor.isAvailable()){
//启用噪音压制功能
mNoiseSuppressor.setEnabled(true);
}
}
后面4个,者需要调用构造器来创建实例,同样需要输入一个audioSession参数,同样用启用它们需要调用 AudioEffect基类的setEnable(true)方法
6.使用SoundPool播放音效
如果应用程序经常需要播放密集,短促的音效,可以使用SoundPool来播放
为什么不使用Mediaplayer?
缺点:
资源占用较高,延迟时间较长
不支持多个音频同时播放
SoundPool主要用于播放一些较短的声音片段,
与MediaPlayer相比,优势在CPU资源占用低,反应延迟小
同时还支持设置声音的品质,音量,播放比特率等参数
6.1创建SoundPool
- Android中提供了SoundPool提供了一个Builder内部类,用于创建SoundPool
- Android5.0后SoundPool默认的构造器过时了,建议使用Builder来创建
布局
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/bomb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="播放Bomb"/>
<Button
android:id="@+id/shot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="播放Shot"/>
<Button
android:id="@+id/arrow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="播放Arrow"/>
</LinearLayout>
Activity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button bomb;
private Button shot;
private Button arrow;
//定义一个SoundPool
private SoundPool soundPool;
//定义一个Map用来存放声音
private HashMap<Integer, Integer> soundMap = new HashMap<Integer, Integer>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bomb = (Button) findViewById(R.id.bomb);
shot = (Button) findViewById(R.id.shot);
arrow = (Button) findViewById(R.id.arrow);
bomb.setOnClickListener(this);
shot.setOnClickListener(this);
arrow.setOnClickListener(this);
AudioAttributes attr = new AudioAttributes.Builder()
//设置音效使用的场景--游戏
.setUsage(AudioAttributes.USAGE_GAME)
//设置音效的类型
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
//创建声音池
soundPool = new SoundPool.Builder()
//设置声明池的属性
.setAudioAttributes(attr)
//设置最大容量
.setMaxStreams(10)
.build();
//加载指定音频文件使用load方法
/*
context
the application context
resId
the resource ID
priority
the priority of the sound. Currently has no effect. Use a value of 1 for future compatibility.
*/
soundMap.put(1, soundPool.load(this, R.raw.bomb, 1));
soundMap.put(2, soundPool.load(this, R.raw.shot, 1));
soundMap.put(3, soundPool.load(this, R.raw.arrow, 1));
}
/**
* 单击事件监听
*
* @param view
*/
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.bomb:
/*
soundID
a soundID returned by the load() function
leftVolume
left volume value (range = 0.0 to 1.0)
rightVolume
right volume value (range = 0.0 to 1.0)
priority
stream priority (0 = lowest priority)
loop
loop mode (0 = no loop, -1 = loop forever)
rate
playback rate (1.0 = normal playback, range 0.5 to 2.0
*/
soundPool.play(soundMap.get(1), 1, 1, 0, 0, 1);
break;
case R.id.shot:
soundPool.play(soundMap.get(2), 1, 1, 0, 0, 1);
break;
case R.id.arrow:
soundPool.play(soundMap.get(3), 1, 1, 0, 0, 1);
break;
}
}
}
7.使用VideoView播放视频
7.1使用videoView播放
在布局文件中定义VideoView就可以
<VideoView
android:id="@+id/videoview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
在Activity在找到这个VideoView使用findViewById,设置资源开始播放
videoView.setVideoPath();
或者videoView.setVideoURI();来设置资源
/*
找到这个控件
*/
videoView = (VideoView) findViewById(R.id.videoview);
videoView.setVideoPath("/mnt/sdcard/oppo.3gp");
//开始播放
videoView.start();
屏幕切换 继续播放,不会重新开始
<activity android:name=".MainActivity" android:configChanges="orientation|keyboardHidden|screenSize">
使用VideoView播放不能做控制,暂停,停止之类的操作
7.2使用MediaController与ViewVideo结合使用
声明一个MediaController对象与ViewVideo对象 设置相互依赖 //定义控制器 MediaController mediaController = new MediaController(this);
/*
找到这个控件
*/
videoView = (VideoView) findViewById(R.id.videoview);
/*
设置相互依赖
*/
mediaController.setAnchorView(videoView);
videoView.setMediaController(mediaController);
videoView.setVideoPath("/mnt/sdcard/oppo.3gp");
//开始播放 自动播放
//videoView.start();
//获取焦点,要手动点击播放才可以播放
videoView.requestFocus();
start与requestFocus()区别
- start:自动开始播放
- requestFocus:当前视频获取到焦点,但不播放,要手动点播放按键才可以播放
8.使用MediaPlayer与SurfaceView来播放视频
MediaPlayer主要是用来播放音频,因此它没有提供图像输出界面此时就要使用SurfaceView来显示MediaPlayer播放的图像输出
- 创建MediaPlayer对象,并让它加载指定的视频文件
- 在界面布局中定义SurfaceView组件,或者在程序中创建SurfaceView组件,并为SurfaceView的SurfaceView添加CallBack监听器
- 调用MediaPlayer对象的setDispay(SurfaceHolder sh)方法将所播放的视频图像输出到指定的SurfaceView组件中
- 调用MediaPlayer对象的start(),stop(),pause()方法来控制视频的播放
布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:gravity="center_horizontal">
<ImageButton
android:id="@+id/play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/play"
/>
<ImageButton
android:id="@+id/pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pause"
/>
<ImageButton
android:id="@+id/stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/stop"
/>
</LinearLayout>
</RelativeLayout>
Actvity
/**
* 使用MediaPlayer与SurfaceView播放视频
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private MediaPlayer mediaPlayer;
private SurfaceView surfaceView;
private ImageButton play;
private ImageButton pause;
private ImageButton stop;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*
初始化
*/
mediaPlayer = new MediaPlayer();
surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
play = (ImageButton) findViewById(R.id.play);
pause = (ImageButton) findViewById(R.id.pause);
stop = (ImageButton) findViewById(R.id.stop);
play.setOnClickListener(this);
pause.setOnClickListener(this);
stop.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.pause:
pause();
break;
case R.id.play:
play();
break;
case R.id.stop:
stop();
break;
}
}
/**
* 播放
*/
private void play() {
File file = new File(Environment.getExternalStorageDirectory(), "bb.mp4");
Uri uri = Uri.fromFile(file);
mediaPlayer.reset();
try {
mediaPlayer.setDataSource(this, uri);
mediaPlayer.prepare();
//设置视频输出到SurfaceView
mediaPlayer.setDisplay(surfaceView.getHolder());
//获取窗口管理器
WindowManager manager = getWindowManager();
//获取屏幕大小
DisplayMetrics metrics = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(metrics);
//设置视频保持纵横比例缩放占满 整个屏幕
surfaceView.setLayoutParams(new RelativeLayout.LayoutParams(metrics.widthPixels, mediaPlayer.getVideoHeight() * metrics.widthPixels / mediaPlayer.getVideoHeight()));
} catch (IOException e) {
e.printStackTrace();
}
mediaPlayer.start();
}
/**
* 暂停
*/
private void pause() {
if(mediaPlayer.isPlaying()){
mediaPlayer.pause();
}else {
mediaPlayer.start();
}
}
/**
* 停止
*/
private void stop() {
if(mediaPlayer!=null){
mediaPlayer.stop();
mediaPlayer.release();
}
}
/**
* 监听器
*/
private class SurfaceListener implements SurfaceHolder.Callback {
@Override
public void surfaceCreated(SurfaceHolder holder) {
play();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if(mediaPlayer!=null){
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer=null;
}
}
}
9.MediaRecorder录制音频
API
Create a new instance of android.media.MediaRecorder. Set the audio source using MediaRecorder.setAudioSource(). You will probably want to use MediaRecorder.AudioSource.MIC. Set output file format using MediaRecorder.setOutputFormat(). Set output file name using MediaRecorder.setOutputFile(). Set the audio encoder using MediaRecorder.setAudioEncoder(). Call MediaRecorder.prepare() on the MediaRecorder instance. To start audio capture, call MediaRecorder.start(). To stop audio capture, call MediaRecorder.stop(). When you are done with the MediaRecorder instance, call MediaRecorder.release() on it. Calling MediaRecorder.release() is always recommended to free the resource immediately.解释
- 创建一个MedaiRecorder对象
- 设置音频源,如:麦克风MediaRecorder.AudioSource.MIC.
- 设置输出文件的格式 MediaRecorder.setOutputFormat().
- 设置输出文件名称 MediaRecorder.setOutputFile(String path).
- 设置录制音频的编码MediaRecorder.setAudioEncoder().setAudioEncodingRitRate(int bitRate),setAudioSamplingRate(int samplingRate)设置音频编码,编码位率,采样率等,这些可以控制声音的品质,大小,一般来说,声音品质越好,文件越大
- 调用MediaRecorder.prepare()方法准备录制
- 调用MediaRecorder.stop()方法停止录制
- 调用MediaRecorder.release(),方法释放资源
布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="horizontal">
<ImageButton
android:id="@+id/record"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/record"
/>
<ImageButton
android:id="@+id/stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/stop"
/>
</LinearLayout>
Activity
/**
* MediaRecord
*
*/
public class MainActivity extends Activity implements View.OnClickListener {
private static final String TAG ="MainActivity" ;
private ImageButton record;
private ImageButton stop;
private File soundFile;
private MediaRecorder mRecorder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
record = (ImageButton) findViewById(R.id.record);
stop = (ImageButton) findViewById(R.id.stop);
/**
* 为2个按键设置单击事件绑定监听器
*/
record.setOnClickListener(this);
stop.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.record:
record();
break;
case R.id.stop:
stop();
break;
}
}
/**
* 录制音频
*/
private void record(){
/**
* 判断SD卡是否已经挂载成功
*/
if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
Toast.makeText(MainActivity.this, "SD卡未成功挂载,请插入SD卡", Toast.LENGTH_SHORT).show();
return;
}
try {
//创建要录制的文件
soundFile = new File(Environment.getExternalStorageDirectory(),+System.currentTimeMillis()+".amr");
Log.d(TAG,Environment.getExternalStorageDirectory()+"");
Log.d(TAG,soundFile.getAbsolutePath());
//创建录制
mRecorder = new MediaRecorder();
//设置声音来源MIC
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置输出格式
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
//设置输出文件
mRecorder.setOutputFile(soundFile.getPath());
//设置编码
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
//准备录制
mRecorder.prepare();
//录制
mRecorder.start();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 停止录制音频,
*/
private void stop(){
if(soundFile!=null&&soundFile.exists()){
mRecorder.stop();
mRecorder.release();
mRecorder=null;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if(soundFile!=null&&soundFile.exists()){
mRecorder.stop();
mRecorder.release();
mRecorder=null;
}
}
}
权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
10.调用摄像头拍照
Android5.0对象拍照的API进行了全新的设计,新增了Camera V2 API,,大幅提高了拍照功能,还能支持RAW照片的输,还可以允许调整相机的对焦,曝光,快门等.这里只是简单调用
布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:onClick="capture"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="使用摄像头"/>
<ImageView
android:id="@+id/iv_display"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Activity
/**
使用系统的相机进行拍照,并显示在界面上
*/
public class MainActivity extends AppCompatActivity {
private ImageView display;
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
private Uri fileUri;
private File file;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
display = (ImageView) findViewById(R.id.iv_display);
}
/**
* 调用摄像头
* @param v
*/
public void capture(View v){
// 使用Intent的方式去打开系统相机应用
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
file = new File(Environment.getExternalStorageDirectory(),System.currentTimeMillis()+".jpg");
fileUri =Uri.fromFile(file);
// 设置保存文件
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
// 启动一个Activity为了拿到结果
startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//data这里是NUll不能进行判断
if(requestCode==CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE){
//
if(resultCode==RESULT_OK){
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
display.setImageBitmap(bitmap);
}else{
Toast.makeText(MainActivity.this, "失败", Toast.LENGTH_SHORT).show();
}
}
}
}
权限
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
11.录像
与上面的步骤一样.只是改一个Intent的Action,保存文件的文件后缀 显示的话可以使用VideoView,或者SurfaceView 简单一点就使用VideoView
布局
<?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">
<Button
android:onClick="record"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="录像"/>
<VideoView
android:id="@+id/videoview"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Activtity
/**
* 使用摄像机录像
*/
public class MainActivity extends AppCompatActivity {
private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200;
private VideoView videoView;
private File file;
/*
VideoView与mediaocontroller控制器
*/
private MediaController mediaController;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
videoView = (VideoView) findViewById(R.id.videoview);
}
/**
开始录像
*/
public void record(View v){
//调用摄像头的录像功能
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
//保存的文件
file = new File(Environment.getExternalStorageDirectory(),System.currentTimeMillis()+".mp4");
Uri fileUri =Uri.fromFile(file);
//保存数据
intent.putExtra(MediaStore.EXTRA_OUTPUT,fileUri);
//启用一个系统的相机
startActivityForResult(intent,CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE);
}
/**
* 接收结果
* @param requestCode
* @param resultCode
* @param data
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//data没有数据是NULL
if(requestCode==CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE){
//说明成功
if(resultCode==RESULT_OK){
//再次判断 下文件是否保存成功
if(file.exists()){
mediaController = new MediaController(this);
videoView.setVideoPath(file.getAbsolutePath());
mediaController.setAnchorView(videoView);
videoView.setMediaController(mediaController);
//获取焦点 或者直接播放 .start()
videoView.requestFocus();
}else{
Toast.makeText(MainActivity.this, "保存失败", Toast.LENGTH_SHORT).show();
}
}else{
Toast.makeText(MainActivity.this, "失败", Toast.LENGTH_SHORT).show();
}
}
}
}
权限
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
13.屏幕捕获
使用MediaProjectionManager
/**
* 捕捉屏幕
*/
public class MainActivity extends AppCompatActivity {
private static final int CAPTURE_CODE = 100;
private SurfaceView surfaceView;
private int screenDensity;
private Surface surface;
//显示区域的宽高
private int displayWidth = 400;
private int displayHeight = 700;
private boolean screenSharing;
private MediaProjection mediaProjection;
private MediaProjectionManager mediaProjectionManager;
private VirtualDisplay virtualDisplay;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView = (SurfaceView) findViewById(R.id.surface);
surface = surfaceView.getHolder().getSurface();
// 获取屏幕分辨率
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
//获取屏幕密度
screenDensity = metrics.densityDpi;
// 控制界面上的SurfaceView组件的宽度和高度
ViewGroup.LayoutParams lp = surfaceView.getLayoutParams();
lp.width = displayWidth;
lp.height = displayHeight;
// 获取MediaProjectionManager管理器
mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
}
/**
* 捕捉屏幕 当用户单击开关按钮时激发该方法
*
* @param view 开关
*/
public void onToggleScreenShare(View view) {
if (((ToggleButton) view).isChecked()) {
shareScreen();
} else {
stopScreen();
}
}
/**
* 停止共享
*/
private void stopScreen() {
screenSharing = false;
if (virtualDisplay == null) {
return;
}
virtualDisplay.release();
}
/**
* 共享屏幕
*/
private void shareScreen() {
screenSharing = true;
if (surface == null) {
return;
}
if (mediaProjection == null) {
//建立一个屏幕捕捉的意图
Intent intent = mediaProjectionManager.createScreenCaptureIntent();
startActivityForResult(intent, CAPTURE_CODE);
return;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CAPTURE_CODE) {
// 如果resultCode不等于RESULT_OK,表明用户拒绝了屏幕捕捉
if (resultCode != RESULT_OK) {
Toast.makeText(this, "用户取消了屏幕捕捉"
, Toast.LENGTH_SHORT).show();
return;
}
mediaProjection = mediaProjectionManager.getMediaProjection(resultCode,data);
/*
public android.hardware.display.VirtualDisplay createVirtualDisplay(java.lang.String name,
int width,
int height,
int dpi,
int flags,
android.view.Surface surface,
android.hardware.display.VirtualDisplay.Callback callback,
android.os.Handler handler)
*/
virtualDisplay = mediaProjection.createVirtualDisplay("屏幕捕捉",displayWidth,displayHeight,screenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,surface,null,null);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mediaProjection != null) {
mediaProjection.stop();
mediaProjection = null;
}
}
}
14.使用SurFace画图
布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:layout_centerInParent="true"
android:id="@+id/surfaceview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
Activity
/**
* Surface画圆
*/
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private SurfaceView surfaceview;
private DisplayMetrics metrics;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceview= (SurfaceView) findViewById(R.id.surfaceview);
surfaceview.getHolder().addCallback(new MySureFaceHolad());
metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
}
private class MySureFaceHolad implements SurfaceHolder.Callback{
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.d(TAG,"surfaceCreated");
new Thread(){
@Override
public void run() {
int radus = 5;
SurfaceHolder holder1 = surfaceview.getHolder();
for (int i = 0; i < 300; i++) {
Canvas canvas = holder1.lockCanvas();
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawCircle(metrics.widthPixels / 2, metrics.heightPixels / 2, radus + i, paint);
holder1.unlockCanvasAndPost(canvas);
SystemClock.sleep(20);
}
}
}.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d(TAG,"surfaceChanged");
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "surfaceDestroyed");
}
}
}
一些注意
- SureFaceView启动时占用的资源比较大
- 使用了双缓冲的机制,两个线程交替执行
- surfaceView这个控件非常的消耗资源,所以,系统不会再一上来的时候就马上去绘制这个控件, 在界面可见了的时候才去绘制它。 所以一开始上来得到的canvas 是null .
- 为了解决刚才这个问题,我们可以选择休眠线程,延后再执行,但是休眠多少呢? 不固定,
-
添加一个回调即可
SurfaceHolder holder = sv.getHolder(); holder.addCallback(new Callback() {
//销毁时候调用
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "surfaceDestroyed---");
}
//创建时候调用
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
//状态发生改变时调用
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
}