一、起始原因
最近项目需求中需要实现屏幕截图,开启了新一轮的翻腾,找寻。是的,我就是一个搬运工,简单的搬运工~~做不完的功能,连接不断地需求~~
基本需求:实现当前页面截图并保存;
扩展需求:截图去除自己添加的控件;
完善需求:截图响应速度要快;
反馈完善需求:适配所有机型。
二、具体实现方式
1),第一种实现方式
/**
* 对View进行量测,布局后截图
*
* @param view
* @return
*/
public Bitmap convertViewToBitmap(View view) {
view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bitmap = view.getDrawingCache();
return bitmap;
}
此种实现方式,基本可以实现所有View的截图及全屏幕的截图。
在实际的需求中,是对WebView进行截图,且WebView的展示中,是使用绘制程序绘制部分内容。这种方式,绘制的内容截屏展示不出来,只能另寻他法。
/**
* 获取整个窗口的截图
*
* @param context
* @return
*/
@SuppressLint("NewApi")
private Bitmap captureScreen(Activity context) {
View cv = context.getWindow().getDecorView();
cv.setDrawingCacheEnabled(true);
cv.buildDrawingCache();
Bitmap bmp = cv.getDrawingCache();
if (bmp == null) {
return null;
}
bmp.setHasAlpha(false);
bmp.prepareToDraw();
return bmp;
}
/**
* 对单独某个View进行截图
*
* @param v
* @return
*/
private Bitmap loadBitmapFromView(View v) {
if (v == null) {
return null;
}
Bitmap screenshot;
screenshot = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.RGB_565);
Canvas c = new Canvas(screenshot);
c.translate(-v.getScrollX(), -v.getScrollY());
v.draw(c);
return screenshot;
}
/**
* 对WebView进行截图
*
* @param webView
* @return
*/
public static Bitmap captureWebView1(WebView webView) {//可执行
Picture snapShot = webView.capturePicture();
Bitmap bmp = Bitmap.createBitmap(snapShot.getWidth(),
snapShot.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
snapShot.draw(canvas);
return bmp;
}
此种实现方式相对较复杂,且很多方法不支持版本较低的Android系统版本。但能够很好地处理WebView绘制内容截图不成功的问题。
在实际的执行中,通过版本和Android内置系统的判断,多种截图方法综合使用,能够实现产品的需求。
只是后来,因为产品业务调整,这一块功能隐藏不使用了~~让我哭会~~~不管怎么样,也算是学东西了,只是后面的路,需要走的更稳妥,更踏实一些~~~
//定义使用变量
/**
* 截屏相关
*/
private MediaProjectionManager mediaProjectionManager;
private MediaProjection mMediaProjection;
private VirtualDisplay mVirtualDisplay;
private static Intent mResultData = null;
private ImageReader mImageReader;
private WindowManager mWindowManager;
private WindowManager.LayoutParams mLayoutParams;
private GestureDetector mGestureDetector;
private ImageView mFloatView;
private int mScreenWidth;
private int mScreenHeight;
private int mScreenDensity;
private String mPhoneType;
public static final int REQUEST_MEDIA_PROJECTION = 18;
//onCreate()初始化变量
/**
* 初始化变量
*/
mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);
mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getMetrics(metrics);
mScreenDensity = metrics.densityDpi;
mScreenWidth = metrics.widthPixels;
mScreenHeight = metrics.heightPixels;
mImageReader = ImageReader.newInstance(mScreenWidth, mScreenHeight, PixelFormat.RGBA_8888, 1);
//截图方法体的实现
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_MEDIA_PROJECTION:
if (resultCode == RESULT_OK && data != null) {
mResultData = data;
//startService(new Intent(getApplicationContext(), FloatWindowsService.class));
}
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
try {
stopVirtual();
tearDownMediaProjection();
} catch (Exception e) {
e.printStackTrace();
}
}
private void startScreenShot() {
Handler handler1 = new Handler();
handler1.postDelayed(new Runnable() {
public void run() {
// start virtual
startVirtual();
}
}, 5);
handler1.postDelayed(new Runnable() {
public void run() {
// capture the screen
startCapture();
}
}, 30);
}
public void startVirtual() {
if (mMediaProjection != null) {
virtualDisplay();
} else {
setUpMediaProjection();
virtualDisplay();
}
}
private void stopVirtual() {
if (mVirtualDisplay == null) {
return;
}
mVirtualDisplay.release();
mVirtualDisplay = null;
}
private void startCapture() {
Image image = mImageReader.acquireLatestImage();
if (image == null) {
startScreenShot();
} else {
SaveTask mSaveTask = new SaveTask();
// mSaveTask.execute(image);
if (Build.VERSION.SDK_INT >= 11) {
// From API 11 onwards, we need to manually select the
// THREAD_POOL_EXECUTOR
AsyncTaskCompatHoneycomb.executeParallel(mSaveTask, image);
} else {
// Before API 11, all tasks were run in parallel
mSaveTask.execute(image);
}
// AsyncTaskCompat.executeParallel(mSaveTask, image);
}
}
static class AsyncTaskCompatHoneycomb {
static void executeParallel(AsyncTask task, Params... params) {
// 这里显示调用了THREAD_POOL_EXECUTOR,所以就可以使用该线程池了
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
}
}
@SuppressLint("NewApi")
private void virtualDisplay() {
Surface sf = mImageReader.getSurface();
mVirtualDisplay = mMediaProjection.createVirtualDisplay(
"screen-mirror", mScreenWidth, mScreenHeight, mScreenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mImageReader.getSurface(), null, null);
}
public void setUpMediaProjection() {
if (mResultData == null) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
startActivity(intent);
} else {
mMediaProjection = getMediaProjectionManager().getMediaProjection(
Activity.RESULT_OK, mResultData);
}
}
private MediaProjectionManager getMediaProjectionManager() {
return (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
}
public class SaveTask extends AsyncTask {
@Override
protected Bitmap doInBackground(Image... params) {
if (params == null || params.length < 1 || params[0] == null) {
return null;
}
Image image = params[0];
int width = image.getWidth();
int height = image.getHeight();
final Image.Plane[] planes = image.getPlanes();
final ByteBuffer buffer = planes[0].getBuffer();
// 每个像素的间距
int pixelStride = planes[0].getPixelStride();
// 总的间距
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * width;
Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);
image.close();
// File file = new File(SAVE_REAL_PATH);
// if (bitmap != null) {
// try {
// if (!file.exists()) {
// file.mkdirs();
// }
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.US);
// String fileImage = file.getAbsolutePath() + "/" + sdf.format(new Date()) + ".jpg";
// FileOutputStream out = new FileOutputStream(fileImage);
// if (out != null) {
// bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
// out.flush();
// out.close();
// Intent media = new Intent(
// Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
// Uri contentUri = Uri.fromFile(file);
// media.setData(contentUri);
// sendBroadcast(media);
// fileDestUri = fileImage;
// }
// } catch (FileNotFoundException e) {
// e.printStackTrace();
// file = null;
// } catch (IOException e) {
// e.printStackTrace();
// file = null;
// }
// }
// if (file != null) {
return bitmap;
// }
// return null;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
// 预览图片
if (bitmap != null) {
//也可以处理保存图片逻辑
captureIV.setImageBitmap(bitmap);
}
}
}
private void tearDownMediaProjection() {
if (mMediaProjection != null) {
mMediaProjection.stop();
mMediaProjection = null;
}
}
//使用
startScreenShot();
三、事后总结
除去上面展示的形式,还测试了其他几种方式,并没有获取到想要的结果。要是兄弟们知道怎么解决,可以一起沟通,一起交流哈~~
1),第一种尝试
/**
* 应用反射的方法
* 【反射不成功】
*
* @return
*/
private Bitmap getBitmapReverse() {
Bitmap mScreenBitmap = null;
DisplayMetrics mDisplayMetrics = new DisplayMetrics();
float[] dims = {mDisplayMetrics.widthPixels,
mDisplayMetrics.heightPixels};
Class> demo = null;
try {
demo = Class.forName("android.view.Surface");
} catch (Exception e) {
e.printStackTrace();
}
try {
Method method = demo.getMethod("screenshot", new Class[]{int.class, int.class});
mScreenBitmap = (Bitmap) method.invoke(demo.newInstance(), (int) dims[0], (int) dims[1]);
//这里其实可以直接用null替换demo.newInstance(),因为screenshot是静态方法,所以第一个invoke的第一个参数会被自动忽略~所以其实你填什么都没关系。
//获取的返回值是个bitmap,然后我们就可以为所欲为了~
} catch (Exception e) {
e.printStackTrace();
}
return mScreenBitmap;
}
应用反射的方法,反射Surface成功,但是反射screenshot()方法不成功,没能够实现截图;
2),第二种尝试
/**
* 需要root权限
*
* @param activity
* @return
*/
public Bitmap captureScreenSystem(Activity activity) {
// 获取屏幕大小:
DisplayMetrics metrics = new DisplayMetrics();
WindowManager WM = (WindowManager) activity
.getSystemService(Context.WINDOW_SERVICE);
Display display = WM.getDefaultDisplay();
display.getMetrics(metrics);
int height = metrics.heightPixels; // 屏幕高
int width = metrics.widthPixels; // 屏幕的宽
// 获取显示方式
int pixelformat = display.getPixelFormat();
PixelFormat localPixelFormat1 = new PixelFormat();
PixelFormat.getPixelFormatInfo(pixelformat, localPixelFormat1);
int deepth = localPixelFormat1.bytesPerPixel;// 位深
byte[] piex = new byte[height * width * deepth];
try {
Runtime.getRuntime().exec(
new String[]{"/system/bin/su", "-c",
"chmod 777 /dev/graphics/fb0"});
} catch (IOException e) {
e.printStackTrace();
}
try {
// 获取fb0数据输入流
InputStream stream = new FileInputStream(new File(
"/dev/graphics/fb0"));
DataInputStream dStream = new DataInputStream(stream);
dStream.readFully(piex);
} catch (Exception e) {
e.printStackTrace();
}
// 保存图片
int[] colors = new int[height * width];
for (int m = 0; m < colors.length; m++) {
int r = (piex[m * 4] & 0xFF);
int g = (piex[m * 4 + 1] & 0xFF);
int b = (piex[m * 4 + 2] & 0xFF);
int a = (piex[m * 4 + 3] & 0xFF);
colors[m] = (a << 24) + (r << 16) + (g << 8) + b;
}
// piex生成Bitmap
Bitmap bitmap = Bitmap.createBitmap(colors, width, height,
Bitmap.Config.ARGB_8888);
return bitmap;
}
反思一:
在实现图片的保存中,保存时耗时操作,为更好地用户体验,最好保存图片在子线程进行。
以下是压缩生成图片的方法:
FileOutputStream out = null;
try {
out = new FileOutputStream(fname);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
bitmap1.compress(Bitmap.CompressFormat.JPEG, 20, out);
选择图片的格式,压缩图片质量,都会影响生成图片的快慢。在实际中,项目保存图片应用20的质量,大家依据自己的需求来做变更~~~
反思二:
产品决定了上层了流程,会影响后续的很多环节。产品一旦修改,研发,测试都得跟着变更~时间延期,工作重复,都是影响团队决战胜利的因素。
当然了,作为研发,实现产品的需求是我们的天职。别给产品讨论需求哦~~
哈哈哈,若是倾权,立删~~~
研发也是一个小人物,调侃调侃生活~~~做好自己该做的,把自己能做的做得更好,愿天下和平~~~~
欢迎小伙伴共同提高进步~~~~
异次元传送门 ---- 哈哈,我就是一个Demo而已~_~
分离也许是为了更好的团聚,闯荡也许是为了更好的生活……又有谁想背井离乡孤独一人拼搏了,又有谁不想花前月下团团圆圆啦……生活是现实的,人都活在现实生活之中,每一个人,每一个家庭都有各自的活法及生活方式,谁不希望美好的生活,美好的生活是要努力打拼奋斗获得的……
趁还有时间,给自已一点不一样~~~~