AutoBackgroundBackButton 在ScrollView上方自动根据返回键按钮下方内容动态改变颜色。自动变色返回键

在日常有时候有一些为了优化体验的需求。AutoBackgroundBackButton 一个可以根据按钮下方背景颜色动态的改版返回键自定义ImageView。这里只展示了黑白切换方式,你如果还有其他需求可以参考颜色校验来自己实现切换对应颜色按钮。【例如白色背景展示黑色样式,图片上方显示白色样式】

返回键效果录屏

下面展示文件 AutoBackgroundBackButton.kt

javascript 复制代码
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.abs

class AutoBackgroundBackButton @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) {

    private var mScrollView: NestedScrollView? = null

    private val handler = Handler(Looper.getMainLooper())
    private val delayMillis = 100L // 延迟截图生成的时间,单位为毫秒
    private var isScrolling = false
    fun setScrollView(scrollView: NestedScrollView) {
        scrollView.viewTreeObserver.addOnGlobalLayoutListener {
            mScrollView = scrollView
            scrollView.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->
                isScrolling = true // 设置滚动状态为 true
                handler.removeCallbacksAndMessages(null) // 移除之前的延迟任务
                handler.postDelayed({
                    if (isScrolling) {
                        isScrolling = false // 滚动结束,将滚动状态设置为 false
                        updateImageResource()
                    }
                }, 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().second
            val y1 = mScrollView!!.getLocationOnScreen().second
            val 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 = startY
        val endYOnScreen = endY
        // 计算在 ScrollView 上的位置
        val startYOnScrollView = startYOnScreen + scrollView.scrollY
        // 截取屏幕上的指定区域
        var regionHeight = endYOnScreen - startYOnScreen
        try {
            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,直接返回null

        val 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)) / 255
        return 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 = 0
        var green: Long = 0
        var blue: Long = 0
        var count = 0

        for (pixel in pixels) {
            red += Color.red(pixel)
            green += Color.green(pixel)
            blue += Color.blue(pixel)
            count++
        }

        if (count == 0) {
            return Color.WHITE // 默认颜色
        }

        red /= count
        green /= count
        blue /= count

        return Color.rgb(red.toInt(), green.toInt(), blue.toInt())
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        handler.removeCallbacksAndMessages(null) // 移除之前的延迟任务
    }
}

xml中使用

javascript 复制代码
    <com.xxx.weight.AutoBackgroundBackButton
        android: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

javascript 复制代码
backButton.setScrollView(scrollView)

这样就可以实现返回键和ScrollView的滑动关联,在滑动的时候能自动的根据下方内容而变化颜色。

相关推荐
用户0273851840267 小时前
[Android]RecycleView的item用法
android
前行的小黑炭8 小时前
Android :为APK注入“脂肪”,论Android垃圾代码在安全加固中的作用
android·kotlin
帅得不敢出门9 小时前
Docker安装Ubuntu搭建Android SDK编译环境
android·ubuntu·docker
tangweiguo030519879 小时前
Android Kotlin 动态注册 Broadcast 的完整封装方案
android·kotlin
fatiaozhang95279 小时前
浪潮CD1000-移动云电脑-RK3528芯片-2+32G-安卓9-2种开启ADB ROOT刷机教程方法
android·网络·adb·电脑·电视盒子·刷机固件·机顶盒刷机
前行的小黑炭10 小时前
Android 不同构建模式下使用不同类的例子:如何在debug模式和release模式,让其使用不同的类呢?
android·kotlin·gradle
andyguo11 小时前
AI模型测评平台工程化实战十二讲(第一讲:从手工测试到系统化的觉醒)
android
2501_9159214311 小时前
小团队如何高效完成 uni-app iOS 上架,从分工到工具组合的实战经验
android·ios·小程序·uni-app·cocoa·iphone·webview
幂简集成11 小时前
通义灵码 AI 程序员低代码 API 课程实战教程
android·人工智能·深度学习·神经网络·低代码·rxjava
2501_9160088912 小时前
uni-app iOS 文件管理与 itools 配合实战,多工具协作的完整流程
android·ios·小程序·https·uni-app·iphone·webview