kotterknife
kotterknife copied to clipboard
Bind view in retained Fragment
@JakeWharton I was wondering if you tried to handle binding views in retained Fragment
s?
I will explain the problem to everyone else:
When you set your fragment retained calling setRetainInstance(true)
view properties once created are kept in memory after fragment layout is re-created e.g. screen orientation changes. It means that after orientation change properties points to the view from the old invalid layout reference.
With java ButterKnife you have to call inject()
in respective fragment method callback. In case this library injection is done automatically whenever view is used for the first time.
I don't use fragments, but I understand the requirement. ButterKnife has reset(Object)
for this purpose.
ButterKnife works great but KotterKnife does not have such functionality. So is there a chance that you will try to take care of that issue? If not, I will try myself but I am still learning Kotlin so it may take some time :)
@JakeWharton, could you please merge my pull request that fixes this issue?
Wouldn't be better to have a single object that manages all the lazy values in the fragment that is updated in onCreateView() in order to invalidate all the cached values? I will write and submit a pull request in the future
I have the same issue with Fragments. Also happens when setRetainInstance(true)
was not set.
Let's say a Fragment is detached and then reattached. The same Fragment instance will be reused however it will go through onCreateView()
and onViewCreated()
again. So the Views are new instances but the properties still point to the old View instances.
private val textView: TextView by bindView(R.id.text)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) =
inflater.inflate(R.layout.fragment_main, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Ooops, won't work when the Fragment is reattached
// because an old instance is accessed
textView.setText("This is a test")
}
So by this logic every use of lazy property in activity or fragment is doomed to fail? Is it safe to do something like this:
val category: String by lazy { getIntent().getStringExtra(EXTRA_CATEGORY) }
So by this logic every use of lazy property in activity or fragment is doomed to fail?
Well, if it's a value that might change and needs to be recalculated on every use of the Activity/Fragment instance I would say yes, lazy
cannot be used then.
I'm also having this issue today with my FragmentPagerAdapter. After navigating far enough away from a fragment in a ViewPager, the FragmentPagerAdapter will destroy the view of that Fragment, but the Fragment itself and everything to which it holds a reference will remain in memory. Upon recreation of the Fragment's view, the view bindings are not reset.
If anyone has come up with a good workaround for this I'd appreciate your input :+1:
EDIT: Nevermind, I just merged the PR from @MichaelRocks into my project, and it works perfectly. Thanks!
Another option is to have methods like
@Suppress("UNCHECKED_CAST")
fun <V: View> SupportFragment.bindViewNoCache(@IdRes id: Int): ReadOnlyProperty<SupportFragment, V> {
return object: ReadOnlyProperty<SupportFragment, V> {
override fun getValue(thisRef: SupportFragment, property: KProperty<*>): V {
return thisRef.view!!.findViewById(id) as V
}
}
}
val myView: TextView by bindViewNoCache(R.id.my_view_id)
I realize this is the same as
val myView get() = view!!.findViewById(R.id.my_view_id) as TextView
but it is slightly shorter and allows consistent access syntax with bindView