Skip to content
This repository has been archived by the owner on Sep 12, 2019. It is now read-only.

Reset cached bindings #7

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/androidTest/kotlin/butterknife/ViewTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,19 @@ public class ViewTest : AndroidTestCase() {
assertEquals(2, example.name.size)
}

public fun testReset() {
class Example(context: Context) : FrameLayout(context) {
val name : View? by bindOptionalView(1)
}

val example = Example(getContext())
example.addView(viewWithId(1))
assertNotNull(example.name)
example.removeAllViews()
ButterKnife.reset(example)
assertNull(example.name)
}

private fun viewWithId(id: Int) : View {
val view = View(getContext())
view.setId(id)
Expand Down
71 changes: 47 additions & 24 deletions src/main/kotlin/butterknife/ButterKnife.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import android.support.v7.widget.RecyclerView.ViewHolder
import android.view.View
import android.view.ViewGroup
import kotlin.properties.ReadOnlyProperty
import java.util.WeakHashMap
import java.util.Collections

public fun <T : View> ViewGroup.bindView(id: Int): ReadOnlyProperty<Any, T> = ViewBinding(id)
public fun <T : View> Activity.bindView(id: Int): ReadOnlyProperty<Any, T> = ViewBinding(id)
Expand Down Expand Up @@ -37,6 +39,12 @@ public fun <T : View> Fragment.bindOptionalViews(vararg ids: Int): ReadOnlyPrope
public fun <T : View> SupportFragment.bindOptionalViews(vararg ids: Int): ReadOnlyProperty<Any, List<T>> = OptionalViewListBinding(ids)
public fun <T : View> ViewHolder.bindOptionalViews(vararg ids: Int): ReadOnlyProperty<Any, List<T>> = OptionalViewListBinding(ids)

object ButterKnife {
fun reset(target: Any) {
LazyRegistry.reset(target)
}
}

private fun findView<T : View>(thisRef: Any, id: Int): T? {
[suppress("UNCHECKED_CAST")]
return when (thisRef) {
Expand All @@ -50,39 +58,38 @@ private fun findView<T : View>(thisRef: Any, id: Int): T? {
} as T?
}

private class ViewBinding<T : View>(val id: Int) : ReadOnlyProperty<Any, T> {
private val lazy = Lazy<T>()

override fun get(thisRef: Any, desc: PropertyMetadata): T = lazy.get {
findView<T>(thisRef, id)
?: throw IllegalStateException("View ID $id for '${desc.name}' not found.")
}
private class ViewBinding<T : View>(val id: Int) : LazyReadOnlyProperty<T>() {
override fun create(thisRef: Any, desc: PropertyMetadata): T =
findView<T>(thisRef, id)
?: throw IllegalStateException("View ID $id for '${desc.name}' not found.")
}

private class OptionalViewBinding<T : View>(val id: Int) : ReadOnlyProperty<Any, T?> {
private val lazy = Lazy<T?>()

override fun get(thisRef: Any, desc: PropertyMetadata): T? = lazy.get {
findView<T>(thisRef, id)
}
private class OptionalViewBinding<T : View>(val id: Int) : LazyReadOnlyProperty<T?>() {
override fun create(thisRef: Any, desc: PropertyMetadata): T? =
findView(thisRef, id)
}

private class ViewListBinding<T : View>(val ids: IntArray) : ReadOnlyProperty<Any, List<T>> {
private var lazy = Lazy<List<T>>()
private class ViewListBinding<T : View>(val ids: IntArray) : LazyReadOnlyProperty<List<T>>() {
override fun create(thisRef: Any, desc: PropertyMetadata): List<T> =
ids.map { id -> findView<T>(thisRef, id)
?: throw IllegalStateException("View ID $id for '${desc.name}' not found.")
}
}

override fun get(thisRef: Any, desc: PropertyMetadata): List<T> = lazy.get {
ids.map { id -> findView<T>(thisRef, id)
?: throw IllegalStateException("View ID $id for '${desc.name}' not found.")
}
}
private class OptionalViewListBinding<T : View>(val ids: IntArray) : LazyReadOnlyProperty<List<T>>() {
override fun create(thisRef: Any, desc: PropertyMetadata): List<T> =
ids.map { id -> findView<T>(thisRef, id) }.filterNotNull()
}

private class OptionalViewListBinding<T : View>(val ids: IntArray) : ReadOnlyProperty<Any, List<T>> {
private var lazy = Lazy<List<T>>()
private abstract class LazyReadOnlyProperty<T : Any?> : ReadOnlyProperty<Any, T> {
private val lazy: Lazy<T> = Lazy()

override fun get(thisRef: Any, desc: PropertyMetadata): List<T> = lazy.get {
ids.map { id -> findView<T>(thisRef, id) }.filterNotNull()
override fun get(thisRef: Any, desc: PropertyMetadata): T = lazy.get {
LazyRegistry.register(thisRef, lazy)
create(thisRef, desc)
}

protected abstract fun create(thisRef: Any, desc: PropertyMetadata): T
}

private class Lazy<T> {
Expand All @@ -96,4 +103,20 @@ private class Lazy<T> {
[suppress("UNCHECKED_CAST")]
return value as T
}

fun reset() {
value = EMPTY
}
}

private object LazyRegistry {
private val lazyMap = WeakHashMap<Any, MutableCollection<Lazy<*>>>()

fun register(target: Any, lazy: Lazy<*>) {
lazyMap.getOrPut(target, { Collections.newSetFromMap(WeakHashMap()) }).add(lazy)
}

fun reset(target: Any) {
lazyMap.get(target)?.forEach { it.reset() }
}
}