Not able to use binding with navcontroller - android

Here is the code :
class FirstFragment : Fragment() {
private var _binding: FragmentFirstBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentFirstBinding.inflate(inflater, container, false)
binding.btnOpen.setOnClickListener {
Navigation.findNavController(view).navigate(R.id.secondFragment)
}
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
At line: Navigation.findNavController(view).navigate(R.id.secondFragment) I'm getting error as Type mismatch required view found view?
My question is why we can't combine navcontroller with view binding?
And is there any resources to learn restrictions of using view binding

Instead of view use binding.root
Like this:
Navigation.findNavController(binding.root).navigate(R.id.secondFragment)
binding.root is reference to root view.

Related

Android Studio: Set Text in fragment

I've recently picked up an Android Studio project I started a year ago in Kotlin.
It features three fragments that can be navigated through by a bottom navigation bar.
Now, to break my current issue down to a simple example that even doesn't work for me:
Given there's a the editText object exercise in fragment_home.xml and I want to call and alter it in HomeFragment.kt.
I checked every source of advice I could find from Google & Stackoverflow and came up with the following code in HomeFragment.kt (partially pre-coded by AndroidStudio):
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val homeViewModel =
ViewModelProvider(this).get(HomeViewModel::class.java)
_binding = FragmentHomeBinding.inflate(inflater, container, false)
val root: View = binding.root
val view: View = inflater!!.inflate(R.layout.fragment_home,container,false)
view.exercise.setText("This is an exceptionally hardcoded string")
The last line stands for every object I tried to reach. I also tried onClickListening for buttons like so:
val btnNewExercise = view.findViewById<Button>(R.id.btn_new_exercise)
btnNewExercise.setOnClickListener {view
Toast.makeText(view.context, "New exercise will be generated", Toast.LENGTH_SHORT).show()
println("Generated a new exercise")
}
but nothing happens when I start the app/ hit the buttons - I seem to can't get through to the actual view's objects to access them. Even ran into NullPointerExceptions on my way to a solution.
I could supply the fragment and layout files if needed - just thought this way it might be easier at first.
If anybody could tell me where I'm wrong I'd be really grateful! Thanks in advance!
You inflated the layout twice.
Remove this. You already inflated the layout using view binding in the FragmentHomeBinding.inflate... call
val view: View = inflater!!.inflate(R.layout.fragment_home,container,false)
and replace
view.exercise.setText("This is an exceptionally hardcoded string")
with (using binding
binding.exercise.setText("This is an exceptionally hardcoded string")
then the last line on your onCreateView should be
return binding.root
Note: You should have these class properties:
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
So it will look like this:
//move your view model as a class property so it will be accessible by other class methods
private val homeViewModel =
ViewModelProvider(this).get(HomeViewModel::class.java)
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!//transform to immutable
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
//use the immutable view binding property
binding.exercise.setText("This is an exceptionally hardcoded string")
return binding.root
}
For more info, read view binding
Try this solution.
class HomeFragment : Fragment() {
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val homeViewModel =
ViewModelProvider(this).get(HomeViewModel::class.java)
_binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.exercise.text="This is an exceptionally hardcoded string"
binding.btnNewExercise.setOnClickListener {
Toast.makeText(view.context, "New exercise will be generated", Toast.LENGTH_SHORT).show()
println("Generated a new exercise")
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
You need to return the view you made in the OnCreateView .

What is a view model and how can I use it to replace findViewById with View Binding and

I am working on a basic bluetooth app in android studio and I am having trouble with view binding. So far I have this in my MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
...
Later in the onCreate I have this line
val btnONOFF = findViewById<View>(R.id.btnONOFF) as Button
and I would like to replace the findViewById using viewbinding but I'm not sure what to replace that line with.
The android developers website says to use something like this
binding.name.text = viewModel.name
but I'm also not sure what a viewModel is.
Here is my ViewFragment.kt incase that is helpful
class ViewFragment : Fragment() {
private var _binding: ActivityMainBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): RelativeLayout {
_binding = ActivityMainBinding.inflate(inflater, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}}
Any help is appreciated, thanks!
You can find the tutorial of viewmodel from here.
ViewFragment.kt
class ViewFragment : Fragment() {
private var _binding: ActivityMainBinding? = null
private val binding get() = _binding!!
private val viewModel by viewModel<ActivityViewModel>()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): RelativeLayout {
_binding = ActivityMainBinding.inflate(inflater, container, false)
setupView()
return binding.root
}
private fun setupView(){
binding.text = viewModel.name
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
You can customise your ActivityViewModel.kt
class ActivityViewModel : ViewModel() {
val name = "Hello"
}
Note: by viewModel is koin injection.

Android : Acces parent fragment function from child fragment

I have a Parent Fragment in an activity and have a Child Fragment in Parent Fragment. The NavGraphs of the two Fragments are different. I want to access the function in the Parent Fragment from the Child Fragment, but the application crashes. How can I do it?
Child Fragment:
class ProfilinMenu : Fragment(){
private var _binding : FragmentProfilinMenuBinding? = null
private val profilinmenubinding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View {
_binding = FragmentProfilinMenuBinding.inflate(inflater,container,false)
(parentFragment as Parent).changeName() // this is for access parent
return profilinmenubinding.root
}}
Parent Fragment:
public class Parent : Fragment() {
private var _binding : FragmentParentBinding? = null
private val parentBinding get() = _binding!!
private var instance: Parent? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentParentBinding.inflate(inflater, container, false)
val view = parentBinding.root
return view
}
public fun changeName(){
parentBinding.profilinIsmin.text = "Merhaba"
}
Try this..
NavHostFragment navHostFragment = (NavHostFragment) getParentFragment();
ParentFragment parent = (ParentFragment) navHostFragment.getParentFragment();
parent.changeName()

Where should I place viewDataBinding.lifecycleOwner = this.viewLifecycleOwner with Data Binding and Lifecycle?

The following code is from the project architecture-samples, you can see it here.
I'm not sure where I should place viewDataBinding.lifecycleOwner = this.viewLifecycleOwner between onCreateView() and onActivityCreated(), could you tell me?
class TasksFragment : Fragment() {
private lateinit var viewDataBinding: TasksFragBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
viewDataBinding = TasksFragBinding.inflate(inflater, container, false).apply {
viewmodel = viewModel
}
setHasOptionsMenu(true)
//viewDataBinding.lifecycleOwner = this.viewLifecycleOwner Can I place here?
return viewDataBinding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
// Set the lifecycle owner to the lifecycle of the view
viewDataBinding.lifecycleOwner = this.viewLifecycleOwner
}
..
}
onActivityCreated is deprecated. You should use onViewCreated or onCreateView.
private var binding: TasksFragBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
setHasOptionsMenu(true)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val viewDataBinding = TasksFragBinding.inflate(inflater, container, false).apply {
viewmodel = viewModel
lifecycleOwner = viewLifecycleOwner
}
this.binding = viewDataBinding
return viewDataBinding.root
}
override fun onDestroyView() {
super.onDestroyView()
binding = null
}

How to generify data binding layout inflation?

I wanna make a BaseFragment. For this, I have to use ViewDataBinding and ViewModel. using generic, I can use variable but not static field. For example I have to Inflate writing this code "FragmentSecondBinding.inflate(layoutInflater, container, false) ". So I tried this code "T.inflate(layoutInflater, container, false)" but got some error. Also ViewModel is like this.
How can I make this code to BaseCode?
abstract class BaseFragment<T: ViewDataBinding, M : ViewModel> : DaggerFragment(){
abstract val layoutId : T
private lateinit var binding : T
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private val viewModel by viewModels<M> { viewModelFactory }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = T.inflate(inflater, container, false).apply {
viewmodel = viewModel
}
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
binding.lifecycleOwner = this.viewLifecycleOwner
}
There is a way to abstract out the particular ViewDataBinding, however, it would require to provide a layout resource reference for each concrete fragment implementation:
protected abstract val layoutResource: Int
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private val viewModel by viewModels<M> { viewModelFactory }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater, layoutResource, container, false).apply {
viewmodel = viewModel
}
return binding.root
}

Categories

Resources