Android實(shí)現(xiàn)截屏功能
導(dǎo)言
目前截屏的方法很多,root不適用,要么其他方法就是有局限性,而其中官方給出的方案最好—MediaProjection
介紹
Android 5.0以后開(kāi)放的錄屏API,取視頻中的一幀數(shù)據(jù),這樣就可以實(shí)現(xiàn)截屏
步驟
在activity中授權(quán),在service中完成初始化并截圖,當(dāng)然可以后臺(tái)定時(shí)截圖,但是6.0系統(tǒng)會(huì)有內(nèi)存溢出的bug
1:build.gradle
compileSdkVersion 21
buildToolsVersion '27.0.3'
defaultConfig {
applicationId "com.aile.screenshot"
multiDexEnabled true
minSdkVersion 21
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
2:在activity中授權(quán)
public void requestCapturePermission() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return;
}
MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);
}
@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) {
Service.setResultData(data);
startService(new Intent(this, Service.class));
finish();
}
break;
}
}
3:在service中初始化ImageReader,MediaProjection
private void createImageReader() {
mImageReader = ImageReader.newInstance(mScreenWidth, mScreenHeight, PixelFormat.RGBA_8888, 1);
}
public void setUpMediaProjection() {
mMediaProjection = getMediaProjectionManager().getMediaProjection(Activity.RESULT_OK, mResultData);
}
}
4:在service中完成截圖重要步驟:
private void startScreenShot() {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
startVirtual();
}
}, 0);
handler.postDelayed(new Runnable() {
@Override
public void run() {
startCapture();
}
}, 50);
}
public void startVirtual() {
if (mMediaProjection != null) {
virtualDisplay();
} else {
setUpMediaProjection();
virtualDisplay();
}
}
private void virtualDisplay() {
mVirtualDisplay = mMediaProjection.createVirtualDisplay("screen-mirror",
mScreenWidth, mScreenHeight, mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mImageReader.getSurface(), null, null);
}
//異常處理的核心
private void startCapture() {
Image image = null;
try {
image = mImageReader.acquireLatestImage();
} catch (IllegalStateException e) {
if (null != image) {
image.close();
image = null;
image = mImageReader.acquireLatestImage();
}
}
if (image == null) {
startScreenShot();
} else {
SaveTask mSaveTask = new SaveTask();
AsyncTaskCompat.executeParallel(mSaveTask, image);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
stopVirtual();
tearDownMediaProjection();
}
}, 0);
}
}
public class SaveTask extends AsyncTask<Image, Void, Bitmap> {
@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();
return bitmap;
}
@Override
protected void onPostExecute(final Bitmap bitmap) {
super.onPostExecute(bitmap);
//處理bitmap的業(yè)務(wù)代碼
}
5:Bitmap轉(zhuǎn)IS流,指定區(qū)域截圖
// 將Bitmap轉(zhuǎn)換成InputStream ByteArrayOutputStream bos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos); InputStream inputStream = new ByteArrayInputStream(bos.toByteArray()); //指定區(qū)域截圖 Rect mRect = new Rect(51, 74, 58, 62); BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, true); Bitmap bm = bitmapRegionDecoder.decodeRegion(mRect, null);
6:定時(shí)任務(wù)的處理
private Timer timer = new Timer();
public void shootByTime() {
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
startScreenShot();
super.handleMessage(msg);
}
};
timer.schedule(new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
}, 0, 100);
}
7:橫豎屏的處理
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == this.getResources().getConfiguration().ORIENTATION_PORTRAIT) {
mRect = new Rect(51, 775, 745, 47);
} else if (newConfig.orientation == this.getResources().getConfiguration().ORIENTATION_LANDSCAPE) {
mRect = new Rect(54, 24, 545, 45);
}
}
8:還有很多,只需按照需求走就OK,沒(méi)有難的東西,需要不停的學(xué)習(xí)和積累
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
android handler.post和handler.sendMessage的區(qū)別和聯(lián)系
handler.post和handler.sendMessage本質(zhì)上是沒(méi)有區(qū)別的,都是發(fā)送一個(gè)消息到消息隊(duì)列中,而且消息隊(duì)列和handler都是依賴于同一個(gè)線程的。接下來(lái)通過(guò)本文給大家分享android handler.post和handler.sendMessage的區(qū)別和聯(lián)系,一起看看吧2017-08-08
Android計(jì)時(shí)與倒計(jì)時(shí)實(shí)現(xiàn)限時(shí)搶購(gòu)的5種方法
這篇文章主要為大家詳細(xì)介紹了Android計(jì)時(shí)與倒計(jì)時(shí)實(shí)現(xiàn)限時(shí)搶購(gòu)的5種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02
Android應(yīng)用隱私合規(guī)檢測(cè)實(shí)現(xiàn)方案詳解
這篇文章主要介紹了Android應(yīng)用隱私合規(guī)檢測(cè)實(shí)現(xiàn)方案,我們需要做的就是提前檢測(cè)好自己的應(yīng)用是否存在隱私合規(guī)問(wèn)題,及時(shí)整改過(guò)來(lái),下面提供Xposed Hook思路去檢測(cè)隱私合規(guī)問(wèn)題,建議有Xposed基礎(chǔ)的童鞋閱讀,需要的朋友可以參考下2022-07-07
Android自定義View實(shí)現(xiàn)拖拽效果
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)拖拽效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11
Android 通過(guò)Messager與Service實(shí)現(xiàn)進(jìn)程間雙向通信案例詳解
這篇文章主要介紹了Android 通過(guò)Messager與Service實(shí)現(xiàn)進(jìn)程間雙向通信案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09
Android實(shí)現(xiàn)畫(huà)板功能(二)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)畫(huà)板功能的第二篇,使用imageView,bitmap方式實(shí)現(xiàn)畫(huà)板,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05
Android RefreshLayout實(shí)現(xiàn)下拉刷新布局
這篇文章主要為大家詳細(xì)介紹了Android RefreshLayout實(shí)現(xiàn)下拉刷新布局,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10
Android學(xué)習(xí)教程之日歷控件使用(7)
這篇文章主要為大家詳細(xì)介紹了Android學(xué)習(xí)教程之日歷控件操作代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11

