如何在Android上以编程方式截屏?

如何通过代码而不是程序来截取手机屏幕的选定区域的截图?

432172 次浏览

你可以尝试以下库: http://code.google.com/p/android-screenshot-library/ Android截图库(ASL)允许以编程方式从Android设备捕获屏幕截图,而不需要具有root访问权限。相反,ASL利用在后台运行的本机服务,在每次设备启动时通过Android调试桥(ADB)启动

你可以尝试这样做,

从布局或视图中获取位图缓存 首先你要setDrawingCacheEnabled到一个布局(线性布局或相对布局,或视图)

然后

Bitmap bm = layout.getDrawingCache()

然后对位图做任何你想做的事情。要么将其转换为图像文件,要么将位图的uri发送到其他地方。

下面是允许我的截图存储在SD卡上的代码,以后无论你需要什么都可以使用:

首先,你需要添加一个适当的权限来保存文件:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

这是代码(运行在一个活动中):

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 ()函数

# EYZ0:

如果对话框包含一个表面视图,这个解决方案就不起作用。详情请查看以下问题的答案:

Android屏幕截图显示黑屏

Mualig的回答很好,但我遇到了Ewoks描述的同样的问题,我没有得到背景。所以有时已经足够好了,有时我得到黑色背景上的黑色文本(取决于主题)。

这个解决方案主要基于Mualig代码和我在Robotium中找到的代码。通过直接调用draw方法,我放弃了绘图缓存的使用。在此之前,我将尝试从当前活动中首先绘制背景。

// 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();
}

作为参考,捕获屏幕(而不仅仅是应用程序活动)的一种方法是捕获framebuffer (device /dev/graphics/fb0)。要做到这一点,你必须有根权限,或者你的应用程序必须是一个签名权限的应用程序(“只有当请求应用程序与声明该权限的应用程序使用相同的证书签名时,系统才授予该权限”)-这是不太可能的,除非你编译了自己的ROM。

从我测试过的几个设备上捕获的每个framebuffer都包含完全的一个截图。人们报告它包含更多,我猜这取决于框架/显示尺寸。

我尝试连续读取framebuffer,但它似乎返回了固定数量的读取字节。在我的例子中,这是(3 410 432)字节,这足以存储854*480 RGBA(3 279 360字节)的显示帧。是的,帧,二进制,从fb0输出是RGBA在我的设备。这很可能取决于不同的设备。这对你解码它很重要=)

在我的设备/dev/graphics/fb0权限是这样,只有根用户和用户组图形可以读取fb0。

图形是一个受限制的组,所以你可能只能使用su命令访问root手机的fb0。

Android应用程序有用户id (uid) = app_##组id (guid) = app_##

亚行壳Uid = shellGuid = shell,这比应用程序有更多的权限。你可以在/system/permissions/platform.xml中检查这些权限

这意味着你将能够在没有根的adb shell中读取fb0,但如果没有根,你将无法在应用中读取它。

同样,在AndroidManifest.xml上给予READ_FRAME_BUFFER和/或ACCESS_SURFACE_FLINGER权限对常规应用程序没有任何作用,因为这些只对“签名”应用程序有效。

还可以查看关闭线程以了解更多细节。

注:仅适用于root phone

通过编程,您可以如下所示运行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,按你的意愿使用。

调用这个方法,传入你想要屏幕截图的最外层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;
}

我的解决方案是:

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();
}
}

图像保存在外部存储文件夹中。

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();
}


}

添加权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
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();
}
}

在清单中添加权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

对于支持棉花糖或以上版本,请在onCreate方法中添加以下代码

ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},00);

我创建了一个简单的库,从View截图,要么给你一个位图对象,要么直接保存到任何你想要的路径

https://github.com/abdallahalaraby/Blink

该方法需要无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 /图片/截图

如果你想捕捉一个视图或布局,如RelativeLayoutLinearLayout等。

只需使用代码:

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();}

对于那些想要捕获GLSurfaceView的人,getDrawingCache或绘制到画布方法将不起作用。

在帧呈现之后,您必须读取OpenGL帧缓冲区的内容。有一个很好的答案在这里

根据@JustinMorris和@NiravDangi的回答,我们必须把一个视图的背景和前景像这样组合起来:

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。配置,通常是Bitmap.Config.RGB_565Bitmap.Config.ARGB_8888

如果你想从fragment中截图,请遵循以下步骤:

  1. < p >覆盖# EYZ0:

             @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;
    }
    
  2. Logic for taking screenshot:

     button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    View view =  mView.findViewById(R.id.scrollView1);
    shareScreenShotM(view, (NestedScrollView) view);
    }
    
  3. method 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.
    }
    
  4. method 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;
    }
    
  5. method 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;
    }
    

For activity you can simply use View v1 = getWindow().getDecorView().getRootView(); instead of mView

只是扩展塔拉洛卡的答案。您必须添加以下行才能使其工作。# EYZ0

    // 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" />

捷径是

FrameLayout layDraw = (FrameLayout) findViewById(R.id.layDraw); /*Your root view to be part of screenshot*/
layDraw.buildDrawingCache();
Bitmap bmp = layDraw.getDrawingCache();

