original source : https://stackoverflow.com/a/58663143/3151712

There are two different lifecycles because the Fragment itself lives longer than the Fragment’s view.

There are a number of events that can cause the Fragment’s view to be destroyed, but currently keep the Fragment itself alive:

  1. Putting the Fragment on the back stack (i.e., when you navigate() to another Fragment)
  2. Calling detach() on a Fragment
  3. Calling setMaxLifecycle(Lifecycle.State.CREATED) on a Fragment

In these cases, the Fragment’s view is destroyed, moving the Fragment view lifecycle to DESTROYED. However, the lifecycle of Fragment itself is not destroyed – it generally stays as CREATED. This means that the Fragment can go through multiple cycles of onCreateView() -> onViewCreated() -> onDestroyView() while only going through onCreate() once.

Where this becomes a problem is when it comes to how LiveData works. When you observe a LiveData, LiveData automatically unregisters the observer when the lifecycle reaches DESTROYED. But if you use the Fragment’s lifecycle to observe in onCreateView(), etc., that registered observer will still exist after onDestroyView(), despite the view being destroyed. This means that the second time your Fragment goes through onCreateView(), you’ll actually create a second active Observer, with both running simultaneously. Then three Observers the next time and on and on.

By using the view LifecycleOwner in onCreateView()/onViewCreated(), you ensure that you’ll only have one active Observer running at a time and that Observers tied to previous view instances are correctly destroyed along with the view. Therefore, yes, you should always use getViewLifecycleOwner() as the LifecycleOwner when in onCreateView() or onViewCreated(), including when using Data Binding.

Of course, if you’re registering an Observer in onCreate(), then the view LifecycleOwner does not exist yet (it is created right before onCreateView()) and you don’t have the multiple registration problem, which is why the Lint check specifically does not apply to any registrations done at onCreate() time. In those cases, using the Fragment’s lifecycle itself is absolutely correct.

As per the Fragments: Past, Present, and Future talk, one future improvements for Fragments is going to be combining the two lifecycles together, always destroying the Fragment whenever the Fragment’s view is destroyed. This is not yet available in any shipped version of Fragments, alpha or otherwise.

https://stackoverflow.com/a/57630353/3151712

질문) ViewModelProviders is deprecated So what’s the alternative to create the ViewModel’s object ?

답) 

boardViewModel = ViewModelProvider(this).get(BoardViewModel::class.java)

답)

private val viewModel: EntityGridViewModel by viewModels()

위의 방법으로 사용했지만 constructor가 있는 ViewModel의 경우 위의 구문실행시기가 이른 시기에 해당해서 constructor에 전달될 parameter를 준비하는데 시간이 부족한 경우가 많다. 그래서 다른 방법을 사용하게 되었다. 

https://www.albertgao.xyz/2018/04/13/how-to-add-additional-parameters-to-viewmodel-via-kotlin/

binding.authViewModel = ViewModelProviders.of(
 this,
 viewModelFactory { MyViewModel("albert") }
).get(AuthViewModel::class.java)

참고자료) passing parameters viewmodel

https://stackoverflow.com/a/60130631/3151712

https://www.albertgao.xyz/2018/04/13/how-to-add-additional-parameters-to-viewmodel-via-kotlin/

.

.

.

https://developer.android.com/topic/libraries/architecture/viewmodel

ViewModel 전반적인 사용 방법

.

.

위와 같이 사용하려고 하였으나 자동완성이 되지 않았다. dependency 설정이 잘못되어있었기 때문이다. 

    // ViewModel
//    android docs ref) https://developer.android.com/jetpack/androidx/releases/lifecycle
   def lifecycle_version = "2.2.0"
   def arch_version = "2.1.0"
   implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
   // LiveData
   implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"

//    ref) https://developer.android.com/kotlin/ktx#fragment
   implementation "androidx.fragment:fragment-ktx:1.2.5"

   //ViewModel
//    from codelab ref) https://codelabs.developers.google.com/codelabs/kotlin-android-training-view-model/index.html?index=..%2F..android-kotlin-fundamentals#4
   implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'

가 필요했다. 

https://developer.android.com/topic/libraries/architecture/viewmodel#sharing

https://developer.android.com/kotlin/ktx#fragment

implementation "androidx.fragment:fragment-ktx:1.2.5"

.

.

.

.

주의사항)

https://developer.android.com/topic/libraries/architecture/viewmodel

Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context.

image

.

.

.

.

하나의 activity 아래에 여러개의 fragment가 있고 activity lifecycle에 맞춰서 share view model을 사용하는 방법 

기본적으로 아래 내용은 그대로 이나 약간 문법이 바뀌었다. 

변화된 내용은 https://developer.android.com/topic/libraries/architecture/viewmodel#sharing 에서 확인할수 있다. 변화된 코드는 아래와 같다. 먼저 바로 아래 것을 보고 밑에것을 보면 된다. 

class SharedViewModel : ViewModel() {
   val selected = MutableLiveData<Item>()

   fun select(item: Item) {
       selected.value = item
   }
}

class MasterFragment : Fragment() {

   private lateinit var itemSelector: Selector

   // Use the 'by activityViewModels()' Kotlin property delegate
   // from the fragment-ktx artifact
   private val model: SharedViewModel by activityViewModels()

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
       super.onViewCreated(view, savedInstanceState)
       itemSelector.setOnClickListener { item ->
           // Update the UI
       }
   }
}

class DetailFragment : Fragment() {

   // Use the 'by activityViewModels()' Kotlin property delegate
   // from the fragment-ktx artifact
   private val model: SharedViewModel by activityViewModels()

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
       super.onViewCreated(view, savedInstanceState)
       model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
           // Update the UI
       })
   }
}

.

https://medium.com/mindorks/how-to-communicate-between-fragments-and-activity-using-viewmodel-ca733233a51c

Here comes ViewModel to rescue us from handling a lot of scenario and implementing interface . We only need to create ViewModel class and create instance in fragment but using the activity scope so that it will be available for all the fragment of the activity including activity itself.

Create a ViewModel class

class SharedViewModel:ViewModel(){
   val inputNumber = MutableLiveData<Int>()
}

To emit or pass data from our input fragment create ViewModel in activity scope. To do this we have to pass the activity reference as argument of the ViewModelProvides.of() method. Noe just pass the data to ViewModel object like this

activity?.let {
   sharedViewModel = ViewModelProviders.of(it).get(SharedViewModel::class.java)
}

et_input.addTextChangedListener(object : TextWatcher {
   override fun afterTextChanged(p0: Editable?) {}

   override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}

   override fun onTextChanged(txt: CharSequence?, p1: Int, p2: Int, p3: Int) {
       txt?.let {
           var input = 0
           if (txt.toString().isNotEmpty()) {
               input = txt.toString().toInt()
           }

           sharedViewModel?.inputNumber?.postValue(input)
       }
   }

In the activity we just need to create instance of our ViewModel and observe the required data like this

val sharedViewModel = ViewModelProviders.of(this).get(SharedViewModel::class.java)

sharedViewModel.inputNumber.observe(this, Observer {
   it?.let {
       // do some thing with the number
   }
})

Now what about output fragment? We can do same for the output fragment to observe the data. But keep in mind that we need create the ViewModel instance in activity scope, otherwise android will create a separate instance rather than sharing the same instance and we will not get the data.

For output fragment do it like this

activity?.let {
   val sharedViewModel = ViewModelProviders.of(it).get(SharedViewModel::class.java)

   sharedViewModel.inputNumber.observe(this, Observer {
   it?.let {
           // do some thing with the number
       }
   })
}

That’s it. Source code can be found here.

.

source code

class InputFragment : Fragment() {

    private var sharedViewModel: SharedViewModel? = null

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        activity?.let {
            /**
             *  create view model in activity scope
             */
            sharedViewModel = ViewModelProviders.of(it).get(SharedViewModel::class.java)
        }

        et_input.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(p0: Editable?) {}

            override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}

            override fun onTextChanged(txt: CharSequence?, p1: Int, p2: Int, p3: Int) {
                txt?.let {
                    var input = 0
                    if (txt.toString().isNotEmpty()) {
                        input = txt.toString().toInt()
                    }

                    sharedViewModel?.inputNumber?.postValue(input)
                }
            }
        })
    }

}
package ninja.shuza.sharedviewmodel

import android.arch.lifecycle.Observer
import android.arch.lifecycle.ViewModelProviders
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import kotlin.properties.Delegates

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        supportFragmentManager.beginTransaction().add(R.id.layout_top, InputFragment()).commit()
        supportFragmentManager.beginTransaction().add(R.id.layout_bottom, OutputFragment()).commit()

        val message = resources.getString(R.string.show_input)

        val sharedViewModel = ViewModelProviders.of(this).get(SharedViewModel::class.java)

        sharedViewModel.inputNumber.observe(this, Observer {
            it?.let {
                tv_show_input.text = "$message  $it"
            }
        })
    }
}
package ninja.shuza.sharedviewmodel

import android.arch.lifecycle.Observer
import android.arch.lifecycle.ViewModelProviders
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_output.*

/**
 *
 * :=  created by:  Shuza
 * :=  create date:  09-Aug-2018
 * :=  (C) CopyRight Shuza
 * :=  www.shuza.ninja
 * :=  shuza.sa@gmail.com
 * :=  Fun  :  Coffee  :  Code
 *
 **/
class OutputFragment : Fragment() {

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        /**
         *  create view model in activity scope
         */
        activity?.let {
            val sharedViewModel = ViewModelProviders.of(it).get(SharedViewModel::class.java)

            observeInput(sharedViewModel)
        }
    }

    private fun observeInput(sharedViewModel: SharedViewModel) {
        sharedViewModel.inputNumber.observe(this, Observer {
            it?.let {
                tv_output.text = "2 x $it  =  ${2 * it}"
            }
        })
    }

}

.

.

.

.

share viewmodel with several fragment

original source : https://medium.com/mindorks/how-to-communicate-between-fragments-and-activity-using-viewmodel-ca733233a51c

image

Here comes ViewModel to rescue us from handling a lot of scenario and implementing interface . We only need to create ViewModel class and create instance in fragment but using the activity scope so that it will be available for all the fragment of the activity including activity itself.

Create a ViewModel class

class SharedViewModel:ViewModel(){
   val inputNumber = MutableLiveData<Int>()
}

To emit or pass data from our input fragment create ViewModel in activity scope. To do this we have to pass the activity reference as argument of the ViewModelProvides.of() method. Noe just pass the data to ViewModel object like this

activity?.let {
   sharedViewModel = ViewModelProviders.of(it).get(SharedViewModel::class.java)
}

et_input.addTextChangedListener(object : TextWatcher {
   override fun afterTextChanged(p0: Editable?) {}

   override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}

   override fun onTextChanged(txt: CharSequence?, p1: Int, p2: Int, p3: Int) {
       txt?.let {
           var input = 0
           if (txt.toString().isNotEmpty()) {
               input = txt.toString().toInt()
           }

           sharedViewModel?.inputNumber?.postValue(input)
       }
   }

In the activity we just need to create instance of our ViewModel and observe the required data like this

val sharedViewModel = ViewModelProviders.of(this).get(SharedViewModel::class.java)

sharedViewModel.inputNumber.observe(this, Observer {
   it?.let {
       // do some thing with the number
   }
})

Now what about output fragment? We can do same for the output fragment to observe the data. But keep in mind that we need create the ViewModel instance in activity scope, otherwise android will create a separate instance rather than sharing the same instance and we will not get the data.

For output fragment do it like this

activity?.let {
   val sharedViewModel = ViewModelProviders.of(it).get(SharedViewModel::class.java)

   sharedViewModel.inputNumber.observe(this, Observer {
   it?.let {
           // do some thing with the number
       }
   })
}

That’s it. Source code can be found here.

https://www.baeldung.com/kotlin-sealed-classes
Sealed Classes in Kotlin

.

.

.

.

https://youtu.be/o1Occux4qR8

.

.

.

.

https://youtu.be/o1Occux4qR8./

.

.

.

.

https://www.charlezz.com/?p=43886

.

.

.

.

https://youtu.be/E-yXefhLjNU

https://www.journaldev.com/18688/kotlin-enum-class

가장 좋은 설명

Enumerations in Kotlin are data types that hold a set of constants. Enums are defined by adding the modifier enum in front of a class as shown below. Yes, in Kotlin, Enums are classes.


enum class Months{
    January,
    February,
    March
}

Here are some important points about enum classes in kotlin.

  • Each enum constant is an object. Enum constants are separated by commas.
  • Each of the enum constants acts as separate instances of the class.
  • Enums are useful in enhancing the readability of your code since it assigns pre-defined names to constants.
  • Unlike classes, an instance of enum classes cannot be created using constructors.
  • Hence, we can assert that enum classes are abstract.

Let’s see how the enum class is initialized in the main function of our class.


fun main(args: Array<String>) {

    println(Months.January) //prints January
    println(Months.values().size) //prints 3
    println(Months.valueOf("March")) //prints March

    for (enum in Months.values()) {
        println(enum.name)
    }

    println(Months.valueOf("Mar")) //throws java.lang.IllegalArgumentException: No enum constant Months.Mar
}

Few inferences from the above code:

Let’s see how to tackle this scenario.


fun enumContains(name: String): Boolean {
    return enumValues<Months>().any { it.name == name }
}

fun main(args: Array<String>) {

    if (enumContains("")) {
        println(Months.valueOf(""))
    } else {
        println("The string value does not match with any of the enum constants.") //this gets printed.
    }

}

enumContains is a function that calls the lambda function any which iterates over the enum constants to check if any of them matches the string and returns a boolean.

Enum Properties

Every enum constant has properties: name, ordinal to retrieve the name and position of the constant.


fun main(args: Array<String>) {
    
    println(Months.January.name) //prints January
    println(Months.March.ordinal) // prints 2
    for (enum in Months.values()) {
        println(enum.name)
    }
}

Enum toString()

We can override the toString() function in the enum class as shown below.


enum class Months {
    January,
    February,
    March;

    override fun toString(): String {
        return super.toString().toUpperCase()
    }
}

fun main(args: Array<String>) {
    println(Months.January) //prints JANUARY
    println(Months.January.name) //prints January
     println(Months.valueOf("March")) //prints MARCH
}

Enums are classes. So besides defining the constants we can define other things as well that can be present in a class. To do so we need to first end the enum part with a semi colon.
Note: Month.January invokes the toString() method of the class whereas Month.January.name directly prints the enum constant’s name.

Enum Initialisation

Enum constants can be initialized using primary constructors as shown below.


enum class Months(var shorthand: String) {
    January("JAN"),
    February("FEB"),
    March("MAR");
}

fun main(args: Array<String>) {

    var x = Months.January
    println(x.shorthand) //prints JAN
    x.shorthand = "Shorthand changed to J."
    println(Months.January.shorthand) //prints Shorthand changed to J.
}

Enums as Anonymous classes

Enum constants can behave as anonymous classes be implementing their own functions along with overriding base functions from the class as shown below.


enum class Months(var shorthand: String) {
    January("JAN"){
        override fun printSomething() {
            println("First month of the year.")
        }
    },
    February("FEB"){
        override fun printSomething() {
            println("Second month of the year.")
        }
    },
    March("MAR"){
        override fun printSomething() {
            println("Third month of the year.")
        }
    };
    var a: String = "Hello, Kotlin Enums"
    abstract fun printSomething()
}

fun main(args: Array<String>) {
    Months.February.printSomething() //prints Second month of the year.
    println(Months.February.a) //prints Hello, Kotlin Enums
}

Each of the enum constants must override

Enum inside an Enum

It’s possible to define another enum class in the current enum class as shown below.


enum class Months {
    January,
    February,
    March;

    enum class Day{
        Monday,
        Tuesday,
        Wednesday
    }
}

fun main(args: Array<String>) {
    println(Months.Day.Tuesday) //prints Tuesday
}

Only an enum class can invoke another one. The inner class Day cannot be invoked from the enum constants.

Enums and when

when is Kotlin equivalent of switch in Java. We’ve covered the workings of it in the Control Flow tutorial.
An example showing how when statements are used with enums is given below.


enum class Months {
    January,
    February,
    March;
}

fun main(args: Array<String>) {

    var m = Months.February

    when (m) {
        Months.January  -> print("Matches with Jan")
        Months.February -> print("Matches with Feb") //prints this.
        Months.March -> print("Matches with Mar")
    }
}

This brings an end to kotlin enum tutorial.

References: Kotlin Docs

.

.

.

.

https://youtu.be/or_HJ6NGLxc
kotlin enum basic 설명

image
image

.

.

.

.

https://youtu.be/jc9AZfZs7kY

enum class

image
image
image

.

image
image

.

image
image

.

.

.

.

https://www.baeldung.com/kotlin-enum
Working with Enums in Kotlin

.

.

.

.

내가 정리한 enum class

  • child class는 parent constructor 를 그대로 구현해야 한다. 
  • parent constructor가 없으면 child에도 constructor는 없다.
  • parent class안에 abstract로 정의된 func은 모든 child 가 구현해야 한다.
  • parent class안에 abstract로 정의되지 않은 함수를 child안에 새로 만들수 있다.
  • child class는 각각 , 로 구분한다. child class정의 마지막은 ;를 사용한다. 
  • parent class는 abstract method, abstract property를 가질수 있다. (sealed class도 같다)


sealed class의 경우 parent constructor가 정의되어있지 않더라도 child class 는 constructor를 가질수 있다. 각 class 정의 사이에 구분자는 없다. (정의부분 뒤에 enum의 경우 , 가 붙는데 sealed class의 경우는 없다는 이야기)

sealed class BasicGarment2(){
   class CLEAN_PRESS(var name:String, var price:BigDecimal): BasicGarment2()
   class CLEAN_ONLY(): BasicGarment2()
}

 

enum class BasicGarment(val na: String, val price:BigDecimal){
   CLEAN_PRESS("a",10.toBigDecimal()){
       override fun calculatePrice(): BigDecimal {
           return 10.toBigDecimal()
       }

       fun foo(){
           println("test func")
       }

       override var testVar: String
           get() = TODO("Not yet implemented")
           set(value) {}
   },
   CLEAN_ONLY("nam", 10.toBigDecimal()){
       override fun calculatePrice(): BigDecimal {
           return 10.toBigDecimal()
       }

       override var testVar: String
           get() = TODO("Not yet implemented")
           set(value) {}
   };
   abstract fun calculatePrice():BigDecimal
   abstract var testVar :String
}

.

.

.

.

https://www.geeksforgeeks.org/enum-classes-in-kotlin/

original source : https://medium.com/incwell-innovations/passing-data-in-android-navigation-architecture-component-part-2-5f1ebc466935

navigation에서 fragment간 이동시 data를 전달하는 2가지 방법 


1. Type Unsafe Way

app_nav.xml 

<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/app_nav"
   app:startDestination="@id/nav_main">
   <fragment
       android:id="@+id/nav_main"
       android:name="com.smartmobe.navigation.MainFragment"
       android:label="Main"
       tools:layout="@layout/fragment_main" >
       <action
           android:id="@+id/action_nav_main_to_nav_dashboard"
           app:destination="@id/nav_dashboard" />
   </fragment>
   <fragment
       android:id="@+id/nav_dashboard"
       android:name="com.smartmobe.navigation.DashboardFragment"
       android:label="Dashboard"
       tools:layout="@layout/fragment_dashboard" >
       <argument
           android:name="username"
           app:type="string" />
   </fragment>
</navigation>

To use the bundleOf() is the extension method of androidx. For that we need the dependency:

implementation 'androidx.core:core-ktx:1.0.0-alpha3'

Receive data in destination fragment as :

.

.

.

2. Type Safe Way

https://developer.android.com/training/data-storage/room/accessing-data

@Dao
interface MyDao {
   @Insert(onConflict = OnConflictStrategy.REPLACE)
   fun insertUsers(vararg users: User)

   @Insert
   fun insertBothUsers(user1: User, user2: User)

   @Insert
   fun insertUsersAndFriends(user: User, friends: List<User>)
}

.

update

@Dao
interface MyDao {
   @Update
   fun updateUsers(vararg users: User)
}

.

delete

@Dao
interface MyDao {
   @Delete
   fun deleteUsers(vararg users: User)
}

.

query

@Dao
interface MyDao {
   @Query("SELECT * FROM user")
   fun loadAllUsers(): Array<User>
}
@Dao
interface MyDao {
   @Query("SELECT * FROM user WHERE age > :minAge")
   fun loadAllUsersOlderThan(minAge: Int): Array<User>
}
@Dao
interface MyDao {
   @Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
   fun loadAllUsersBetweenAges(minAge: Int, maxAge: Int): Array<User>

   @Query("SELECT * FROM user WHERE first_name LIKE :search " +
          "OR last_name LIKE :search")
   fun findUserWithName(search: String): List<User>
}

.

더 많은 내용이 있는데 web page 참조