diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
index b268ef3..dc26666 100644
--- a/.idea/deploymentTargetSelector.xml
+++ b/.idea/deploymentTargetSelector.xml
@@ -4,6 +4,14 @@
+
+
+
+
+
+
+
+
diff --git a/README.md b/README.md
index 128aff6..ed21396 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ dependencyResolutionManagement {
```kotlin
dependencies {
- implementation("com.github.YenalyLiew:CircularRevealSwitch:0.4.5")
+ implementation("com.github.YenalyLiew:CircularRevealSwitch:0.4.6")
}
```
@@ -181,6 +181,16 @@ builder.setSwitcher();
## 更新
+### v0.4.6
+
+1. 使用 PixelCopyCompat,可使在 API 小于 26 时使用先进的 PixelCopy。虽然使用了反射调用,但整体速度还是比 `View#getDrawingCache()` 快不少
+
+ 图上部分为 PixelCopy 在 API 24 时的表现(平均耗时 40ms 左右),图下部分为 `View#getDrawingCache()` 的表现(平均耗时 60-70ms 左右)
+
+ ![](./docs/img/pixelcopy_android_7.png)
+
+2. `takeScreenshot()` 方法实现由 `View#getDrawingCache()` 修改为 `View#drawToBitmap()`,速度快一倍以上(平均耗时 30ms 左右)
+
### v0.4.5
1. 修复多 Activity 下,日间夜间切换动画无法正常播放的问题
diff --git a/README_EN.md b/README_EN.md
index 1d1e7fd..7f9cdfe 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -44,7 +44,7 @@ Add dependency in your module's build.gradle.kts
```kotlin
dependencies {
- implementation("com.github.YenalyLiew:CircularRevealSwitch:0.4.5")
+ implementation("com.github.YenalyLiew:CircularRevealSwitch:0.4.6")
}
```
@@ -164,6 +164,16 @@ builder.setSwitcher();
## Updates
+### v0.4.6
+
+1. Using PixelCopyCompat allows for the use of advanced PixelCopy when the API is less than 26. Although reflection calls are used, the overall speed is still much faster than `View#getDrawingCache()`.
+
+ The top part of the image shows the performance of PixelCopy at API 24 (average time consumption around 40ms), while the bottom part shows the performance of `View#getDrawingCache()` (average time consumption around 60-70ms).
+
+ ![](./docs/img/pixelcopy_android_7.png)
+
+2. The implementation of the `takeScreenshot()` method has been changed from `View#getDrawingCache()` to `View#drawToBitmap()`, which is more than twice as fast (average time consumption around 30ms).
+
### v0.4.5
1. Fixed the issue where the day-night switch animation cannot play normally under multiple Activities
diff --git a/circularrevealswitch/build.gradle.kts b/circularrevealswitch/build.gradle.kts
index 05f99bd..082e6fe 100644
--- a/circularrevealswitch/build.gradle.kts
+++ b/circularrevealswitch/build.gradle.kts
@@ -42,7 +42,7 @@ android {
afterEvaluate {
publishing {
- val versionName = "0.4.5"
+ val versionName = "0.4.6"
publications {
create("release") {
from(components["release"])
diff --git a/circularrevealswitch/src/main/java/com/yenaly/circularrevealswitch/CircularRevealSwitch.kt b/circularrevealswitch/src/main/java/com/yenaly/circularrevealswitch/CircularRevealSwitch.kt
index 150901a..6761ccd 100644
--- a/circularrevealswitch/src/main/java/com/yenaly/circularrevealswitch/CircularRevealSwitch.kt
+++ b/circularrevealswitch/src/main/java/com/yenaly/circularrevealswitch/CircularRevealSwitch.kt
@@ -22,6 +22,7 @@ import android.widget.ImageView
import androidx.core.animation.addListener
import androidx.core.view.children
import androidx.core.view.doOnAttach
+import androidx.core.view.drawToBitmap
import androidx.core.view.isInvisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
@@ -178,35 +179,32 @@ abstract class CircularRevealSwitch>(crSwitchBuilder: T)
*/
protected open fun Window.takeScreenshotCompat(): Bitmap {
val root = decorView.rootView
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val thread = HandlerThread("Screenshot")
+ try {
val bitmap = Bitmap.createBitmap(root.width, root.height, Bitmap.Config.ARGB_8888)
- val thread = HandlerThread("Screenshot")
thread.start()
var isSuccess = false
- try {
- val latch = CountDownLatch(1)
- var time = System.currentTimeMillis()
- PixelCopy.request(this, bitmap, { copyResult ->
- isSuccess = copyResult == PixelCopy.SUCCESS
- latch.countDown()
- }, Handler(thread.looper))
- isSuccess = latch.await(1000, TimeUnit.MILLISECONDS) && isSuccess
- time = System.currentTimeMillis() - time
- if (isSuccess) {
- if (DEBUG) {
- Log.d(TAG, "take screenshot by PixelCopy, time: $time ms")
- }
- return bitmap
+ val latch = CountDownLatch(1)
+ var time = System.currentTimeMillis()
+ PixelCopyCompat.request(this, bitmap, { copyResult ->
+ isSuccess = copyResult == PixelCopy.SUCCESS
+ latch.countDown()
+ }, Handler(thread.looper))
+ isSuccess = latch.await(1000, TimeUnit.MILLISECONDS) && isSuccess
+ time = System.currentTimeMillis() - time
+ if (isSuccess) {
+ if (DEBUG) {
+ Log.d(TAG, "take screenshot by PixelCopy, time: $time ms")
}
- return takeScreenshot()
- } catch (e: Exception) {
- e.printStackTrace()
- return takeScreenshot()
- } finally {
- thread.quit()
+ return bitmap
}
+ return takeScreenshot()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ return takeScreenshot()
+ } finally {
+ thread.quit()
}
- return takeScreenshot()
}
/**
@@ -216,13 +214,10 @@ abstract class CircularRevealSwitch>(crSwitchBuilder: T)
*
* @return Bitmap Returns a screenshot of the window.
*/
- @Suppress("DEPRECATION")
protected open fun Window.takeScreenshot(): Bitmap {
val root = decorView.rootView
var time = System.currentTimeMillis()
- root.isDrawingCacheEnabled = true
- val bitmap = Bitmap.createBitmap(root.drawingCache)
- root.isDrawingCacheEnabled = false
+ val bitmap = root.drawToBitmap()
time = System.currentTimeMillis() - time
if (DEBUG) {
Log.d(TAG, "take screenshot by default, time: $time ms")
diff --git a/circularrevealswitch/src/main/java/com/yenaly/circularrevealswitch/PixelCopyCompat.kt b/circularrevealswitch/src/main/java/com/yenaly/circularrevealswitch/PixelCopyCompat.kt
new file mode 100644
index 0000000..2e991ce
--- /dev/null
+++ b/circularrevealswitch/src/main/java/com/yenaly/circularrevealswitch/PixelCopyCompat.kt
@@ -0,0 +1,102 @@
+package com.yenaly.circularrevealswitch
+
+import android.annotation.SuppressLint
+import android.graphics.Bitmap
+import android.graphics.Rect
+import android.os.Build
+import android.os.Handler
+import android.view.PixelCopy
+import android.view.Surface
+import android.view.View
+import android.view.Window
+import androidx.annotation.RequiresApi
+
+object PixelCopyCompat {
+
+ fun request(
+ window: Window, srcRect: Rect?, dest: Bitmap,
+ listener: PixelCopy.OnPixelCopyFinishedListener,
+ listenerHandler: Handler,
+ ) {
+ when {
+ Build.VERSION.SDK_INT >= 26 -> Api26Impl.request(
+ window, srcRect, dest,
+ listener, listenerHandler
+ )
+
+ else -> Api24Impl.request(
+ window, dest, listener,
+ listenerHandler
+ )
+ }
+ }
+
+ fun request(
+ window: Window, dest: Bitmap,
+ listener: PixelCopy.OnPixelCopyFinishedListener,
+ listenerHandler: Handler,
+ ) = request(window, null, dest, listener, listenerHandler)
+
+ @RequiresApi(26)
+ internal object Api26Impl {
+ fun request(
+ window: Window, srcRect: Rect?, dest: Bitmap,
+ listener: PixelCopy.OnPixelCopyFinishedListener,
+ listenerHandler: Handler,
+ ) = PixelCopy.request(window, srcRect, dest, listener, listenerHandler)
+ }
+
+ internal object Api24Impl {
+ fun request(
+ window: Window, dest: Bitmap,
+ listener: PixelCopy.OnPixelCopyFinishedListener,
+ listenerHandler: Handler,
+ ) {
+ val insets = Rect()
+ val surface = sourceForWindow(window, insets)
+ PixelCopy.request(surface, dest, listener, listenerHandler)
+ }
+
+ @SuppressLint("PrivateApi")
+ private fun sourceForWindow(source: Window?, outInsets: Rect): Surface {
+ requireNotNull(source) { "source is null" }
+ requireNotNull(source.peekDecorView()) { "Only able to copy windows with decor views" }
+ var surface: Surface? = null
+ val dv = source.peekDecorView()
+ val root = dv?.let {
+ View::class.java.getDeclaredField("mAttachInfo").apply {
+ isAccessible = true
+ }[it]?.let { attachInfo ->
+ attachInfo.javaClass.getDeclaredField("mViewRootImpl").apply {
+ isAccessible = true
+ }[attachInfo]
+ }
+ } // as ViewRootImpl
+ if (root != null) {
+ surface = root.javaClass.getDeclaredField("mSurface").apply {
+ isAccessible = true
+ }[root] as Surface
+ val windowAttrs = root.javaClass.getDeclaredField("mWindowAttributes").apply {
+ isAccessible = true
+ }[root] // as WindowManager.LayoutParams
+ val surfaceInsets = windowAttrs.javaClass.getDeclaredField("surfaceInsets").apply {
+ isAccessible = true
+ }[windowAttrs] as Rect
+ // val width = root.javaClass.getDeclaredField("mWidth").apply {
+ // isAccessible = true
+ // }[root] as Int
+ // val height = root.javaClass.getDeclaredField("mHeight").apply {
+ // isAccessible = true
+ // }[root] as Int
+ val width = dv.rootView.width
+ val height = dv.rootView.height
+ outInsets.set(
+ surfaceInsets.left, surfaceInsets.top,
+ width + surfaceInsets.left, height + surfaceInsets.top
+ )
+ }
+ require(surface != null && surface.isValid) { "Window doesn't have a backing surface!" }
+ return surface
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/img/pixelcopy_android_7.png b/docs/img/pixelcopy_android_7.png
new file mode 100644
index 0000000..84f8a48
Binary files /dev/null and b/docs/img/pixelcopy_android_7.png differ