在日常有时候有一些为了优化体验的需求。AutoBackgroundBackButton 一个可以根据按钮下方背景颜色动态的改版返回键自定义ImageView。这里只展示了黑白切换方式,你如果还有其他需求可以参考颜色校验来自己实现切换对应颜色按钮。【例如白色背景展示黑色样式,图片上方显示白色样式】
返回键效果录屏
下面展示文件 AutoBackgroundBackButton.kt
。
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.drawable.BitmapDrawable
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.view.View
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.widget.NestedScrollView
import kotlin.math.absclass AutoBackgroundBackButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) {private var mScrollView: NestedScrollView? = nullprivate val handler = Handler(Looper.getMainLooper())private val delayMillis = 100L // 延迟截图生成的时间,单位为毫秒private var isScrolling = falsefun setScrollView(scrollView: NestedScrollView) {scrollView.viewTreeObserver.addOnGlobalLayoutListener {mScrollView = scrollViewscrollView.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->isScrolling = true // 设置滚动状态为 truehandler.removeCallbacksAndMessages(null) // 移除之前的延迟任务handler.postDelayed({if (isScrolling) {isScrolling = false // 滚动结束,将滚动状态设置为 falseupdateImageResource()}}, delayMillis)})updateImageResource()}}private fun updateImageResource() {val bottomAreaColor = getBottomAreaColor()//方式1:可以直接设置setImageResource来改变//setImageResource(if (isColorDark(bottomAreaColor)) R.drawable.ic_back_white else R.drawable.ic_back_black)//方式2:使用imageTintList属性来直接改版图片颜色val states = arrayOf(intArrayOf())val colors = intArrayOf(if (isColorDark(bottomAreaColor)) Color.WHITE else Color.BLACK)imageTintList = ColorStateList(states, colors)}private fun getBottomAreaColor(): Int {if (mScrollView == null || mScrollView?.width == 0 || mScrollView?.height == 0) {return Color.WHITE} else {val y0 = getLocationOnScreen().secondval y1 = mScrollView!!.getLocationOnScreen().secondval startY = abs(y0 - y1)val bitmap = getScreenshotOfScreenRegion(mScrollView!!,width,startY,startY + height)if (bitmap != null && !bitmap.isRecycled) {if (bitmap.height == 0) {return Color.WHITE} else {val color = getAverageColorFromBitmap(bitmap, 0, 0, bitmap.width, bitmap.height)//测试预览,可以打开注解查看[打开记得注释bitmap.recycle()]
// setBackgroundDrawable(BitmapDrawable(resources, bitmap))bitmap.recycle()return color}} else {return Color.WHITE}}}fun View.getLocationOnScreen(): Pair<Int, Int> {val location = IntArray(2)getLocationOnScreen(location)return Pair(location[0], location[1])}private fun getScreenshotOfScreenRegion(scrollView: NestedScrollView,bitmapWidth: Int,startY: Int,endY: Int): Bitmap? {// 获取屏幕的截图val screenBitmap = getScrollViewScreenshot(scrollView, bitmapWidth) ?: return null// 计算在屏幕上的起始位置和结束位置val startYOnScreen = startYval endYOnScreen = endY// 计算在 ScrollView 上的位置val startYOnScrollView = startYOnScreen + scrollView.scrollY// 截取屏幕上的指定区域var regionHeight = endYOnScreen - startYOnScreentry {return Bitmap.createBitmap(screenBitmap,0,startYOnScrollView,bitmapWidth,regionHeight)} catch (ex: Exception) {return null}}private fun getScrollViewScreenshot(scrollView: NestedScrollView, bitmapWidth: Int): Bitmap? {val height = scrollView.getChildAt(0)?.height ?: return null // 获取 ScrollView 内容的高度if (height == 0) return null // 如果 ScrollView 的高度为0,直接返回nullval bitmap = Bitmap.createBitmap(bitmapWidth, height, Bitmap.Config.ARGB_8888)val canvas = Canvas(bitmap)scrollView.draw(canvas)return bitmap}private fun isColorDark(color: Int): Boolean {val darkness =1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255return darkness >= 0.1}private fun getAverageColorFromBitmap(bitmap: Bitmap, startX: Int, startY: Int, width: Int, height: Int): Int {val pixels = IntArray(width * height)bitmap.getPixels(pixels, 0, width, startX, startY, width, height)var red: Long = 0var green: Long = 0var blue: Long = 0var count = 0for (pixel in pixels) {red += Color.red(pixel)green += Color.green(pixel)blue += Color.blue(pixel)count++}if (count == 0) {return Color.WHITE // 默认颜色}red /= countgreen /= countblue /= countreturn Color.rgb(red.toInt(), green.toInt(), blue.toInt())}override fun onDetachedFromWindow() {super.onDetachedFromWindow()handler.removeCallbacksAndMessages(null) // 移除之前的延迟任务}
}
xml中使用
<com.xxx.weight.AutoBackgroundBackButtonandroid:id="@+id/backButton"android:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="10dp"android:src="@drawable/ic_back_black"android:tint="@color/white" />
代码中关联ScrollView
backButton.setScrollView(scrollView)
这样就可以实现返回键和ScrollView的滑动关联,在滑动的时候能自动的根据下方内容而变化颜色。