Android之MediaProjectionManager实现手机截屏总结
比較好的文章:
Android中使用代碼截圖的各種方法總結(jié)
http://blog.csdn.net/woshinia/article/details/11520403
手機(jī)截屏:
http://www.cnblogs.com/tgyf/p/4655507.html
轉(zhuǎn)載的地方:
http://www.cnblogs.com/tgyf/p/4851092.html
分享一種截屏方法
在任何時(shí)候點(diǎn)擊手機(jī)上的浮動(dòng)小球(紅色圈內(nèi))就能完成整個(gè)屏幕信息的截取功能,而且最終保存的圖像還不會(huì)包含該小球,這就是本文將要介紹的 方法。手機(jī)整體屏幕獲取項(xiàng)目下載鏈接:http://files.cnblogs.com/files/tgyf/CaptureScreen.rar。以新的視角實(shí)現(xiàn)手機(jī)屏幕的截取(快照)功能,文章可能比較長(zhǎng),感興趣的朋友一定得看完,會(huì)有收獲的哦!若發(fā)現(xiàn)有哪些地方存在問題或某些功能有更好的實(shí)現(xiàn)方式,歡迎指點(diǎn),先謝過(我可以盡快改正或完善,以免繼續(xù)誤導(dǎo)別人)。
關(guān)于手機(jī)(或平板,以下描述均以手機(jī)表示這兩類設(shè)備)整體屏幕的截取,有的機(jī)型默認(rèn)設(shè)置的方式是同時(shí)按下電源鍵和一個(gè)音量鍵(如華碩、諾基亞等,向下音量鍵+電源鍵),有的機(jī)型是同時(shí)按下電源鍵和Home鍵(如蘋果)。另外,借助一些輔助工具經(jīng)過特定的設(shè)置也是可以完成屏幕快照的獲取。
從打算開發(fā)一個(gè)與傳統(tǒng)截屏方法不同的截屏應(yīng)用開始,針對(duì)Android手機(jī)截屏的基本原理、相關(guān)知識(shí)及Google最新案例,已經(jīng)在學(xué)習(xí)與摸索的途中寫過兩篇文章了,感興趣的朋友可以通過下面給出的鏈接去瞧一瞧(這方面知識(shí)掌握較好的可以直接忽略)。
在Win7+Android Studio環(huán)境嘗試了網(wǎng)絡(luò)上曬出的很多方式均失敗后,帶著受打擊的心態(tài)寫了第一篇文章:
“Android手機(jī)截屏”——http://www.cnblogs.com/tgyf/p/4655507.html。
當(dāng)時(shí)的目的是希望實(shí)現(xiàn)過的大神能給點(diǎn)有用的建議或意見。當(dāng)然,一般來說Android應(yīng)用在Android Studio和Eclipse下都是可以實(shí)現(xiàn)的,雖不能將項(xiàng)目代碼在兩者之間直接進(jìn)行轉(zhuǎn)換,但如若工作量不是特別大,移植起來也不麻煩,嘗試過的朋友應(yīng)該懂得。
可能對(duì)于截屏應(yīng)用進(jìn)行學(xué)習(xí)或者實(shí)現(xiàn)的人不太多,筆者并沒有得到什么寶貴性的建議。后面不甘心又進(jìn)行了一番Google,還是沒有直截了當(dāng)?shù)拇鸢?#xff0c;最后決定靜下心來老老實(shí)實(shí)地分析案例源碼(案例沒有屏幕數(shù)據(jù)獲取與圖片保存的實(shí)現(xiàn))。于是就有了第二篇文章:
“Google最新截屏案例詳解”——http://www.cnblogs.com/tgyf/p/4675082.html。
簡(jiǎn)單回顧一下:如果只是想得到正在運(yùn)行的應(yīng)用程序自身的界面,那相當(dāng)簡(jiǎn)單,真正獲取界面信息的代碼只有兩三句,在第一篇文章給出的例子中有提及。由于在舊版本的API中,Google是將手機(jī)截屏相關(guān)的方法與接口隱藏的,開發(fā)者要想自主實(shí)現(xiàn)手機(jī)完整屏幕的快照,有很多局限性(必須采用System級(jí)別的應(yīng)用開發(fā),或者在Linux下進(jìn)行Root、源碼編譯等操作),這部分內(nèi)容的總結(jié)在第二篇文章中。
大家都知道一些手機(jī)廠商會(huì)在自家手機(jī)發(fā)售前定制、預(yù)裝一些應(yīng)用,而這些應(yīng)用APK有些就是System級(jí)的(需要通過源碼Build)。也就是說不是沒有辦法利用隱藏的方法或借口實(shí)現(xiàn)手機(jī)截屏,而是本文將要尋找一種誰都能自己進(jìn)行實(shí)現(xiàn)的方式,包括初學(xué)者。
俗話說事不過三,今天這篇文章就來說說怎么實(shí)現(xiàn)輕松自在地、以一種全新的方式進(jìn)行手機(jī)截屏,希望能給人眼前一亮的感覺。
本文截屏應(yīng)用的實(shí)現(xiàn)思路大致是這樣:
1、拋棄組合快捷鍵,采用浮動(dòng)小圖標(biāo)作為截屏按鍵(類似于360浮動(dòng)小球,對(duì)其思想和詳細(xì)的實(shí)現(xiàn)方式感興趣的小伙伴可以見另一篇文章:“Android浮動(dòng)小球與開機(jī)自啟動(dòng)”——http://www.cnblogs.com/tgyf/p/4665401.html)。浮動(dòng)小圖標(biāo)的實(shí)現(xiàn)類Service1繼承自Service類,這樣可以方便地創(chuàng)建只有一個(gè)浮動(dòng)圖標(biāo)按鍵的布局,在Activity等地方利用startService(Intent intent)方法開啟服務(wù)。
2、由于浮動(dòng)按鍵本身不是希望截取的屏幕信息,故在開始截屏后將其隱藏,圖像保存后面再使其浮現(xiàn)。
3、圖像保存為PNG格式,路徑為手機(jī)sdcard的Pictures文件目錄下,而系統(tǒng)截屏默認(rèn)的目錄是Screenshots。
4、浮動(dòng)小球的優(yōu)先級(jí)為一般應(yīng)用的最頂層,即除了狀態(tài)欄下拉列表外,小球總是可見的,這要得益于Service類的性質(zhì)了。雖然在看電視等環(huán)境下會(huì)比較不適合,但該設(shè)計(jì)能讓用戶隨時(shí)、方便地截取到想要的屏幕圖像。
5、開機(jī)自啟動(dòng)功能雖然保留了,但是因?yàn)榻仄翍?yīng)用需要得到用戶的同意,所以在手機(jī)重啟后由廣播機(jī)制自動(dòng)打開的小球并不能完成截屏,還是需要點(diǎn)擊應(yīng)用圖標(biāo)打開應(yīng)用以進(jìn)行截取環(huán)境的請(qǐng)求。
文章開頭已經(jīng)給出整個(gè)工程的代碼(Android Studio版本),所以在介紹過程中就只給出實(shí)現(xiàn)截屏的關(guān)鍵代碼,感興趣的可以下載并自己進(jìn)行實(shí)踐。這里再給出鏈接為:http://files.cnblogs.com/files/tgyf/CaptureScreen.rar。一切準(zhǔn)備就緒,開始吧。
?
一、截屏請(qǐng)求結(jié)果數(shù)據(jù)共享類ShotApplication
上面已經(jīng)提到,屏幕獲取需要用戶同意,初次運(yùn)行時(shí)會(huì)有請(qǐng)求對(duì)話框,同意之后才能繼續(xù),否則程序會(huì)終止。既然需要用戶選擇后的信息,那在發(fā)出截屏請(qǐng)求時(shí)就不能用簡(jiǎn)單的startActivity(Intent intent)方法,而是要用startActivityForResult(Intent intent, intresquestCode)方法。而可恨的是Service類中startActivityForResult(Intent intent, int resquestCode)方法不可用,確切的說是不存在可供子類重載的onActivityResult(int resquestCode, int resultCode, Intent data) 方法。但現(xiàn)實(shí)是Service1類在實(shí)現(xiàn)截屏過程中又要用到后面兩個(gè)返回值(resultCode與data)來構(gòu)建MediaProjection類的對(duì)象。
可能有人會(huì)有疑問,那截屏過程直接在Activity中進(jìn)行不就可以了嗎?沒錯(cuò),是可以。但是問題在于我們需要在任何想截取屏幕的時(shí)候就能快速、方便地進(jìn)行,即需要借助利用Service實(shí)現(xiàn)并浮動(dòng)在一般性應(yīng)用窗口之上的小球。而在Activity中實(shí)現(xiàn)的話就達(dá)不到這種效果了,往往能獲取的只能是應(yīng)用本身界面,或者是將其隱藏后的下一層界面,總之做不到想要即可得的效果。
所以,首要問題是讓類Service1的對(duì)象能得到這兩個(gè)數(shù)據(jù)。另外得注意,Bundle可以完成一般數(shù)據(jù)的加載并賦給Intent類對(duì)象,然后一起發(fā)送給目標(biāo)類,但參數(shù)data本身就是Intent類型的。雖然Intent類存在putExtras(Intent src)方法,但為了體現(xiàn)面向?qū)ο髷?shù)據(jù)封裝的思想,這里采取的是數(shù)據(jù)共享的思路,利用繼承自Application類的子類ShotApplication,然后定義需要共享的成員變量(有些是其他類的對(duì)象)。類代碼很簡(jiǎn)單(不包括import *):
public class ShotApplication extends Application {private int result;private Intent intent;private MediaProjectionManager mMediaProjectionManager;public int getResult(){return result;}public Intent getIntent(){return intent;}public MediaProjectionManager getMediaProjectionManager(){return mMediaProjectionManager;}public void setResult(int result1){this.result = result1;}public void setIntent(Intent intent1){this.intent = intent1;}public void setMediaProjectionManager(MediaProjectionManager mMediaProjectionManager){this.mMediaProjectionManager = mMediaProjectionManager;} }其中MediaProjectionManager類對(duì)象在發(fā)送截屏請(qǐng)求和構(gòu)建MediaProjection類對(duì)象時(shí)均會(huì)用到,至于成員值的設(shè)置及獲取很直觀,就不解釋了。那么數(shù)據(jù)的傳遞就明朗了:先從主程序類MainActivity中存入共享類ShotApplication,然后服務(wù)類Service1從共享類ShotApplication中提取出來。
?
二、主程序類MainActivity所做4件事
先給出代碼(不包括import *),然后分析到底是哪4件事:
public class MainActivity extends ActionBarActivity {private int result = 0;private Intent intent = null;private int REQUEST_MEDIA_PROJECTION = 1;private MediaProjectionManager mMediaProjectionManager;@TargetApi(Build.VERSION_CODES.LOLLIPOP)@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mMediaProjectionManager = (MediaProjectionManager)getApplication().getSystemService(Context.MEDIA_PROJECTION_SERVICE);startIntent();}@TargetApi(Build.VERSION_CODES.LOLLIPOP)private void startIntent(){if(intent != null && result != 0){((ShotApplication)getApplication()).setResult(result);((ShotApplication)getApplication()).setIntent(intent);Intent intent = new Intent(getApplicationContext(), Service1.class);startService(intent);}else{startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);((ShotApplication)getApplication()).setMediaProjectionManager(mMediaProjectionManager);}}@TargetApi(Build.VERSION_CODES.LOLLIPOP)@Overridepublic void onActivityResult(int requestCode, int resultCode, Intent data) {if (requestCode == REQUEST_MEDIA_PROJECTION) {if (resultCode != Activity.RESULT_OK) {return;}else if(data != null && resultCode != 0){result = resultCode;intent = data;((ShotApplication)getApplication()).setResult(resultCode);((ShotApplication)getApplication()).setIntent(data);Intent intent = new Intent(getApplicationContext(), Service1.class);startService(intent);finish();}}} }向用戶提出截屏請(qǐng)求的代碼就是下面這句: startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);
這正是類MainActivity做的第1件事。當(dāng)然,在這之前需要獲取類MediaProjectionManager實(shí)例,代碼為:
mMediaProjectionManager = (MediaProjectionManager)getApplication().getSystemService(Context.MEDIA_PROJECTION_SERVICE);
由于onCreate()方法是應(yīng)用開啟后自動(dòng)調(diào)用的(startIntent隨即被調(diào)用),所以這一行截屏請(qǐng)求代碼也會(huì)自動(dòng)執(zhí)行。如果是應(yīng)用安裝后第一次開啟,那么就會(huì)彈出截屏權(quán)限允許對(duì)話框,需要用戶授權(quán)。如圖:
說到這,既可以解釋上面提到思路的第5條了,開機(jī)自啟動(dòng)后能夠開啟服務(wù),但不能執(zhí)行截屏請(qǐng)求操作。原因很簡(jiǎn)單:一開機(jī)就莫名其妙彈出截屏請(qǐng)求對(duì)話框不符合用戶使用習(xí)慣,再者無論是在廣播還是服務(wù)中調(diào)用sratActivityForResult()方法都是不太現(xiàn)實(shí)的。
類MainActivity做的第2件事就是將用戶操作所返回的值和初始獲取的類MediaProjectionManager實(shí)例寫入數(shù)據(jù)共享類ShotApplication中了,代碼如下:
1 //截屏請(qǐng)求對(duì)話框用戶操作返回?cái)?shù)據(jù)result和intent 2 ((ShotApplication)getApplication()).setResult(result); 3 ((ShotApplication)getApplication()).setIntent(intent); 4 //類MediaProjectionManager對(duì)象mMediaProjectionManager 5 ((ShotApplication)getApplication()).setMediaProjectionManager(mMediaProjectionManager);類MainActivity做的第3件事就是肯定是開啟截屏服務(wù)了,代碼如下:
Intent intent = new Intent(getApplicationContext(), Service1.class); 2 startService(intent); <span style="font-size: 16px;"> 注意自定義方法startIntent()時(shí)在onCreate()方法被調(diào)用,其在不同時(shí)期作用不同。如果在此次被調(diào)用之前用戶已經(jīng)允許過截屏操作,那么直接開啟截屏服務(wù);而如果沒有允許過,則向用戶請(qǐng)求,即做上述第1件事。</span><p><span style="font-size: 16px;"> 類MainActivity做的第4件事是將自身銷毀,之后的控制權(quán)就交給服務(wù)類Service1的浮動(dòng)小球(這即是該類整個(gè)界面)了。</span></p><pre name="code" class="html">1 finish();
三、服務(wù)類Service1完成整體屏幕截取
終于到了關(guān)鍵的類Service1了,同樣先給出代碼(不包括import *):
public class Service1 extends Service {private LinearLayout mFloatLayout = null;private WindowManager.LayoutParams wmParams = null;private WindowManager mWindowManager = null;private LayoutInflater inflater = null;private ImageButton mFloatView = null;private static final String TAG = "MainActivity";private SimpleDateFormat dateFormat = null;private String strDate = null;private String pathImage = null;private String nameImage = null;private MediaProjection mMediaProjection = null;private VirtualDisplay mVirtualDisplay = null;public static int mResultCode = 0;public static Intent mResultData = null;public static MediaProjectionManager mMediaProjectionManager1 = null;private WindowManager mWindowManager1 = null;private int windowWidth = 0;private int windowHeight = 0;private ImageReader mImageReader = null;private DisplayMetrics metrics = null;private int mScreenDensity = 0;@Overridepublic void onCreate(){// TODO Auto-generated method stubsuper.onCreate();createFloatView();createVirtualEnvironment();}@Overridepublic IBinder onBind(Intent intent){// TODO Auto-generated method stubreturn null;}private void createFloatView(){wmParams = new WindowManager.LayoutParams();mWindowManager = (WindowManager)getApplication().getSystemService(getApplication().WINDOW_SERVICE);wmParams.type = LayoutParams.TYPE_PHONE;wmParams.format = PixelFormat.RGBA_8888;wmParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE;wmParams.gravity = Gravity.LEFT | Gravity.TOP;wmParams.x = 0;wmParams.y = 0;wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;inflater = LayoutInflater.from(getApplication());mFloatLayout = (LinearLayout) inflater.inflate(R.layout.float_layout, null);mWindowManager.addView(mFloatLayout, wmParams);mFloatView = (ImageButton)mFloatLayout.findViewById(R.id.float_id);mFloatLayout.measure(View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));mFloatView.setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {// TODO Auto-generated method stubwmParams.x = (int) event.getRawX() - mFloatView.getMeasuredWidth() / 2;wmParams.y = (int) event.getRawY() - mFloatView.getMeasuredHeight() / 2 - 25;mWindowManager.updateViewLayout(mFloatLayout, wmParams);return false;}});mFloatView.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// hide the buttonmFloatView.setVisibility(View.INVISIBLE);Handler handler1 = new Handler();handler1.postDelayed(new Runnable() {public void run() {//start virtualstartVirtual();}}, 500);Handler handler2 = new Handler();handler2.postDelayed(new Runnable() {public void run() {//capture the screenstartCapture();}}, 1500);Handler handler3 = new Handler();handler3.postDelayed(new Runnable() {public void run() {mFloatView.setVisibility(View.VISIBLE);//stopVirtual();}}, 1000);}});Log.i(TAG, "created the float sphere view");}private void createVirtualEnvironment(){dateFormat = new SimpleDateFormat("yyyy_MM_dd_hh_mm_ss");strDate = dateFormat.format(new java.util.Date());pathImage = Environment.getExternalStorageDirectory().getPath()+"/Pictures/";nameImage = pathImage+strDate+".png";mMediaProjectionManager1 = (MediaProjectionManager)getApplication().getSystemService(Context.MEDIA_PROJECTION_SERVICE);mWindowManager1 = (WindowManager)getApplication().getSystemService(Context.WINDOW_SERVICE);windowWidth = mWindowManager1.getDefaultDisplay().getWidth();windowHeight = mWindowManager1.getDefaultDisplay().getHeight();metrics = new DisplayMetrics();mWindowManager1.getDefaultDisplay().getMetrics(metrics);mScreenDensity = metrics.densityDpi;mImageReader = ImageReader.newInstance(windowWidth, windowHeight, 0x1, 2); //ImageFormat.RGB_565Log.i(TAG, "prepared the virtual environment");}@TargetApi(Build.VERSION_CODES.LOLLIPOP)public void startVirtual(){if (mMediaProjection != null) {Log.i(TAG, "want to display virtual");virtualDisplay();} else {Log.i(TAG, "start screen capture intent");Log.i(TAG, "want to build mediaprojection and display virtual");setUpMediaProjection();virtualDisplay();}}@TargetApi(Build.VERSION_CODES.LOLLIPOP)public void setUpMediaProjection(){mResultData = ((ShotApplication)getApplication()).getIntent();mResultCode = ((ShotApplication)getApplication()).getResult();mMediaProjectionManager1 = ((ShotApplication)getApplication()).getMediaProjectionManager();mMediaProjection = mMediaProjectionManager1.getMediaProjection(mResultCode, mResultData);Log.i(TAG, "mMediaProjection defined");}@TargetApi(Build.VERSION_CODES.LOLLIPOP)private void virtualDisplay(){mVirtualDisplay = mMediaProjection.createVirtualDisplay("screen-mirror",windowWidth, windowHeight, mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,mImageReader.getSurface(), null, null);Log.i(TAG, "virtual displayed");}@TargetApi(Build.VERSION_CODES.LOLLIPOP)private void startCapture(){strDate = dateFormat.format(new java.util.Date());nameImage = pathImage+strDate+".png";Image image = mImageReader.acquireLatestImage();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();Log.i(TAG, "image data captured");if(bitmap != null) {try{File fileImage = new File(nameImage);if(!fileImage.exists()){fileImage.createNewFile();Log.i(TAG, "image file created");}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(fileImage);media.setData(contentUri);this.sendBroadcast(media);Log.i(TAG, "screen image saved");}}catch(FileNotFoundException e) {e.printStackTrace();}catch (IOException e){e.printStackTrace();}}}@TargetApi(Build.VERSION_CODES.LOLLIPOP)private void tearDownMediaProjection() {if (mMediaProjection != null) {mMediaProjection.stop();mMediaProjection = null;}Log.i(TAG,"mMediaProjection undefined");}private void stopVirtual() {if (mVirtualDisplay == null) {return;}mVirtualDisplay.release();mVirtualDisplay = null;Log.i(TAG,"virtual display stopped");}@Overridepublic void onDestroy(){// to remove mFloatLayout from windowManagersuper.onDestroy();if(mFloatLayout != null){mWindowManager.removeView(mFloatLayout);}tearDownMediaProjection();Log.i(TAG, "application destroy");} } 由于類Service1中大部分代碼和之前文章中給出的相差不大,接下來會(huì)先將類中各方法簡(jiǎn)單羅列一遍,然后更加著重介紹改進(jìn)的地方。從onCreate()方法開始,其調(diào)用了兩個(gè)方法:createFloatView()和createVirtualEnvironment();createFloatView()方法負(fù)責(zé)浮動(dòng)小球的生成、拖動(dòng)及其點(diǎn)擊事件的響應(yīng);createVirtualEnvironment()方法定義截屏所需的變量(包括屏幕信息、圖像格式、保存格式等等)。另外,各個(gè)方法利用Log日志類輸出了運(yùn)行過程中的狀態(tài)信息,便于觀察代碼執(zhí)行過程。
關(guān)鍵之處就在于對(duì)浮動(dòng)小球點(diǎn)擊事件的響應(yīng)實(shí)現(xiàn),而拖動(dòng)只是附帶的一個(gè)輔助功能而已。浮動(dòng)小球點(diǎn)擊事件的響應(yīng)代碼也完成了4件事情,下面一一進(jìn)行分析。
1、隱藏小球
1 mFloatView.setVisibility(View.INVISIBLE);2、初始化截屏環(huán)境
public void startVirtual(){if (mMediaProjection != null) {Log.i(TAG, "want to display virtual");virtualDisplay();} else {Log.i(TAG, "start screen capture intent");Log.i(TAG, "want to build mediaprojection and display virtual");setUpMediaProjection();virtualDisplay();} }
可以看出,這個(gè)過程先是對(duì)MediaProjection類實(shí)例mMediaProjection的值進(jìn)行了判斷,若之前沒有初始化(即值為null),則調(diào)用setUpMediaProjection()方法獲取共享數(shù)據(jù)并對(duì)其進(jìn)行賦值;若已初始化,則直接調(diào)用virtualDisplay()方法利用之前定義的變量對(duì)截屏環(huán)境進(jìn)行初始化,而真正執(zhí)行最終操作的方法為createVirtualDisplay()。
3、屏幕截取
????? 截屏環(huán)境初始化完成之后,便可以開始獲取屏幕信息了,所以接下來調(diào)用的是startCapture()方法。該方法的實(shí)現(xiàn)和之前不同,也是容易出錯(cuò)的地方在于以下兩句代碼:
int rowPadding = rowStride - pixelStride * width; 2 Bitmap bitmap = Bitmap.createBitmap(width+rowPadding/pixelStride, height, Bitmap.Config.ARGB_8888);值得注意的是調(diào)用方法createBitmap()創(chuàng)建Bitmap對(duì)象時(shí)所用的第1、3個(gè)參數(shù),分別對(duì)應(yīng)于圖像的寬度、格式。實(shí)現(xiàn)過程中發(fā)現(xiàn),只有將格式設(shè)置為ARGB_8888才能獲取想要的圖像質(zhì)量;而對(duì)于寬度,后面會(huì)解釋為什么要為其設(shè)置偏移值。
4、顯示小球
mFloatView.setVisibility(View.VISIBLE);四、總結(jié)
至于服務(wù)類Service1類中的其他代碼,以及用于開機(jī)自啟動(dòng)服務(wù)的廣播子類BootBroadcastReceiver這里就不打算介紹了。
下面來看看不同情況下的效果圖,這里指的不同情況跟上述創(chuàng)建位圖的兩個(gè)參數(shù)有關(guān)。
1、圖像格式
如果創(chuàng)建位圖時(shí)用的格式不是ARGB_8888,比如RGB_565,雖然屏幕信息的獲取沒有問題,但是在將信息轉(zhuǎn)化為圖像并保存的過程中出現(xiàn)了嚴(yán)重的偏差。如下圖:
2、寬度值之前介紹過調(diào)用createBitmap()方法設(shè)置其寬度參數(shù)時(shí)添加了偏移信息,如果不這么做,獲取的屏幕截圖會(huì)出現(xiàn)左邊部分缺失的情況(右邊會(huì)以黑色補(bǔ)全)。如下圖:
而圖像的寬度和格式參數(shù)設(shè)置正確后的截屏結(jié)果圖如下:?
總結(jié)
以上是生活随笔為你收集整理的Android之MediaProjectionManager实现手机截屏总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android之Intent.ACTIO
- 下一篇: Android之用SingleTask和