-
-
Save bolot/7a6db460abf701d2526be287ea0eabaf to your computer and use it in GitHub Desktop.
Lifecycle Aware Lazy and Find View
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:orientation="vertical" | |
tools:context="com.bignerdranch.android.lazyforlifecycle.MainActivity"> | |
<TextView | |
android:id="@+id/questionTextView" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:textAppearance="@style/TextAppearance.AppCompat.Medium" | |
tools:text="Civil Rights Leader"/> | |
<TextView | |
android:id="@+id/nameTextView" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:textAppearance="@style/TextAppearance.AppCompat.Medium" | |
tools:text="Martin Luther King Jr."/> | |
<TextView | |
android:id="@+id/cityTextView" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:textAppearance="@style/TextAppearance.AppCompat.Medium" | |
tools:text="Atlanta"/> | |
<TextView | |
android:id="@+id/stateTextView" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:textAppearance="@style/TextAppearance.AppCompat.Medium" | |
tools:text="Georgia"/> | |
<Button | |
android:id="@+id/showAnswerButton" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:text="Show answer"/> | |
</LinearLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.bignerdranch.android.kotleta.ext | |
import android.app.Activity | |
import android.arch.lifecycle.GenericLifecycleObserver | |
import android.arch.lifecycle.Lifecycle | |
import android.arch.lifecycle.LifecycleOwner | |
import android.support.v4.app.Fragment | |
import android.view.View | |
import kotlin.reflect.KProperty | |
/** | |
* Represents a value with lifecycleAwareLazy initialization. | |
* | |
* To create an instance of [LifecycleAwareLazy] use the [lifecycleAwareLazy] function. | |
*/ | |
public interface LifecycleAwareLazy<out T> { | |
/** | |
* Gets the lazily initialized value of the current LifecycleAwareLazy instance. | |
* Once the value was initialized it must not change during the rest of lifetime of this LifecycleAwareLazy instance. | |
*/ | |
public val value: T | |
/** | |
* Returns `true` if a value for this LifecycleAwareLazy instance has been already initialized, and `false` otherwise. | |
* Once this function has returned `true` it stays `true` for the rest of lifetime of this LifecycleAwareLazy instance. | |
*/ | |
public fun isInitialized(): Boolean | |
} | |
/** | |
* Lifecycle aware | |
*/ | |
public fun <T> lifecycleAwareLazy(lifecycle: Lifecycle, initializer: () -> T): LifecycleAwareLazy<T> | |
= LifecycleAwareLazyImpl(lifecycle, initializer) | |
public fun <T, U> findViewLazy(fragment: U, viewId: Int): LifecycleAwareLazy<T> | |
where T: View, U: Fragment, U: LifecycleOwner { | |
return lifecycleAwareLazy(fragment.lifecycle) { | |
fragment.view!!.findViewById<T>(viewId) | |
} | |
} | |
public fun <T, U> findViewLazy(activity: U, viewId: Int): LifecycleAwareLazy<T> | |
where T: View, U: Activity, U: LifecycleOwner { | |
return lifecycleAwareLazy(activity.lifecycle) { | |
activity.findViewById<T>(viewId) | |
} | |
} | |
/** | |
* An extension to delegate a read-only property of type [T] to an instance of [LifecycleAwareLazy]. | |
* | |
* This extension allows to use instances of LifecycleAwareLazy for property delegation: | |
* `val property: TextView by lifecycleAwareLazy(lifecycle) { initializer }` | |
*/ | |
public inline operator fun <T> LifecycleAwareLazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value | |
private object UNINITIALIZED_VALUE | |
private class LifecycleAwareLazyImpl<out T>(lifecycle: Lifecycle, initializer: () -> T) | |
: LifecycleAwareLazy<T>, GenericLifecycleObserver { | |
override fun getReceiver() = this | |
override fun onStateChanged(source: LifecycleOwner?, event: Lifecycle.Event?) { | |
when(event) { | |
Lifecycle.Event.ON_STOP -> _value = UNINITIALIZED_VALUE | |
// Lifecycle.Event.ON_DESTROY -> initializer = null | |
else -> return | |
} | |
} | |
init { | |
lifecycle.addObserver(this) | |
} | |
private var initializer: (() -> T)? = initializer | |
private var _value: Any? = UNINITIALIZED_VALUE | |
override val value: T | |
get() { | |
if (_value === UNINITIALIZED_VALUE) { | |
_value = initializer!!() | |
} | |
@Suppress("UNCHECKED_CAST") | |
return _value as T | |
} | |
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE | |
override fun toString(): String = if (isInitialized()) value.toString() else "LifecycleAwareLazy value not initialized yet." | |
} | |
public interface LifecycleAwareFindView<out T: View> { | |
/** | |
* Gets the lazily initialized value of the current LifecycleAwareLazy instance. | |
* Once the value was initialized it must not change during the rest of lifetime of this LifecycleAwareLazy instance. | |
*/ | |
public val value: T | |
/** | |
* Returns `true` if a value for this LifecycleAwareLazy instance has been already initialized, and `false` otherwise. | |
* Once this function has returned `true` it stays `true` for the rest of lifetime of this LifecycleAwareLazy instance. | |
*/ | |
public fun isInitialized(): Boolean | |
} | |
public fun <T, U> findView(fragment: U, viewId: Int): LifecycleAwareFindView<T> | |
where T: View, U: Fragment, U: LifecycleOwner = FindFragmentViewImpl(fragment, viewId) | |
private class FindFragmentViewImpl<out T, out U>(fragment: U, val viewId: Int) | |
: LifecycleAwareFindView<T>, GenericLifecycleObserver where T: View, U: Fragment, U: LifecycleOwner { | |
override fun getReceiver() = this | |
override fun onStateChanged(source: LifecycleOwner?, event: Lifecycle.Event?) { | |
when(event) { | |
Lifecycle.Event.ON_STOP -> _value = UNINITIALIZED_VALUE | |
Lifecycle.Event.ON_DESTROY -> if (fragment?.retainInstance == false) fragment = null | |
else -> return | |
} | |
} | |
init { | |
fragment.lifecycle.addObserver(this) | |
} | |
private var fragment: U? = fragment | |
private var _value: Any? = UNINITIALIZED_VALUE | |
override val value: T | |
get() { | |
if (_value === UNINITIALIZED_VALUE) { | |
_value = fragment!!.view!!.findViewById(viewId) | |
} | |
@Suppress("UNCHECKED_CAST") | |
return _value as T | |
} | |
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE | |
override fun toString(): String = if (isInitialized()) value.toString() else "LifecycleAwareLazy value not initialized yet." | |
} | |
/** | |
* An extension to delegate a read-only property of type [T] to an instance of [LifecycleAwareLazy]. | |
* | |
* This extension allows to use instances of LifecycleAwareLazy for property delegation: | |
* `val property: TextView by lifecycleAwareLazy(lifecycle) { initializer }` | |
*/ | |
public inline operator fun <T: View> LifecycleAwareFindView<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.bignerdranch.android.lazyforlifecycle | |
import android.arch.lifecycle.LifecycleRegistry | |
import android.arch.lifecycle.LifecycleRegistryOwner | |
import android.os.Bundle | |
import android.support.v4.app.Fragment | |
import android.view.LayoutInflater | |
import android.view.View | |
import android.view.ViewGroup | |
import android.widget.Button | |
import android.widget.TextView | |
import com.bignerdranch.android.kotleta.ext.findView | |
import com.bignerdranch.android.kotleta.ext.getValue | |
import com.bignerdranch.android.kotleta.ext.lifecycleAwareLazy | |
class MainFragment: Fragment(), LifecycleRegistryOwner { | |
val registry = LifecycleRegistry(this) | |
override fun getLifecycle() = registry | |
var showAnswerButton: Button? = null | |
lateinit var questionTextView: TextView | |
val nameTextView by lazy { view!!.findViewById<TextView>(R.id.nameTextView) } | |
val cityTextView by lifecycleAwareLazy(lifecycle) { view!!.findViewById<TextView>(R.id.cityTextView) } | |
val stateTextView: TextView by findView(this, R.id.stateTextView) | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
retainInstance = true | |
} | |
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { | |
val view = layoutInflater.inflate(R.layout.fragment_main, container, false) | |
showAnswerButton = view.findViewById(R.id.showAnswerButton) | |
questionTextView = view.findViewById(R.id.questionTextView) | |
questionTextView.text = getString(R.string.question1) | |
showAnswerButton?.setOnClickListener { | |
nameTextView.setText(R.string.answer1_name) | |
stateTextView.setText(R.string.answer1_state) | |
cityTextView.setText(R.string.answer1_city) | |
} | |
return view | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment