成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

截屏實現方式和監聽截屏詳解

移動開發 Android
今天我們來介紹下Android里截屏方面的知識點介紹;通過獲取DecorView的方式來實現截屏(前提是當前Activity已經加載完成),DecorView為整個Window界面的最頂層View,因此截屏不包含狀態欄(SystemUI)部分.

[[433218]]

前言

今天我們來介紹下Android里截屏方面的知識點介紹;

一、Android截屏的方式

1、獲取DecorView截屏

通過獲取DecorView的方式來實現截屏(前提是當前Activity已經加載完成),DecorView為整個Window界面的最頂層View,因此截屏不包含狀態欄(SystemUI)部分.

方式一

  1. View view = getWindow().getDecorView();     // 獲取DecorView 
  2.    view.setDrawingCacheEnabled(true); 
  3.    view.buildDrawingCache(); 
  4.    Bitmap bitmap1 = view.getDrawingCache();    

方式二

  1. Bitmap bitmap2 = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888); 
  2.    Canvas canvas = new Canvas(); 
  3.    canvas.setBitmap(bitmap2); 
  4.    view.draw(canvas); 

保存 bitmap1 或 bitmap2 均可保存截屏圖片

2、調用系統源碼截屏

由于是hide api,通過反射調用如下:

  1. public Bitmap takeScreenShot() { 
  2.         Bitmap bmp = null
  3.         mDisplay.getMetrics(mDisplayMetrics); 
  4.         float[] dims = {(float) mDisplayMetrics.widthPixels, (float) heightPixels}; 
  5.         float degrees = getDegreesForRotation(mDisplay.getRotation()); 
  6.         boolean requiresRotation = degrees > 0; 
  7.         if (requiresRotation) { 
  8.             mDisplayMatrix.reset(); 
  9.             mDisplayMatrix.preRotate(-degrees); 
  10.             mDisplayMatrix.mapPoints(dims); 
  11.             dims[0] = Math.abs(dims[0]); 
  12.             dims[1] = Math.abs(dims[1]); 
  13.         } 
  14.         try { 
  15.             Class<?> demo = Class.forName("android.view.SurfaceControl"); 
  16.             Method method = demo.getMethod("screenshot", new Class[]{Integer.TYPE, Integer.TYPE}); 
  17.             bmp = (Bitmap) method.invoke(demo, new Object[]{Integer.valueOf((int) dims[0]), Integer.valueOf((int) dims[1])}); 
  18.             if (bmp == null) { 
  19.                 return null
  20.             } 
  21.             if (requiresRotation) { 
  22.                 Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels, heightPixels, Bitmap.Config.RGB_565); 
  23.                 Canvas c = new Canvas(ss); 
  24.                 c.translate((float) (ss.getWidth() / 2), (float) (ss.getHeight() / 2)); 
  25.                 c.rotate(degrees); 
  26.                 c.translate((-dims[0] / 2), (-dims[1] / 2)); 
  27.                 c.drawBitmap(bmp, 0, 0, null); 
  28.                 c.setBitmap(null); 
  29.                 bmp.recycle(); 
  30.                 bmp = ss; 
  31.             } 
  32.             if (bmp == null) { 
  33.                 return null
  34.             } 
  35.             bmp.setHasAlpha(false); 
  36.             bmp.prepareToDraw(); 
  37.             return bmp; 
  38.         } catch (Exception e) { 
  39.             e.printStackTrace(); 
  40.             return bmp; 
  41.         } 
  42.     } 

二、截屏手機里的監聽

利用FileObserver監聽某個目錄中資源變化情況;

利用ContentObserver監聽全部資源的變化;

ContentObserver:通過ContentObserver監聽圖片多媒體的變化,當手機上有新圖片文件產生時會通過MediaProvider類向圖片數據庫插入一條記錄,監聽圖片插入事件來獲得圖片的URI;

今天講的是通過ContentObserver實現;