仅限系统应用!

Process process;
process = Runtime.getRuntime().exec("screencap -p " + outputPath);
process.waitFor();

注意:系统应用不需要执行“su”命令执行该命令。

参数视图是根布局对象。

public static Bitmap screenShot(View view) {
Bitmap bitmap = null;
if (view.getWidth() > 0 && view.getHeight() > 0) {
bitmap = Bitmap.createBitmap(view.getWidth(),
view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
}
return bitmap;
}

这个问题的大多数答案使用Canvas绘图方法或绘图缓存方法。然而,View.setDrawingCache()方法在API 28中已弃用。目前,用于制作截图的推荐API是从API 24中获得的PixelCopy类(但是接受Window参数的方法是从API 26 == Android 8.0 Oreo中获得的)。下面是检索Bitmap的示例Kotlin代码:

@RequiresApi(Build.VERSION_CODES.O)
fun saveScreenshot(view: View) {
val window = (view.context as Activity).window
if (window != null) {
val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
val locationOfViewInWindow = IntArray(2)
view.getLocationInWindow(locationOfViewInWindow)
try {
PixelCopy.request(window, Rect(locationOfViewInWindow[0], locationOfViewInWindow[1], locationOfViewInWindow[0] + view.width, locationOfViewInWindow[1] + view.height), bitmap, { copyResult ->
if (copyResult == PixelCopy.SUCCESS) {
saveBitmap(bitmap)
}
// possible to handle other result codes ...
}, Handler())
} catch (e: IllegalArgumentException) {
// PixelCopy may throw IllegalArgumentException, make sure to handle it
}
}
}

在android中截取视图的截图。

public static Bitmap getViewBitmap(View v) {
v.clearFocus();
v.setPressed(false);


boolean willNotCache = v.willNotCacheDrawing();
v.setWillNotCacheDrawing(false);


int color = v.getDrawingCacheBackgroundColor();
v.setDrawingCacheBackgroundColor(0);


if (color != 0) {
v.destroyDrawingCache();
}
v.buildDrawingCache();
Bitmap cacheBitmap = v.getDrawingCache();
if (cacheBitmap == null) {
return null;
}


Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);


v.destroyDrawingCache();
v.setWillNotCacheDrawing(willNotCache);
v.setDrawingCacheBackgroundColor(color);


return bitmap;
}

对于整页滚动截图

如果你想要捕捉一个完整的视图截图(其中包含一个滚动视图左右),那么在这个库中进行检查

https://github.com/peter1492/LongScreenshot

你所要做的就是导入Gradel,并创建一个bigscreen的对象

# EYZ0

当自动滚动屏幕视图组并在最后组装在一起时,将接收回调的屏幕截图位图。

# EYZ0

哪些可以保存到画廊或任何必要的用途,他们之后

从Android 11 (API级别30),你可以用辅助服务截屏:

takeScreenshot -截取指定显示的屏幕截图,并通过AccessibilityService.ScreenshotResult返回。

如果你想捕捉视图的截图,使用View::drawToBitmap扩展函数:

val bitmap = myTargetView.drawToBitmap(/*Optional:*/ Bitmap.Config.ARGB_8888)

只需要确保使用AndroidX核心库的-ktx版本:

implementation("androidx.core:core-ktx:1.6.0")

我已经回答过类似的问题在这里

芬兰湾的科特林

    private fun screenShot() {
try {
val mPath: String = this.getExternalFilesDir(null).getAbsolutePath()
.toString() + "/temp" + ".png"
// create bitmap screenshot
val v1: View = getWindow().getDecorView().getRootView()
v1.isDrawingCacheEnabled = true
val bitmap = Bitmap.createBitmap(v1.drawingCache)
v1.isDrawingCacheEnabled = false
val imageFile = File(mPath)
val outputStream = FileOutputStream(imageFile)
val quality = 100
bitmap.compress(Bitmap.CompressFormat.PNG, quality, outputStream)
outputStream.flush()
outputStream.close()
        

//or you can share to test the method fast
val uriPath =
FileProvider.getUriForFile(this, getPackageName() + ".sharing.provider", imageFile)
val intent = Intent(Intent.ACTION_SEND)
intent.type = "image/*"
intent.clipData = ClipData.newRawUri("", uriPath)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
intent.putExtra(Intent.EXTRA_STREAM, uriPath)
startActivity(Intent.createChooser(intent, "Sharing to..."))
} catch (e: Throwable) {
e.printStackTrace()
}
}

Java

  private void screenShot() {
try {
String mPath = this.getExternalFilesDir(null).getAbsolutePath().toString() + "/temp" + ".png";
// create bitmap screenshot
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.PNG, quality, outputStream);
outputStream.flush();
outputStream.close();


//or you can share to test the method fast


Uri uriPath = FileProvider.getUriForFile(this, getPackageName() + ".sharing.provider", imageFile);
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/*");
intent.setClipData(ClipData.newRawUri("", uriPath));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.putExtra(Intent.EXTRA_STREAM, uriPath);
startActivity(Intent.createChooser(intent, "Sharing to..."));


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