android屏幕录像
2015-11-16 21:21
441 查看
实现原理:
利用Java反射机制,获取WindowManagerImpl,该类里面有一个mViews的参数,它就是存放最终输出到界面的view视图,把这些视图保存在文件中为png,最后播放的时候从文件中拿出来即可。
一个Activity,一个后台Service既可以完成:
后台Service:
但是,这只能录制当前App下的图像
利用Java反射机制,获取WindowManagerImpl,该类里面有一个mViews的参数,它就是存放最终输出到界面的view视图,把这些视图保存在文件中为png,最后播放的时候从文件中拿出来即可。
一个Activity,一个后台Service既可以完成:
package com.jack.screenrecorder; import java.io.File; import java.util.Arrays; import java.util.Comparator; import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.Toast; import com.jack.service.RecoderService; public class MainActivity extends Activity implements OnClickListener{ private static final String TAG = "MainActivity"; private EditText input; private File[] arrayOfFile2; private ImageView playView; private MainActivityHandler mHandler; private boolean flagPlay = false; private boolean flagRecorder = false; private Button play,recorder,delete; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); input = (EditText)this.findViewById(R.id.input); playView = (ImageView)this.findViewById(R.id.playView); playView.setImageResource(R.drawable.ic_launcher); mHandler = new MainActivityHandler(); play = (Button)this.findViewById(R.id.startPlay); recorder = (Button)this.findViewById(R.id.startRecorder); delete = (Button)this.findViewById(R.id.delete); play.setOnClickListener(this); recorder.setOnClickListener(this); delete.setOnClickListener(this); Log.i(TAG, Environment.getExternalStorageState()); } @Override protected void onStart() { // TODO Auto-generated method stub super.onStart(); int[] lo = new int[2]; input.getLocationOnScreen(lo); Log.i(TAG, "input x y :" + lo[0] + lo[1]); playView.getLocationOnScreen(lo); Log.i(TAG, "playView x y :" + lo[0] + lo[1]); playView.getLocationInWindow(lo); Log.i(TAG, "playView x y :" + lo[0] + lo[1]); } /** * 开始录制方法 */ public void startRecorder(){ Log.i(TAG, "startRecorder"); flagRecorder = true; recorder.setText("停止录制"); Intent intent = new Intent(this,RecoderService.class); intent.putExtra("isStart", flagRecorder); startService(intent); } public void endRecorder(){ Log.i(TAG, "endRecorder"); flagRecorder = false; recorder.setText("开始录制"); Intent intent = new Intent(this,RecoderService.class); stopService(intent); } /** * 开始播放方法 */ public void startPlay(){ flagPlay = true; Log.i(TAG, "startPlay"); play.setText("停止播放"); arrayOfFile2 = getFileList(); mHandler.setCount(0); mHandler.sendEmptyMessageDelayed(RecoderService.MSG_WHAT, RecoderService.MSG_DELEAY); } /** * 停止播放 */ public void endPlay(){ flagPlay = false; Log.i(TAG, "endPlay"); play.setText("开始播放"); mHandler.removeMessages(RecoderService.MSG_WHAT); } /** * 获取存储卡里面路径下的文件列表 * @return */ public File[] getFileList(){ File localFile1 = Environment.getExternalStorageDirectory(); File localFile2 = new File(localFile1, RecoderService.PATH); //该路径创建不成功 if(!localFile2.exists()){ return null; } //返回该路径下的文件列表 File[] fileList = localFile2.listFiles(); //把文件拿来排序,存放文件时名字为日期,将日期转为long型数据进行排序 Arrays.sort(fileList, new Comparator<File>() { @Override public int compare(File arg0, File arg1) { // TODO Auto-generated method stub Long preFileLength1 = Long.valueOf(getPreFileName(arg0.getName())); long preFileLength2 = Long.valueOf(getPreFileName(arg1.getName())); return (int)(preFileLength1-preFileLength2); } }); return fileList; } /** * 获取filename的前缀名 * @param filename * @return * 如:20150305.jpg === >>> 20150305 */ public String getPreFileName(String filename){ String preFileName = null; if(filename == null || filename.length() < 1){ Log.i(TAG, "当前文件名为空或长度小于1"); return null; } int i = filename.lastIndexOf('.'); if(i != -1){ int j = filename.length(); if(i < j){ preFileName = filename.substring(0, i); } } return preFileName; } private class MainActivityHandler extends Handler{ private int count = 0; public void setCount(int value){ count = value; } public int getCount(){ return count; } @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub if(arrayOfFile2 != null && count < arrayOfFile2.length){ Bitmap localBitmap = BitmapFactory.decodeFile(arrayOfFile2[count++].getAbsolutePath()); //获取该路径下的文件 并转换为bitmap playView.setImageBitmap(localBitmap); localBitmap.recycle(); //延迟发送message if(flagPlay){ mHandler.sendEmptyMessageDelayed(RecoderService.MSG_WHAT, RecoderService.MSG_DELEAY); } } } } @Override public void onClick(View arg0) { // TODO Auto-generated method stub switch(arg0.getId()){ case R.id.startPlay: if(flagPlay){ endPlay(); }else{ if(!flagRecorder){ startPlay(); }else{ Toast.makeText(MainActivity.this, "请先关闭录制功能", Toast.LENGTH_SHORT).show(); } } break; case R.id.startRecorder: if(flagRecorder){ endRecorder(); }else{ if(!flagPlay){ startRecorder(); }else{ Toast.makeText(MainActivity.this, "请先关闭播放功能", Toast.LENGTH_SHORT).show(); } } break; case R.id.delete: if(!flagRecorder && !flagPlay){ Log.i(TAG, "delete file"); deleteRecorderFile(); }else{ Toast.makeText(MainActivity.this, "请关闭录制和播放功能", Toast.LENGTH_SHORT); } break; default: break; } } public void deleteRecorderFile(){ File[] file1 = this.getFileList(); for(File file : file1){ if(file.isFile() || file.exists()){ file.delete(); } } } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); int[] lo = new int[2]; input.getLocationOnScreen(lo); Log.i(TAG, "input x y :" + lo[0] + lo[1]); playView.getLocationOnScreen(lo); Log.i(TAG, "playView x y :" + lo[0] + lo[1]); playView.getLocationInWindow(lo); Log.i(TAG, "playView x y :" + lo[0] + lo[1]); } @Override protected void onPause() { // TODO Auto-generated method stub super.onResume(); int[] lo = new int[2]; input.getLocationOnScreen(lo); Log.i(TAG, "onPause input x y :" + lo[0] + lo[1]); playView.getLocationOnScreen(lo); Log.i(TAG, "playView x y :" + lo[0] + lo[1]); playView.getLocationInWindow(lo); Log.i(TAG, "playView x y :" + lo[0] + lo[1]); } }
后台Service:
package com.jack.service; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Field; import android.app.Service; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Paint; import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.util.Log; import android.view.View; public class RecoderService extends Service{ private static final String TAG = "RecoderService"; public static final int MSG_WHAT = 256; public static final long MSG_DELEAY = 100L; public static final String PATH = "/recoder/"; private boolean isStart = false; @Override public IBinder onBind(Intent arg0) { // TODO Auto-generated method stub return null; } @Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); Log.i(TAG, "onCreate()..."); } @Override public void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); Log.i(TAG, "end Service()==========================================="); Log.i(TAG, "end Service()==========================================="); Log.i(TAG, "end Service()==========================================="); isStart = false; } @Override public int onStartCommand(Intent intent, int flags, int startId) { // TODO Auto-generated method stub Log.i(TAG, "onStartCommand()==========================================="); Log.i(TAG, "onStartCommand()==========================================="); Log.i(TAG, "onStartCommand()==========================================="); boolean flag = intent.getBooleanExtra("isStart", false); //如果传递过来的值时开启就开启 否则不开启 if(flag != isStart){ isStart = flag; if(isStart){ mHandler.sendEmptyMessageDelayed(MSG_WHAT, MSG_DELEAY); }else{ mHandler.removeMessages(MSG_WHAT); } } return super.onStartCommand(intent, flags, startId); } @Override public boolean onUnbind(Intent intent) { // TODO Auto-generated method stub return super.onUnbind(intent); } /** * 排序views,以view的绝对坐标 * @param viewArray * @return */ public View[] sortViews(View[] viewArray){ if(viewArray == null || viewArray.length <= 0){ Log.i(TAG, "sortView() paramter viewArray is null"); return null; } int i = 0; View[] views = new View[viewArray.length]; for(View view : viewArray){ views[i++] = view; } //sort the view int length = views.length; int[] arrayInt = new int[2]; for(i = 0; i < length; i++){ View localView = views[i]; localView.getLocationOnScreen(arrayInt); if(arrayInt[0] > 0 && arrayInt[1] > 0){ for(int j = i+1; j < views.length; i++){ views[j-1] = views[j]; } views[views.length-1] = localView; i--; length--; } } return views; } /** * * @return views[]s */ public View[] getWindowDecorViewDownApi15(){ View[] view = null; try { Log.i(TAG, "getWindowDecorViewDownApi15"); Class<?> windowManager = Class.forName("android.view.WindowManagerImpl"); Field viewFiled = windowManager.getDeclaredField("mViews"); Field instanceFiled = windowManager.getDeclaredField("mWindowManager"); viewFiled.setAccessible(true); instanceFiled.setAccessible(true); //可修改 Object instance = instanceFiled.get(null); view = (View[]) viewFiled.get(instance); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block Log.i(TAG, "getWindowDecorViewDownApi15() ClassNotFoundException " + e.toString()); e.printStackTrace(); } catch (NoSuchFieldException e) { // TODO Auto-generated catch block Log.i(TAG, "getWindowDecorViewDownApi15() NoSuchFieldException " + e.toString()); e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } return sortViews(view); } /** * * @return */ public View[] getWindowDecorViewsApi14_16(){ View[] view = null; try { Log.i(TAG, "getWindowDecorViewsApi14_16"); Class<?> windowManager = Class .forName("android.view.WindowManagerImpl"); Field viewsField = windowManager.getDeclaredField("mViews"); Field instanceField = windowManager.getDeclaredField("sWindowManager"); viewsField.setAccessible(true); instanceField.setAccessible(true); //可修改 Object instance = instanceField.get(null); view = (View[]) viewsField.get(instance); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block Log.i(TAG, "getWindowDecorViewDownApi15() ClassNotFoundException " + e.toString()); e.printStackTrace(); } catch (NoSuchFieldException e) { // TODO Auto-generated catch block Log.i(TAG, "getWindowDecorViewDownApi15() NoSuchFieldException " + e.toString()); e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } return sortViews(view); } public View[] getWindowDecorViewsApiUp17(){ View[] view = null; try { Log.i(TAG, "getWindowDecorViewsApiUp17"); Class<?> windowManager = Class .forName("android.view.WindowManagerGlobal"); Field viewsField = windowManager.getDeclaredField("mViews"); Field instanceField = windowManager.getDeclaredField("sDefaultWindowManager"); viewsField.setAccessible(true); instanceField.setAccessible(true); //可修改 Object instance = instanceField.get(null); view = (View[]) viewsField.get(instance); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block Log.i(TAG, "getWindowDecorViewDownApi15() ClassNotFoundException " + e.toString()); e.printStackTrace(); } catch (NoSuchFieldException e) { // TODO Auto-generated catch block Log.i(TAG, "getWindowDecorViewDownApi15() NoSuchFieldException " + e.toString()); e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } return sortViews(view); } /** * 根据不同的sdk版本采取不同的方法获取图片 * @return */ private View[] getWindowDecorViews(){ View[] views = null; if(android.os.Build.VERSION.SDK_INT >= 14 && android.os.Build.VERSION.SDK_INT < 17){ views = getWindowDecorViewsApi14_16(); }else if(android.os.Build.VERSION.SDK_INT >= 17){ views = getWindowDecorViewsApiUp17(); }else{ views = getWindowDecorViewDownApi15(); } return views; } /** * 从底层获取view在转换为bitmap,在保存到文件 */ public void screenShot(){ try { View[] views = getWindowDecorViews(); Bitmap bitmap = viewsToBitmap(views); new Thread(new SavePicThread(bitmap)).start(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); Log.i(TAG, "screenshot() has a error. " + e.toString()); } } /** * * @param view * @return Bitmap */ public Bitmap viewToBitmap(View view){ Log.i(TAG, "turn view to bitmap"); /** * set缓冲方便下次调用getDrawingCache把view画到一个bitmap里面去 */ view.setDrawingCacheEnabled(true); view.buildDrawingCache(); Bitmap bitmap1 = view.getDrawingCache(); Bitmap bitmap2 = null; if(bitmap1 != null){ bitmap2 = Bitmap.createBitmap(bitmap1); } view.destroyDrawingCache(); return bitmap2; } /** * 将几个view画到一个bitmap有点不懂为什么 * @param arrayView * @return */ public Bitmap viewsToBitmap(View[] arrayView){ Log.i(TAG, "turn viewssss to bitmap"); Canvas localCanvas = null; Paint mPaint = null; Bitmap newBitmap = null; int[] location = new int[2]; Bitmap localBitmap = null; for(View view : arrayView){ localBitmap = viewToBitmap(view); view.getLocationOnScreen(location); //画一个和view尺寸大小的画布 if(null == localCanvas){ newBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Config.ARGB_8888); localCanvas = new Canvas(newBitmap); mPaint = new Paint(); mPaint.setAntiAlias(true); } localCanvas.drawBitmap(localBitmap, location[0], location[1], mPaint); localBitmap.recycle(); localBitmap = null; } if(localCanvas != null){ localCanvas.save(Canvas.ALL_S***E_FLAG); localCanvas.restore(); } return newBitmap; } /** * b保存bitmap为图片 * @param paramBitmap * @param paramString */ public void savePic(Bitmap paramBitmap, String paramString){ FileOutputStream fsStream = null; try { fsStream = new FileOutputStream(paramString); if(fsStream != null){ paramBitmap.compress(Bitmap.CompressFormat.PNG, 90, fsStream); //压缩保存 fsStream.flush(); fsStream.close(); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block Log.i(TAG, e.toString()); Log.i(TAG, "savePic() paramString is not exist" + paramString); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); Log.i(TAG, "savePic() close() flush() is error!"); } } /** * 保存图片线程 * @author JackZhouYu * 将内存中的bitmap写到file里面去,因为是一个比较耗时的工作,所以开线程存储 */ private class SavePicThread implements Runnable{ private Bitmap bitmap; public SavePicThread(Bitmap bitmap){ this.bitmap = bitmap; } @Override public void run() { // TODO Auto-generated method stub File file1 = Environment.getExternalStorageDirectory(); File file2 = new File(file1,PATH); //创建目录 if(!file2.exists()){ file2.mkdir(); } String filename = file2.getAbsolutePath() + "/" + System.currentTimeMillis() + ".png"; Log.i(TAG, "save pic to file " + filename); savePic(bitmap, filename); bitmap.recycle(); bitmap = null; } } private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { Log.i(TAG, "mHandler i got message"); // TODO Auto-generated method stub if(msg.what == MSG_WHAT){ RecoderService.this.screenShot(); if(isStart){ mHandler.sendEmptyMessageDelayed(MSG_WHAT, MSG_DELEAY); } } } }; }
但是,这只能录制当前App下的图像
相关文章推荐
- Xamarin.Android广播接收器
- Android 一些基本组件的使用
- Android Studio 配置 Gradle 直接运行打包(relase)程序 图文详解
- android带返回按钮的自定义标题栏布局文件详解
- Android 实现LazyFragment
- 目前和接下来的android
- Android基础
- Android APK反编译就这么简单 详解(附图)
- Android开发日记(一)
- android菜鸟修炼记
- Android项目——导入工程出错的解决问题
- Android 学习笔记之AndBase框架学习(七) SlidingMenu滑动菜单的实现
- Android打包安装过程
- [Android] 关于Android的.so文件你所需要知道的 - 简书
- android四大组件之Service 电话录音
- 深入浅出 - Android系统移植与平台开发(十一) - Sensor HAL框架分析之一
- 彻底明白Android中AIDL及其使用
- android 之 自定义圆形头像
- android应用层相关设置及命令(二)
- 自定义日历控件-——Android