1、ScreenShotHelper截屏幫助類

  1. /** 
  2.  * Description: 截屏幫助類 
  3.  */ 
  4. class ScreenShotHelper { 
  5.     companion object { 
  6.         const val TAG = "ScreenShotLog" 
  7.         /** 
  8.          * 讀取媒體數據庫時需要讀取的列 
  9.          */ 
  10.         val MEDIA_PROJECTIONS = arrayOf( 
  11.             MediaStore.Images.ImageColumns.DATA, 
  12.             MediaStore.Images.ImageColumns.DATE_TAKEN 
  13.         ) 
  14.         /** 
  15.          * 讀取媒體數據庫時需要讀取的列,其中 width、height 字段在 API 16 之后才有 
  16.          */ 
  17.         val MEDIA_PROJECTIONS_API_16 = arrayOf( 
  18.             MediaStore.Images.ImageColumns.DATA, 
  19.             MediaStore.Images.ImageColumns.DATE_TAKEN, 
  20.             MediaStore.Images.ImageColumns.WIDTH, 
  21.             MediaStore.Images.ImageColumns.HEIGHT 
  22.         ) 
  23.         /** 
  24.          * 截屏路徑判斷的關鍵字 
  25.          */ 
  26.         val KEYWORDS = arrayOf( 
  27.             "screenshot""screen_shot""screen-shot""screen shot"
  28.             "screencapture""screen_capture""screen-capture""screen capture"
  29.             "screencap""screen_cap""screen-cap""screen cap" 
  30.         ) 
  31.         fun showLog(msg: String) { 
  32.             Log.d(TAG, msg) 
  33.         } 
  34.     } 

2、監聽器ScreenShotListener

  1. /** 
  2.  * Description: 截屏監聽 
  3.  */ 
  4. class ScreenShotListener constructor(context: Context?) { 
  5.     private var mContext: Context 
  6.     private var mScreenRealSize: Point? = null 
  7.     private val mHasCallbackPaths: ArrayList<String> = ArrayList() 
  8.     private var mListener: OnScreenShotListener? = null 
  9.     private var mStartListenTime: Long = 0 
  10.     /** 
  11.      * 內部存儲器內容觀察者 
  12.      */ 
  13.     private var mInternalObserver: MediaContentObserver? = null 
  14.     /** 
  15.      * 外部存儲器內容觀察者 
  16.      */ 
  17.     private var mExternalObserver: MediaContentObserver? = null 
  18.     /** 
  19.      * 運行在 UI 線程的 Handler, 用于運行監聽器回調 
  20.      */ 
  21.     private var mUiHandler = Handler(Looper.getMainLooper()) 
  22.     init { 
  23.         ScreenShotHelper.showLog("init"
  24.         assertInMainThread() 
  25.         requireNotNull(context) { "The context must not be null." } 
  26.         mContext = context 
  27.         if (mScreenRealSize == null) { 
  28.             mScreenRealSize = getRealScreenSize() 
  29.             if (mScreenRealSize != null) { 
  30.                 ScreenShotHelper.showLog("Screen Real Size: " + mScreenRealSize!!.x + " * " + mScreenRealSize!!.y) 
  31.             } else { 
  32.                 ScreenShotHelper.showLog("Get screen real size failed."
  33.             } 
  34.         } 
  35.     } 
  36.     /** 
  37.      * 單例 
  38.      */ 
  39.     companion object : SingletonHolder<ScreenShotListener, Context>(::ScreenShotListener) 
  40.     /** 
  41.      * 開啟監聽 
  42.      */ 
  43.     fun startListener() { 
  44.         assertInMainThread() 
  45.         // 記錄開始監聽的時間戳 
  46.         mStartListenTime = System.currentTimeMillis() 
  47.         // 創建內容觀察者 
  48.         mInternalObserver = 
  49.             MediaContentObserver(MediaStore.Images.Media.INTERNAL_CONTENT_URI, mUiHandler) 
  50.         mExternalObserver = 
  51.             MediaContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mUiHandler) 
  52.         // 注冊內容觀察者 
  53.         mContext.contentResolver.registerContentObserver( 
  54.             MediaStore.Images.Media.INTERNAL_CONTENT_URI, 
  55.             false
  56.             mInternalObserver 
  57.         ) 
  58.         mContext.contentResolver.registerContentObserver( 
  59.             MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
  60.             false
  61.             mExternalObserver 
  62.         ) 
  63.     } 
  64.     fun stopListener() { 
  65.         assertInMainThread() 
  66.         // 注銷內容觀察者 
  67.         if (mInternalObserver != null) { 
  68.             try { 
  69.                 mContext.contentResolver.unregisterContentObserver(mInternalObserver!!) 
  70.             } catch (e: Exception) { 
  71.                 e.printStackTrace() 
  72.             } 
  73.             mInternalObserver = null 
  74.         } 
  75.         if (mExternalObserver != null) { 
  76.             try { 
  77.                 mContext.contentResolver.unregisterContentObserver(mExternalObserver!!) 
  78.             } catch (e: Exception) { 
  79.                 e.printStackTrace() 
  80.             } 
  81.             mExternalObserver = null 
  82.         } 
  83.         // 清空數據 
  84.         mStartListenTime = 0 
  85.         mListener = null 
  86.     } 
  87.     /** 
  88.      * 處理媒體數據庫的內容改變 
  89.      */ 
  90.     fun handleMediaContentChange(contentUri: Uri) { 
  91.         var cursorCursor? = null 
  92.         try { 
  93.             cursor = mContext.contentResolver.query( 
  94.                 contentUri, 
  95.                 if (Build.VERSION.SDK_INT < 16) ScreenShotHelper.MEDIA_PROJECTIONS else ScreenShotHelper.MEDIA_PROJECTIONS_API_16, 
  96.                 nullnull
  97.                 "${MediaStore.Images.ImageColumns.DATE_ADDED} desc limit 1" 
  98.             ) 
  99.             if (cursor == null) { 
  100.                 ScreenShotHelper.showLog("Deviant logic."
  101.                 return 
  102.             } 
  103.             if (!cursor.moveToFirst()) { 
  104.                 ScreenShotHelper.showLog("Cursor no data."
  105.                 return 
  106.             } 
  107.             // 獲取各列的索引 
  108.             val dataIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA) 
  109.             val dateTakenIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_TAKEN) 
  110.             var widthIndex = -1 
  111.             var heightIndex = -1 
  112.             if (Build.VERSION.SDK_INT >= 16) { 
  113.                 widthIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.WIDTH) 
  114.                 heightIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.HEIGHT) 
  115.             } 
  116.             // 獲取行數據 
  117.             val data = cursor.getString(dataIndex) 
  118.             val dateTaken = cursor.getLong(dateTakenIndex) 
  119.             var width = 0 
  120.             var height = 0 
  121.             if (widthIndex >= 0 && heightIndex >= 0) { 
  122.                 width = cursor.getInt(widthIndex) 
  123.                 height = cursor.getInt(heightIndex) 
  124.             } else { 
  125.                 val size = getImageSize(data) 
  126.                 width = size.x 
  127.                 height = size.y 
  128.             } 
  129.             // 處理獲取到的第一行數據 
  130.             handleMediaRowData(data, dateTaken, width, height) 
  131.         } catch (e: Exception) { 
  132.             ScreenShotHelper.showLog("Exception: ${e.message}"
  133.             e.printStackTrace() 
  134.         } finally { 
  135.             if (cursor != null && !cursor.isClosed) { 
  136.                 cursor.close() 
  137.             } 
  138.         } 
  139.     } 
  140.     private fun getImageSize(imagePath: String): Point { 
  141.         val options = BitmapFactory.Options() 
  142.         options.inJustDecodeBounds = true 
  143.         BitmapFactory.decodeFile(imagePath, options) 
  144.         return Point(options.outWidth, options.outHeight) 
  145.     } 
  146.     /** 
  147.      * 處理獲取到的一行數據 
  148.      */ 
  149.     private fun handleMediaRowData(data: String, dateTaken: Long, width: Int, height: Int) { 
  150.         if (checkScreenShot(data, dateTaken, width, height)) { 
  151.             ScreenShotHelper.showLog("ScreenShot: path = $data; size = $width * $height; date = $dateTaken"
  152.             if (mListener != null && !checkCallback(data)) { 
  153.                 mListener!!.onScreenShot(data) 
  154.             } 
  155.         } else { 
  156.             // 如果在觀察區間媒體數據庫有數據改變,又不符合截屏規則,則輸出到 log 待分析 
  157.             ScreenShotHelper.showLog("Media content changed, but not screenshot: path = $data; size = $width * $height; date = $dateTaken"
  158.         } 
  159.     } 
  160.     /** 
  161.      * 判斷指定的數據行是否符合截屏條件 
  162.      */ 
  163.     private fun checkScreenShot(data: String?, dateTaken: Long, width: Int, height: Int): Boolean { 
  164.         // 判斷依據一: 時間判斷 
  165.         // 如果加入數據庫的時間在開始監聽之前, 或者與當前時間相差大于10秒, 則認為當前沒有截屏 
  166.         if (dateTaken < mStartListenTime || System.currentTimeMillis() - dateTaken > 10 * 1000) { 
  167.             return false 
  168.         } 
  169.         // 判斷依據二: 尺寸判斷 
  170.         if (mScreenRealSize != null) { 
  171.             // 如果圖片尺寸超出屏幕, 則認為當前沒有截屏 
  172.             if (!(width <= mScreenRealSize!!.x && height <= mScreenRealSize!!.y) 
  173.                 || (height <= mScreenRealSize!!.x && width <= mScreenRealSize!!.y) 
  174.             ) { 
  175.                 return false 
  176.             } 
  177.         } 
  178.         // 判斷依據三: 路徑判斷 
  179.         if (data.isNullOrEmpty()) { 
  180.             return false 
  181.         } 
  182.         val lowerData = data.toLowerCase(Locale.getDefault()) 
  183.         // 判斷圖片路徑是否含有指定的關鍵字之一, 如果有, 則認為當前截屏了 
  184.         for (keyWork in ScreenShotHelper.KEYWORDS) { 
  185.             if (lowerData.contains(keyWork)) { 
  186.                 return true 
  187.             } 
  188.         } 
  189.         return false 
  190.     } 
  191.     /** 
  192.      * 判斷是否已回調過, 某些手機ROM截屏一次會發出多次內容改變的通知; <br></br> 
  193.      * 刪除一個圖片也會發通知, 同時防止刪除圖片時誤將上一張符合截屏規則的圖片當做是當前截屏. 
  194.      */ 
  195.     private fun checkCallback(imagePath: String): Boolean { 
  196.         if (mHasCallbackPaths.contains(imagePath)) { 
  197.             ScreenShotHelper.showLog("ScreenShot: imgPath has done; imagePath = $imagePath"
  198.             return true 
  199.         } 
  200.         // 大概緩存15~20條記錄便可 
  201.         if (mHasCallbackPaths.size >= 20) { 
  202.             for (i in 0..4) { 
  203.                 mHasCallbackPaths.removeAt(0) 
  204.             } 
  205.         } 
  206.         mHasCallbackPaths.add(imagePath) 
  207.         return false 
  208.     } 
  209.     /** 
  210.      * 獲取屏幕分辨率 
  211.      */ 
  212.     private fun getRealScreenSize(): Point? { 
  213.         var screenSize: Point? = null 
  214.         try { 
  215.             screenSize = Point() 
  216.             val windowManager = mContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager 
  217.             val defaultDisplay = windowManager.defaultDisplay 
  218.             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 
  219.                 defaultDisplay.getRealSize(screenSize) 
  220.             } else { 
  221.                 try { 
  222.                     val mGetRawW = Display::class.java.getMethod("getRawWidth"
  223.                     val mGetRawH = Display::class.java.getMethod("getRawHeight"
  224.                     screenSize.set
  225.                         mGetRawW.invoke(defaultDisplay) as Int
  226.                         mGetRawH.invoke(defaultDisplay) as Int 
  227.                     ) 
  228.                 } catch (e: Exception) { 
  229.                     screenSize.set(defaultDisplay.width, defaultDisplay.height) 
  230.                     e.printStackTrace() 
  231.                 } 
  232.             } 
  233.         } catch (e: Exception) { 
  234.             e.printStackTrace() 
  235.         } 
  236.         return screenSize 
  237.     } 
  238.     private fun assertInMainThread() { 
  239.         if (Looper.myLooper() != Looper.getMainLooper()) { 
  240.             val stackTrace = Thread.currentThread().stackTrace 
  241.             var methodMsg: String? = null 
  242.             if (stackTrace != null && stackTrace.size >= 4) { 
  243.                 methodMsg = stackTrace[3].toString() 
  244.             } 
  245.             ScreenShotHelper.showLog("Call the method must be in main thread: $methodMsg"
  246.         } 
  247.     } 
  248.     /** 
  249.      * 媒體內容觀察者 
  250.      */ 
  251.     private inner class MediaContentObserver(var contentUri: Uri, handler: Handler) : 
  252.         ContentObserver(handler) { 
  253.         override fun onChange(selfChange: Boolean) { 
  254.             super.onChange(selfChange) 
  255.             handleMediaContentChange(contentUri) 
  256.         } 
  257.     } 
  258.     /** 
  259.      * 設置截屏監聽器回調 
  260.      */ 
  261.     fun setListener(listener: OnScreenShotListener) { 
  262.         this.mListener = listener 
  263.     } 
  264.     /** 
  265.      * 截屏監聽接口 
  266.      */ 
  267.     interface OnScreenShotListener { 
  268.         fun onScreenShot(picPath: String) 
  269.     } 

3、使用

  1. class ScreenShotActivity : AppCompatActivity() { 
  2.     private lateinit var screenShotListener: ScreenShotListener 
  3.     var isHasScreenShotListener = false 
  4.     override fun onCreate(savedInstanceState: Bundle?) { 
  5.         super.onCreate(savedInstanceState) 
  6.         setContentView(R.layout.activity_screen_shot) 
  7.         screenShotListener = ScreenShotListener.getInstance(this) 
  8.         // 申請權限 
  9.         val permission = arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE) 
  10.         if (ActivityCompat.checkSelfPermission( 
  11.                 this, 
  12.                 Manifest.permission.READ_EXTERNAL_STORAGE 
  13.             ) != PackageManager.PERMISSION_GRANTED 
  14.         ) { 
  15.             ActivityCompat.requestPermissions(this, permission, 1001) 
  16.         } 
  17.     } 
  18.     override fun onRequestPermissionsResult( 
  19.         requestCode: Int
  20.         permissions: Array<out String>, 
  21.         grantResults: IntArray 
  22.     ) { 
  23.         super.onRequestPermissionsResult(requestCode, permissions, grantResults) 
  24.         if (requestCode == 1001) { 
  25.             if (grantResults[0] == PermissionChecker.PERMISSION_GRANTED) { 
  26.                 customToast("權限申請成功"
  27.             } else { 
  28.                 customToast("權限申請失敗"
  29.             } 
  30.         } 
  31.     } 
  32.     override fun onResume() { 
  33.         super.onResume() 
  34.         startScreenShotListen() 
  35.     } 
  36.     override fun onPause() { 
  37.         super.onPause() 
  38.         stopScreenShotListen() 
  39.     } 
  40.     private fun startScreenShotListen() { 
  41.         if (!isHasScreenShotListener) { 
  42.             screenShotListener.setListener(object : ScreenShotListener.OnScreenShotListener { 
  43.                 override fun onScreenShot(picPath: String) { 
  44.                     customToast("監聽截屏成功"
  45.                     Log.d(ScreenShotHelper.TAG, picPath) 
  46.                 } 
  47.             }) 
  48.             screenShotListener.startListener() 
  49.             isHasScreenShotListener = true 
  50.         } 
  51.     } 
  52.     private fun stopScreenShotListen() { 
  53.         if (isHasScreenShotListener) { 
  54.             screenShotListener.stopListener() 
  55.             isHasScreenShotListener = false 
  56.         } 
  57.     } 

注意點:

  • 若要監聽整個APP的所有頁面,則將監聽器加入到BaseActivity,在頁面的onResume中開啟監聽,在onPause停止監聽;
  • 需要讀取內部存儲(READ_EXTERNAL_STORAGE)權限;

總結

截屏還有滾動,以后會介紹實現方式;努力進步學習;

本文轉載自微信公眾號「Android開發編程」

 

責任編輯:姜華 來源: Android開發編程
相關推薦

2020-12-21 16:35:51

JavaScript網頁截屏代碼

2023-07-25 10:45:48

OHScrcpy鴻蒙

2021-08-08 14:15:30

Windows 11Windows微軟

2021-08-05 16:36:16

Windows 11操作系統微軟

2011-07-25 14:44:41

iPhone iPhone開發 截屏

2017-01-11 18:36:04

Android矩形區域截屏移動開發

2011-07-21 15:56:32

iPhone 截屏

2009-12-23 14:10:23

Linux截屏工具

2011-05-18 14:24:38

2017-03-10 14:23:18

Windows 10Windows截屏

2014-06-16 09:28:08

Linux命令行

2021-02-11 13:56:21

JSweb插件

2017-12-11 09:04:53

LinuxScrot截屏

2016-01-20 13:08:33

2018-04-24 13:40:59

Python盜號原理截圖

2020-02-03 10:10:05

Windows 10技巧Windows

2020-02-24 09:45:02

Bash截Linux系統配置

2010-02-24 14:50:33

Ubuntu vim

2013-10-24 14:24:17

搜狗輸入法

2018-10-15 10:42:17

Linux截屏工具
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久久久国产一区二区三区 | 日本高清中文字幕 | 免费的色网站 | 美女视频久久 | 黑人巨大精品欧美黑白配亚洲 | 国产伦精品一区二区三毛 | 国产伦精品一区二区三区高清 | 亚洲精品免费视频 | 国产精品久久国产精品久久 | 久久国产精品久久久久久 | 人人澡人人射 | 欧美一二区 | 国产成人叼嘿视频在线观看 | 亚洲国产成人av好男人在线观看 | 国产精品欧美一区二区三区不卡 | 91成人免费看 | 国产视频精品区 | 欧洲av在线 | 91av在线看| 日韩一区二区三区在线观看 | www.久久99 | 亚洲一区亚洲二区 | 91精品国产日韩91久久久久久 | 久久精品国产一区二区三区不卡 | 97国产一区二区精品久久呦 | 午夜精品一区二区三区免费视频 | 精品一区二区视频 | 337p日本欧洲亚洲大胆精蜜臀 | 91精品国产乱码久久久久久久久 | 午夜精品久久 | 亚洲国产精品自拍 | 亚洲美女一区 | 在线日韩 | 亚洲免费在线 | 男女午夜免费视频 | 在线观看亚洲精品视频 | 国产成人精品亚洲日本在线观看 | 久久五月婷 | 中文字幕在线国产 | 国产精品色一区二区三区 | 91精品国产91久久综合桃花 |