简体   繁体   中英

Is this a bug in Kotlin's "By Lazy"?

I've been trying to track down this strange anomaly and believe it's a bug in Kotlin. Whenever I click on one of the first two buttons in my UI, the last button mirrors its background and acts as though it was also clicked.

I believe the bug is related to my use of "by lazy", because if I alter the code and instead use a function to assign the background the bug disappears.

I made a repo of the defect here . The app is simple, just 3 toggle buttons that looks like this:

截屏

In this screenshot I've clicked on button #1 only and notice that button #3's background has also changed according to the assigned background drawable, even though if you inspect button #3 it will show as "checked=false"

Here's my fragment code:

class FirstFragment : Fragment() {

    private val buttonBackgroundDrawable by lazy {
        context?.let { context ->
            ContextCompat.getDrawable(context, R.drawable.button_background)
        }
    }

    private fun getButtonBackground(): Drawable? {
        return context?.let { context ->
            ContextCompat.getDrawable(context, R.drawable.button_background)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_first, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // Bugged
        buttonBackgroundDrawable?.let { bgDrawable ->
            toggleButton1.background = bgDrawable
            toggleButton2.background = bgDrawable
            toggleButton3.background = bgDrawable
        }

        // Works
//            toggleButton1.background = getButtonBackground()
//            toggleButton2.background = getButtonBackground()
//            toggleButton3.background = getButtonBackground()

    }
}

And here is the background drawable code:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="true">
        <shape android:shape="oval">
            <corners android:radius="6dp" />
            <solid android:color="#f00" />
        </shape>
    </item>
    <item>
        <shape android:shape="oval">
            <corners android:radius="6dp" />
            <solid android:color="#aaa" />
            <stroke android:width="3dp" android:color="#fff" />
        </shape>
    </item>
</selector>

If someone either confirm that this is a bug or explain to me what I've done wrong in my code, I'd love to hear it, because I'm totally baffled by this one.

This is expected behavior. lazy initializes the property one time, only the first time it is accessed, so you are instantiating a single Drawable that is shared by all your buttons. Changing the Drawable instance's property values will affect anything sharing that same Drawable instance.

As for why your second button isn't changing, this may just be an undefined behavior resulting from using a StateListDrawable instance on multiple Views, something that was never intended to be done, but apparently doesn't trigger any warnings.

On the other hand, your getButtonBackground() function instantiates a distinct Drawable instance each time it is called, so then each button gets its own distinct Drawable.

This is not a lazy bug, this is caused due to single instance of the drawable. getButtonBackground() creates new instance everytime you call it. Change in property of single instance of drawable affect the other view you've applied to. So you need to create new drawable for all buttons or set in xml.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM