Home > OS >  How to apply changes on current fragment when opening new fragment in Kotlin?
How to apply changes on current fragment when opening new fragment in Kotlin?

Time:10-19

I have managed to switch between fragment, but the changes on current fragment doesn't applied when entering a new fragment. I have 2 fragments, HomeFragment and Subreddit. When I first run the app, it shows HomeFragment which is showing r/aww content. If I click action bar, it will change into Subreddit fragment. In Subreddit fragment, I can click a subreddit category (e.g. r/sports, r/news, etc.). After I click a subreddit category, the fragment change to HomeFragment again where it should shows the content of subreddit category I just choose, but it doesn't. I choose r/news while in Subreddit fragment, but it still showing the first subreddit category when I run my app, which is r/aww.

Here is my MainActivity.kt

class MainActivity : AppCompatActivity() {
    companion object {
        var globalDebug = false
        lateinit var jsonAww100: String
        lateinit var subreddit1: String
        private const val mainFragTag = "mainFragTag"
        private const val favoritesFragTag = "favoritesFragTag"
        private const val subredditsFragTag = "subredditsFragTag"
    }
    private var actionBarBinding: ActionBarBinding? = null
    private val viewModel: MainViewModel by viewModels()

    fun hideKeyboard() {
        val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
        imm.hideSoftInputFromWindow(window.decorView.rootView.windowToken, 0)
    }

    private fun initActionBar(actionBar: ActionBar) {
        actionBar.setDisplayShowTitleEnabled(false)
        actionBar.setDisplayShowCustomEnabled(true)
        actionBarBinding = ActionBarBinding.inflate(layoutInflater)
        actionBar.customView = actionBarBinding?.root
    }

    private fun actionBarTitleLaunchSubreddit()  {
        actionBarBinding?.actionTitle?.setOnClickListener {
            actionBarBinding?.actionTitle?.text = "Pick"
            supportFragmentManager.commit {
                add(R.id.main_frame, Subreddits.newInstance(), subredditsFragTag)
                setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
            }
        }
    }

    private fun addHomeFragment() {
        supportFragmentManager.commit {
            add(R.id.main_frame, HomeFragment.newInstance(), mainFragTag)
            setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
        }
    }

    private fun initDebug() {
        if(globalDebug) {
            assets.list("")?.forEach {
                Log.d(javaClass.simpleName, "Asset file: $it" )
            }
            jsonAww100 = assets.open("aww.hot.1.100.json.transformed.txt").bufferedReader().use {
                it.readText()
            }
            subreddit1 = assets.open("subreddits.1.json.txt").bufferedReader().use {
                it.readText()
            }
        }
    }
    private fun initTitleObservers() {
        viewModel.observeTitle().observe(this){
            actionBarBinding?.actionTitle?.text = it
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)
        setSupportActionBar(activityMainBinding.toolbar)
        supportActionBar?.let{
            initActionBar(it)
        }

        addMenuProvider(object : MenuProvider {
            override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
                menuInflater.inflate(R.menu.menu_main, menu)
            }

            override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
                return when (menuItem.itemId) {
                    android.R.id.home -> false
                    else -> true
                }
            }
        })

        addHomeFragment()
        initDebug()
        initTitleObservers()
        actionBarTitleLaunchSubreddit()
        actionBarLaunchFavorites()
        actionBarSearch()
        viewModel.setTitleToSubreddit()
    }
}

Here is my MainViewModel.kt

class MainViewModel : ViewModel() {
    private var title = MutableLiveData<String>()
    private var api = RedditApi.create()
    private var repository = RedditPostRepository(api)
    private val data = MutableLiveData<List<RedditPost>>()
    private val subred = MutableLiveData<List<RedditPost>>()  
    private var subreddit = "aww"
    fun netFetchData() = viewModelScope.launch(
        context = viewModelScope.coroutineContext
                  Dispatchers.IO) {
        data.postValue(repository.getPosts(subreddit.value.toString()))
    }
    fun observeData(): LiveData<List<RedditPost>> {
        return data
    }
    fun updateNewCategory(updateTitle: String){
        subreddit = updateTitle
    }
    fun observeNewCategory(): String {
        return subreddit
    }
    fun netFetchSubreddit() = viewModelScope.launch(
        context = viewModelScope.coroutineContext
                  Dispatchers.IO) {
        subred.postValue(repository.getSubreddits())
    }
    fun observeSubreddit(): LiveData<List<RedditPost>> {
        return subred
    }  
    fun setTitleToSubreddit() {
        title.value = "r/${subreddit.value}"
    }    
    fun observeTitle(): LiveData<String> {
        return title
    }
    fun setTitle(newTitle: String) {
        title.value = newTitle
    }
}

Here is my SubredditListAdapter.kt

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
    val binding = RowSubredditBinding.inflate(LayoutInflater.from(parent.context),
        parent, false)
    val holder = VH(binding)
    holder.rowSubredditBinding.root.setOnClickListener {
        viewModel.setTitle(getItem(holder.adapterPosition).displayName.toString())
        viewModel.updateNewCategory(getItem(holder.adapterPosition).displayName.toString())
        fragmentActivity.supportFragmentManager.commit{
            add(R.id.main_frame, HomeFragment.newInstance(), "mainFragTag")
            setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
        }
    }
    return holder
}

Here is my HomeFragment.kt:

class HomeFragment: Fragment() {
    private val viewModel: MainViewModel by viewModels()
    private var _binding: FragmentRvBinding? = null
    private val binding get() = _binding!!

    companion object {
        fun newInstance(): HomeFragment {
            return HomeFragment()
        }
    }

    private fun initAdapter(binding: FragmentRvBinding) : PostRowAdapter {
        val rv = binding.recyclerView
        val adapter = PostRowAdapter(viewModel)
        rv.adapter = adapter
        rv.layoutManager = LinearLayoutManager(binding.root.context)

        viewModel.netFetchData(viewModel.observeNewCategory())
        viewModel.observeData().observe(viewLifecycleOwner) {
            adapter.submitList(it)
        }

        return adapter
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentRvBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        Log.d(javaClass.simpleName, "onViewCreated")

        initAdapter(binding)
    }
}

I thought by using updateNewCategory and observeNewCategory would change the subreddit category, but turns out it didn't. How to update the category and apply the changes?

CodePudding user response:

What I am understanding with your code snippet is that your are trying to use common MainViewModel to communicate among your fragments and Activity.

Your issue is that you are creating new instance of MainViewModel in your fragment by using private val viewModel: MainViewModel by viewModels() instead of using activityViewModel

Use following line to get viewModel reference of your activity in your fragment

private val viewModel: MainViewModel by activityViewModels()
  • Related