如何以编程方式在Android中截图?
我怎样才能截取手机屏幕的选定区域不是由任何程序,而是从代码?
这里是允许我的屏幕截图存储在SD卡上的代码,稍后用于任何您的需要:
首先,您需要添加一个适当的权限来保存文件:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
这是代码(在Activity中运行):
private void takeScreenshot() { Date now = new Date(); android.text.format.DateFormat.format("yyyy-MM-dd_hh:mm:ss", now); try { // image naming and path to include sd card appending name you choose for file String mPath = Environment.getExternalStorageDirectory().toString() + "/" + now + ".jpg"; // create bitmap screen capture View v1 = getWindow().getDecorView().getRootView(); v1.setDrawingCacheEnabled(true); Bitmap bitmap = Bitmap.createBitmap(v1.getDrawingCache()); v1.setDrawingCacheEnabled(false); File imageFile = new File(mPath); FileOutputStream outputStream = new FileOutputStream(imageFile); int quality = 100; bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream); outputStream.flush(); outputStream.close(); openScreenshot(imageFile); } catch (Throwable e) { // Several error may come out with file handling or DOM e.printStackTrace(); } }
这就是你如何打开最近生成的图像:
private void openScreenshot(File imageFile) { Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); Uri uri = Uri.fromFile(imageFile); intent.setDataAndType(uri, "image/*"); startActivity(intent); }
如果你想在片段视图上使用这个,然后使用:
View v1 = getActivity().getWindow().getDecorView().getRootView();
代替
View v1 = getWindow().getDecorView().getRootView();
在takeScreenshot()函数上
调用这个方法,传入你想要的屏幕截图的最外面的ViewGroup:
public Bitmap screenShot(View view) { Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); view.draw(canvas); return bitmap; }
注意:只适用于固定电话
以编程方式,你可以像下面那样运行adb shell /system/bin/screencap -p /sdcard/img.png
Process sh = Runtime.getRuntime().exec("su", null,null); OutputStream os = sh.getOutputStream(); os.write(("/system/bin/screencap -p " + "/sdcard/img.png").getBytes("ASCII")); os.flush(); os.close(); sh.waitFor();
然后阅读img.png
作为Bitmap
并使用您的愿望。
编辑:怜悯降薪。 2010年,我回答了这个问题。
所有允许截图的程序只能在根植的手机上使用。
Mualig的答案非常好,但是我遇到了Ewoks所描述的同样的问题,我没有得到这个背景。 所以有时候是够好的,有时我会在黑色的背景上看到黑色的文字(取决于主题)。
这个解决scheme主要基于Mualig代码和我在Robotium中find的代码。 我通过直接调用draw方法来放弃使用绘图caching。 在此之前,我将尝试从当前活动中获取可绘制的背景,以便首先绘制它。
// Some constants final static String SCREENSHOTS_LOCATIONS = Environment.getExternalStorageDirectory().toString() + "/screenshots/"; // Get device dimmensions Display display = getWindowManager().getDefaultDisplay(); Point size = new Point(); display.getSize(size); // Get root view View view = mCurrentUrlMask.getRootView(); // Create the bitmap to use to draw the screenshot final Bitmap bitmap = Bitmap.createBitmap(size.x, size.y, Bitmap.Config.ARGB_4444); final Canvas canvas = new Canvas(bitmap); // Get current theme to know which background to use final Activity activity = getCurrentActivity(); final Theme theme = activity.getTheme(); final TypedArray ta = theme .obtainStyledAttributes(new int[] { android.R.attr.windowBackground }); final int res = ta.getResourceId(0, 0); final Drawable background = activity.getResources().getDrawable(res); // Draw background background.draw(canvas); // Draw views view.draw(canvas); // Save the screenshot to the file system FileOutputStream fos = null; try { final File sddir = new File(SCREENSHOTS_LOCATIONS); if (!sddir.exists()) { sddir.mkdirs(); } fos = new FileOutputStream(SCREENSHOTS_LOCATIONS + System.currentTimeMillis() + ".jpg"); if (fos != null) { if (!bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos)) { Log.d(LOGTAG, "Compress/Write failed"); } fos.flush(); fos.close(); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
此方法不需要root权限或不需要大编码 。
在adb shell中使用下面的命令可以拍摄屏幕截图。
input keyevent 120
这个命令不需要任何root权限,所以你也可以从android应用程序的java代码执行同样的操作。
Process process; process = Runtime.getRuntime().exec("input keyevent 120");
有关Android中的keyevent代码的更多信息,请参阅http://developer.android.com/reference/android/view/KeyEvent.html
这里我们已经使用了。 KEYCODE_SYSRQ的值为120,用于系统请求/打印屏幕键。
正如CJBS所说,输出的图片将被保存在/ sdcard /图片/屏幕截图中
作为参考,捕获屏幕(而不仅仅是您的应用程序活动)的一种方法是捕获帧缓冲 (device / dev / graphics / fb0)。 为此,您必须拥有root权限,或者您的应用程序必须是具有签名权限的应用程序(“仅当请求的应用程序使用与声明权限的应用程序相同的证书进行签名时 ,系统才会授予的权限)”除非你编译你自己的ROM。
从我testing过的几个设备中,每个帧caching捕获的内容都只有一个截图。 人们已经报道它包含更多,我想这取决于框架/显示器的大小。
我试图不断读取framebuffer,但似乎返回固定数量的字节读取。 在我的情况下(3 410 432)字节,这足以存储854 * 480 RGBA(3 279 360字节)的显示帧。 是的,在我的设备中,从fb0输出的二进制帧是RGBA 。 这很可能取决于设备之间的设备。 这对你来说是很重要的解码=)
在我的设备/ dev / graphics / fb0权限是这样的,只有组和graphics用户可以读取fb0。
graphics是一个受限制的组,所以你可能只能使用su命令访问带有根的手机的fb0。
Android应用程序具有用户标识(uid)= app _ ##和组标识(guid)= app _ ## 。
adb shell有uid = shell和guid = shell ,比应用程序拥有更多的权限。 您实际上可以在/system/permissions/platform.xml中检查这些权限
这意味着你将能够在没有root的情况下在adb shell中读取fb0,但是你不会在没有root的应用程序中读取它。
此外,在AndroidManifest.xml中授予READ_FRAME_BUFFER和/或ACCESS_SURFACE_FLINGER权限对于常规应用程序无效,因为这些只适用于“ 签名 ”应用程序。
另外检查这个closures的线程的更多细节。
您可以尝试以下库: http : //code.google.com/p/android-screenshot-library/ Android Screenshot Library(ASL)能够以编程方式捕获Android设备的屏幕截图,而不需要具有root访问权限。 相反,ASL利用在后台运行的本地服务,每个设备启动时通过Androiddebugging桥(ADB)启动一次。
private void captureScreen() { View v = getWindow().getDecorView().getRootView(); v.setDrawingCacheEnabled(true); Bitmap bmp = Bitmap.createBitmap(v.getDrawingCache()); v.setDrawingCacheEnabled(false); try { FileOutputStream fos = new FileOutputStream(new File(Environment .getExternalStorageDirectory().toString(), "SCREEN" + System.currentTimeMillis() + ".png")); bmp.compress(CompressFormat.PNG, 100, fos); fos.flush(); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
我的解决scheme是:
public static Bitmap loadBitmapFromView(Context context, View v) { DisplayMetrics dm = context.getResources().getDisplayMetrics(); v.measure(MeasureSpec.makeMeasureSpec(dm.widthPixels, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dm.heightPixels, MeasureSpec.EXACTLY)); v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight()); Bitmap returnedBitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(), Bitmap.Config.ARGB_8888); Canvas c = new Canvas(returnedBitmap); v.draw(c); return returnedBitmap; }
和
public void takeScreen() { Bitmap bitmap = ImageUtils.loadBitmapFromView(this, view); //get Bitmap from the view String mPath = Environment.getExternalStorageDirectory() + File.separator + "screen_" + System.currentTimeMillis() + ".jpeg"; File imageFile = new File(mPath); OutputStream fout = null; try { fout = new FileOutputStream(imageFile); bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fout); fout.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { fout.close(); } }
图像保存在外部存储文件夹中。
你可以尝试做这样的事情,
从布局或视图获取位图caching通过执行类似的操作首先,您必须将setDrawingCacheEnabled设置为布局(线性布局或相关布局或视图)
然后
Bitmap bm = layout.getDrawingCache()
然后你用位图做任何你想要的。 要么将其转换为图像文件,要么将位图的uri发送到其他位置。
public class ScreenShotActivity extends Activity{ private RelativeLayout relativeLayout; private Bitmap myBitmap; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); relativeLayout = (RelativeLayout)findViewById(R.id.relative1); relativeLayout.post(new Runnable() { public void run() { //take screenshot myBitmap = captureScreen(relativeLayout); Toast.makeText(getApplicationContext(), "Screenshot captured..!", Toast.LENGTH_LONG).show(); try { if(myBitmap!=null){ //save image to SD card saveImage(myBitmap); } Toast.makeText(getApplicationContext(), "Screenshot saved..!", Toast.LENGTH_LONG).show(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); } public static Bitmap captureScreen(View v) { Bitmap screenshot = null; try { if(v!=null) { screenshot = Bitmap.createBitmap(v.getMeasuredWidth(),v.getMeasuredHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(screenshot); v.draw(canvas); } }catch (Exception e){ Log.d("ScreenShotActivity", "Failed to capture screenshot because:" + e.getMessage()); } return screenshot; } public static void saveImage(Bitmap bitmap) throws IOException{ ByteArrayOutputStream bytes = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 40, bytes); File f = new File(Environment.getExternalStorageDirectory() + File.separator + "test.png"); f.createNewFile(); FileOutputStream fo = new FileOutputStream(f); fo.write(bytes.toByteArray()); fo.close(); } }
join许可
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
对于那些想要捕获GLSurfaceView的人来说,getDrawingCache或绘图到canvas方法将不起作用。
在渲染帧之后,您必须读取OpenGL framebuffer的内容。 这里有一个很好的答案
基于@JustinMorris上面的答案和@NiravDangi这里https://stackoverflow.com/a/8504958/2232148我们必须采取一个视图的背景和前景,并像这样组装:;
public static Bitmap takeScreenshot(View view, Bitmap.Config quality) { Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), quality); Canvas canvas = new Canvas(bitmap); Drawable backgroundDrawable = view.getBackground(); if (backgroundDrawable != null) { backgroundDrawable.draw(canvas); } else { canvas.drawColor(Color.WHITE); } view.draw(canvas); return bitmap; }
质量参数采用Bitmap.Config常量,通常为Bitmap.Config.RGB_565
或Bitmap.Config.ARGB_8888
。
我创build了一个简单的库,从一个View
截图,并给你一个位图对象或直接保存到任何你想要的path
如果你想从fragment
获取屏幕截图,请执行以下操作:
-
覆盖
onCreateView()
:@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_one, container, false); mView = view; }
-
逻辑截图:
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { View view = mView.findViewById(R.id.scrollView1); shareScreenShotM(view, (NestedScrollView) view); }
-
方法
shareScreenShotM)()
:public void shareScreenShotM(View view, NestedScrollView scrollView){ bm = takeScreenShot(view,scrollView); //method to take screenshot File file = savePic(bm); // method to save screenshot in phone. }
-
方法takeScreenShot():
public Bitmap takeScreenShot(View u, NestedScrollView z){ u.setDrawingCacheEnabled(true); int totalHeight = z.getChildAt(0).getHeight(); int totalWidth = z.getChildAt(0).getWidth(); Log.d("yoheight",""+ totalHeight); Log.d("yowidth",""+ totalWidth); u.layout(0, 0, totalWidth, totalHeight); u.buildDrawingCache(); Bitmap b = Bitmap.createBitmap(u.getDrawingCache()); u.setDrawingCacheEnabled(false); u.destroyDrawingCache(); return b; }
-
方法savePic():
public static File savePic(Bitmap bm){ ByteArrayOutputStream bytes = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.JPEG, 100, bytes); File sdCardDirectory = new File(Environment.getExternalStorageDirectory() + "/Foldername"); if (!sdCardDirectory.exists()) { sdCardDirectory.mkdirs(); } // File file = new File(dir, fileName); try { file = new File(sdCardDirectory, Calendar.getInstance() .getTimeInMillis() + ".jpg"); file.createNewFile(); new FileOutputStream(file).write(bytes.toByteArray()); Log.d("Fabsolute", "File Saved::--->" + file.getAbsolutePath()); Log.d("Sabsolute", "File Saved::--->" + sdCardDirectory.getAbsolutePath()); } catch (IOException e) { e.printStackTrace(); } return file; }
对于活动,您可以简单地使用View v1 = getWindow().getDecorView().getRootView();
而不是mView
简短的方法是
FrameLayout layDraw = (FrameLayout) findViewById(R.id.layDraw); /*Your root view to be part of screenshot*/ layDraw.buildDrawingCache(); Bitmap bmp = layDraw.getDrawingCache();
随着捕捉截图,如果我们也想播放音调,我们可以使用下面的代码
MediaPlayer _shootMP = null; AudioManager manager = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); manager.setStreamVolume(AudioManager.STREAM_NOTIFICATION, manager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0); if (_shootMP == null) _shootMP = MediaPlayer .create(getActivity(), Uri.parse("file:///system/media/audio/ui/camera_click.ogg")); if (_shootMP != null) { try { _shootMP.start(); _shootMP.setOnCompletionListener(new OnCompletionListener() { @Override public void onCompletion(MediaPlayer arg0) { // release the media _shootMP.stop(); _shootMP.reset(); _shootMP.release(); _shootMP = null; } }); } catch (IllegalStateException e) { Log.w(TAG, "Exception takeScreenShot" + e.getMessage()); } }
只是扩大taraloca的答案。 您必须添加以下行来使其工作。 我已经使图像名称静态。 请确保您使用taraloca的时间戳variables,因为您需要dynamic图像名称。
// Storage Permissions private static final int REQUEST_EXTERNAL_STORAGE = 1; private static String[] PERMISSIONS_STORAGE = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; private void verifyStoragePermissions() { // Check if we have write permission int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE); if (permission != PackageManager.PERMISSION_GRANTED) { // We don't have permission so prompt the user ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE); }else{ takeScreenshot(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (requestCode == REQUEST_EXTERNAL_STORAGE) { takeScreenshot(); } } }
在AndroidManifest.xml文件中,以下条目是必须的:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
如果要捕获像RelativeLayout或LinearLayout等视图或布局,只需使用代码:
LinearLayout llMain = (LinearLayout) findViewById(R.id.linearlayoutMain); Bitmap bm = loadBitmapFromView(llMain);
现在您可以通过以下方式将此位图保存在设备存储上:
FileOutputStream outStream = null; File f=new File(Environment.getExternalStorageDirectory()+"/Screen Shots/"); f.mkdir(); String extStorageDirectory = f.toString(); File file = new File(extStorageDirectory, "my new screen shot"); pathOfImage = file.getAbsolutePath(); try { outStream = new FileOutputStream(file); bm.compress(Bitmap.CompressFormat.PNG, 100, outStream); Toast.makeText(getApplicationContext(), "Saved at "+f.getAbsolutePath(), Toast.LENGTH_LONG).show(); addImageGallery(file); //mail.setEnabled(true); flag=true; } catch (FileNotFoundException e) {e.printStackTrace();} try { outStream.flush(); outStream.close(); } catch (IOException e) {e.printStackTrace();}