Using safeargs I get "required Bundle found Bundle?" - android

I am passing arguments using safeargs. In the reciving fragment I am getting a compile error: 'Required Bundle Found Bundle?'. Cannot see where the error has crept in.
Googled around, checked text and udacity tutorial
Where error appears (at 'arguments')
package com.example.android.naveditoryoutube
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_two_fragment.*
class FragmentTwo : Fragment() {
companion object {
fun newInstance() = FragmentTwo()
}
private lateinit var viewModel: FragmentTwoViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_two_fragment, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(FragmentTwoViewModel::class.java)
}
override fun onStart() {
super.onStart()
var args = FragmentTwoArgs.fromBundle(arguments)
argText.text = args.calculated_Number
}
}
Sending code:
package com.example.android.naveditoryoutube
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 android.widget.Button
import androidx.navigation.Navigation
import kotlinx.android.synthetic.main.fragment_one_fragment.*
class FragmentOne : Fragment() {
companion object {
fun newInstance() = FragmentOne()
}
private lateinit var viewModel: FragmentOneViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_one_fragment, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(FragmentOneViewModel::class.java)
var calculated_Number : String = viewModel.sendNewNumber().toString()
button_calculate.setOnClickListener{view: View ->
if (number_box.text.isNotEmpty()){
var number_entered: String = number_box.text.toString()
viewModel.findNewNumber((number_entered))
calculated_Number = viewModel.sendNewNumber()
Navigation.findNavController(view).navigate(FragmentOneDirections.actionFragmentOneToFragmentTwo(calculated_Number))
}
}
}
}
Error appears here
var args = FragmentTwoArgs.fromBundle(arguments)
arguments is expected to be Bundle but is Bundle?

I had to change it to use !!
var args = FragmentTwoArgs.fromBundle(arguments!!)

use requireArguments() instead of arguments!!

try using requireArguments() instead of arguments
var args = FragmentTwoArgs.fromBundle(requireArguments())

You shouldn't use FragmentTwoArgs.fromBundle(arguments)
just use:
val args: FragmentTwoArgsby navArgs()
And in your method call:
val amount = args.amount
It's work for me, see official guide:
https://developer.android.com/topic/libraries/architecture/navigation/navigation-pass-data

Instead of
var args = FragmentTwoArgs.fromBundle(arguments)
argText.text = args.calculated_Number
you can use
arguments?.let {
val args = FragmentTwoArgs.fromBundle(it)
argText.text = args.calculated_Number
}

Related

setOnClickListener in Fragment with Kotlin

I am working on a sample code and I want a Button showing a message but it didn't work. App will open but buttonn didn't work
This app should show a Toast but it does not. I tried lateinit var firstComend : Button but didn't work too
I should sat will converting Activity to Fragment
Please help me
package com.mysfk.android.frogments
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.mysfk.android.R
import android.graphics.Color
import android.telephony.SmsManager
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
class OrderFragment : Fragment() {
private var param1: String? = null
private var param2: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val let = arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
var firstComend = view?.findViewById<Button>(R.id.firstButton)
firstComend?.setOnClickListener {
Toast.makeText(context, "ارسال شد", Toast.LENGTH_SHORT).show()
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_order, container, false)
}
companion object {
#JvmStatic
fun newInstance(param1: String, param2: String) =
MessageFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
Try to set your click listener inside the onCreateView, like this:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_order, container, false)
var firstComend = view?.findViewById<Button>(R.id.firstButton)
firstComend?.setOnClickListener {
Toast.makeText(context, "Test", Toast.LENGTH_SHORT).show()
}
}
All view related decision must be done in onCreateView or onViewCreated
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_order, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?){
var firstComend = view?.findViewById<Button>(R.id.firstButton)
firstComend?.setOnClickListener {
Toast.makeText(context, "Test", Toast.LENGTH_SHORT).show()
}
}

Why am I getting " kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized " when binding is initialized?

I'm new to kotlin and android development.
I'm currently learning fragment and how to create it, receive data from one fragment and display it on another through the activity that connects them.
What I have done:
I created two fragment
I called them in the main activity dynamically
I used viewBinding
I created an interface called fragmentAactionListener in the SendingTextFragment and it has a method that will be defined in the main activity but called from the SendingTextFragment
What I was hoping to achieve:
When someone type a name in the editView and click the send button, it will call the method defined in the main activity and it will pass it to the DisplayFragment.
But when I type a name and click the button but the application class and say kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized but initialized it.
The error message:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.fragmenttest, PID: 5187
kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized
at com.example.fragmenttest.DisplayFragment.recieveName(DisplayFragment.kt:31)
at com.example.fragmenttest.MainActivity.onButtonClick(MainActivity.kt:37)
at com.example.fragmenttest.SendingTestFragment.onButtonClicked(SendingTestFragment.kt:52)
at com.example.fragmenttest.SendingTestFragment.onViewCreated$lambda-0(SendingTestFragment.kt:46)
at com.example.fragmenttest.SendingTestFragment.lambda$g_0sTs_brBZ0HIBdlZHmgd8vecI(Unknown Source:0)
at com.example.fragmenttest.-$$Lambda$SendingTestFragment$g_0sTs_brBZ0HIBdlZHmgd8vecI.onClick(Unknown Source:2)
at android.view.View.performClick(View.java:7448)
at android.view.View.performClickInternal(View.java:7425)
at android.view.View.access$3600(View.java:810)
at android.view.View$PerformClick.run(View.java:28305)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
My code:
The MainActivity
package com.example.fragmenttest
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.example.fragmenttest.databinding.ActivityMainBinding
class MainActivity : FragmentActivity(), SendingTextFragment.FragmentActionListener {
private lateinit var binding : ActivityMainBinding
private var displayFragment : DisplayFragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
var sendingTestFragment = SendingTextFragment()
displayFragment = DisplayFragment()
addFragment(sendingTestFragment)
}
// add fragment
fun addFragment(fragment: Fragment){
// get the fragment manager and start fragment transaction
var fTransaction = supportFragmentManager.beginTransaction()
fTransaction.replace(R.id.FLFragment, fragment)
fTransaction.addToBackStack(null)
fTransaction.commit()
}
override fun onButtonClick(name: String) {
displayFragment!!.recieveName(name)
addFragment(displayFragment!!)
}
}
The SendingFragment
package com.example.fragmenttest
import android.content.Context
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.example.fragmenttest.databinding.FragmentSendingTestBinding
import java.text.SimpleDateFormat
class SendingTextFragment : Fragment() {
private lateinit var binding : FragmentSendingTestBinding
private var activityContext : SendingTextFragment.FragmentActionListener? = null
private var simpleDateFormat: SimpleDateFormat? = null
interface FragmentActionListener{
fun onButtonClick(name : String)
}
override fun onAttach(context: Context) {
super.onAttach(context)
activityContext = context as FragmentActionListener
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = FragmentSendingTestBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
simpleDateFormat = SimpleDateFormat("HH:mm:ss")
binding.time.text = simpleDateFormat?.format(System.currentTimeMillis())
binding.button.setOnClickListener {
onButtonClicked(it)
}
}
private fun onButtonClicked(view: View){
if (activityContext != null){
activityContext?.onButtonClick(binding.name.text.toString())
}
}
}
The Display
package com.example.fragmenttest
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.example.fragmenttest.databinding.FragmentDisplayBinding
class DisplayFragment : Fragment() {
private lateinit var binding : FragmentDisplayBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = FragmentDisplayBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
fun recieveName(name : String){
binding.name.text = name
}
}

Kotlin: update fragment in viewPager

I am considering a simple example consists of the following component:
MainActivity: has editText, button, viewPager
FragmentA: has nothing
FragmentB: has a textView
So, in the screen, you can swipe to see each fragment. I want my example to have the function: if one writes a text in the editText and click the button then it shows up in Fragment B.
A tried it by using notifyDatsetChanged() but it does not work. Could anyone help? Below is the whole code of mine:
MainActivity.kt
package com.myproject.chapterfivesectionone
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.myproject.chapterfivesectionone.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
lateinit var fragmentB: FragmentB
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
val fragmentList = listOf(FragmentA(), FragmentB())
val adapter = FragmentAdapter(this)
adapter.fragmentList = fragmentList
binding.viewPager.adapter = adapter
binding.buttonSend.setOnClickListener {
var bundle = Bundle()
bundle.putString("key1", binding.editTextWriteSomething.text.toString())
fragmentB.arguments = bundle
adapter.notifyDataSetChanged()
}
}
}
FragmentAdapter.kt
package com.myproject.chapterfivesectionone
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
class FragmentAdapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {
var fragmentList = listOf<Fragment>()
override fun getItemCount(): Int {
return fragmentList.size
}
override fun createFragment(position: Int): Fragment {
return fragmentList.get(position)
}
}
FragmentA.kt
package com.myproject.chapterfivesectionone
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
class FragmentA : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_a, container, false)
}
}
FragmentB.kt
package com.myproject.chapterfivesectionone
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.myproject.chapterfivesectionone.databinding.FragmentBBinding
class FragmentB : Fragment() {
lateinit var binding: FragmentBBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentBBinding.inflate(inflater, container, false)
if (arguments?.getString("key1") != null) {
binding.textViewInFragmentB.text = arguments?.getString("key1")
}
return binding.root
}
}
Currently, fragmentB is inside fragmentList so you can retrieve it like fragmentList[fragmentBPosition]. Then defind a function inside FragmentB for update the TextView
binding.buttonSend.setOnClickListener {
(fragmentList[1] as FragmentB).updateTheTextView("new")
// if you want code more clean, can replace hard code value by a constant or you can write some logic to find FragmentB from fragmentList
}
FragmentB
class FragmentB : Fragment() {
fun updateTheTextView(newText : String) {
binding.textViewInFragmentB.text = newText
}
}

How to pass a Mutable list from fragment to another fragment?

I'm new to Kotlin and Fragments, actually in my App i have two fragments, Fragment1 and Fragment2
In the first fragment i have a method which get data from some editTexts and add them to a MutableList then in my Fragment2 i have a ListView in which i would be able to show that MutableList...
But how can i pass the MutableList from Fragment1 to Fragment2?
Here is my Fragment1 code:
private var listArticoli: MutableList<Articolo>? = null
private fun addBarcode(barcode: String, qta: String) {
if (barcode.isEmpty()) {
txtBarcode.requestFocus()
return;
}
listArticoli?.add(Articolo(barcode, qta.toInt()))
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
txtBarcode = view.findViewById(R.id.txtBarcode)
listArticoli = mutableListOf()
btnArticoli.setOnClickListener {
findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment) // button on click of which i navigate to Fragment2
}
}
Fragment2 code:
class SecondFragment : Fragment() {
lateinit var listView: ListView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_second, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
listView = view.findViewById(R.id.listView)
view.findViewById<Button>(R.id.btnIndietro).setOnClickListener {
findNavController().navigate(R.id.action_SecondFragment_to_FirstFragment)
}
}
}
1.Share the viewmodel with their common parent activity
2.FragmentResultListener from fragment(after 1.3.0-alpha04)
To access Mutable list in both the fragments,you can create ViewModel which consists of MutableLiveDate of you required data type.Then expose MutableLiveData from viewmodel.So use the same ViewModel in both the fragments by setting data from one fragment1 and by observing data from fragment2.
SampleViewModel.kt
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class SampleViewModel: ViewModel() {
var mutableLiveData:MutableLiveData<MutableList<Articolo> = MutableLiveData()
}
Fragment1.kt
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
class Fragment1 : Fragment() {
private lateinit var viewModel: SampleViewModel
var arrayList: ArrayList<Articolo> = ArrayList()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(requireActivity()).get(SampleViewModel::class.java)
//TODO: Add data to arraylist
viewModel.mutableLiveData.value = arrayList
}
}
Fragment2.kt
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
class Fragment2 : Fragment() {
private lateinit var viewModel: SampleViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(requireActivity()).get(SampleViewModel::class.java)
viewModel.mutableLiveData.observe(viewLifecycleOwner, Observer {
//TODO:Can do you requirements here
})
}
}

Automated Java to Kotlin conversion: unresolved reference to 'ARG_LAYOUT_RES_ID'

I'm using AppIntro in my Kotlin app, and need to customize its layout. The tutorial tells us to use this code as starting point. Because that's written in Java, I converted it to Kotlin (not 100% automated though, because Kotlin doesn't support 'static' keyword):
import android.support.v4.app.Fragment
import android.os.Bundle
import android.support.annotation.Nullable
import android.view.ViewGroup
import android.view.LayoutInflater
import android.view.View
class IntroFragment: Fragment() {
private val ARG_LAYOUT_RES_ID = "layoutResId"
private var layoutResId: Int = 0
companion object {
fun newInstance(layoutResId: Int): IntroFragment{
val args: Bundle = Bundle()
args.putSerializable(ARG_LAYOUT_RES_ID, layoutResId)
val fragment = IntroFragment()
fragment.arguments = args
return fragment
}
}
override fun onCreate(#Nullable savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (arguments != null && arguments!!.containsKey(ARG_LAYOUT_RES_ID)) {
layoutResId = arguments!!.getInt(ARG_LAYOUT_RES_ID)
}
}
#Nullable
override fun onCreateView(inflater: LayoutInflater, #Nullable container: ViewGroup?,
#Nullable savedInstanceState: Bundle?): View? {
return inflater.inflate(layoutResId, container, false)
}
}
At this part:
args.putSerializable(ARG_LAYOUT_RES_ID, layoutResId)
Android Studio complains:
Unresolved reference: ARG_LAYOUT_RES_ID
How to solve this?
It is because ARG_LAYOUT_RES_ID is an instance variable while your newInstance method is a class (static) method.
If you move ARG_LAYOUT_RES_ID into the companion object, it will work. Like this:
import android.support.v4.app.Fragment
import android.os.Bundle
import android.support.annotation.Nullable
import android.view.ViewGroup
import android.view.LayoutInflater
import android.view.View
class IntroFragment: Fragment() {
private var layoutResId: Int = 0
companion object {
private const val ARG_LAYOUT_RES_ID = "layoutResId"
fun newInstance(layoutResId: Int): IntroFragment {
val args: Bundle = Bundle()
args.putSerializable(ARG_LAYOUT_RES_ID, layoutResId)
val fragment = IntroFragment()
fragment.arguments = args
return fragment
}
}
override fun onCreate(#Nullable savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (arguments != null && arguments!!.containsKey(ARG_LAYOUT_RES_ID)) {
layoutResId = arguments!!.getInt(ARG_LAYOUT_RES_ID)
}
}
#Nullable
override fun onCreateView(inflater: LayoutInflater,
#Nullable container: ViewGroup?,
#Nullable savedInstanceState: Bundle?): View? {
return inflater.inflate(layoutResId, container, false)
}
}
You could also make ARG_LAYOUT_RES_ID a const like i've shown above.
Move the field to your companion object:
companion object {
private val ARG_LAYOUT_RES_ID = "layoutResId"
fun newInstance(layoutResId: Int): IntroFragment{
val args: Bundle = Bundle()
args.putSerializable(ARG_LAYOUT_RES_ID, layoutResId)
val fragment = IntroFragment()
fragment.arguments = args
return fragment
}
}
The variables and functions inside the companion object block are basically static (if you were to draw parallels from Java).
The ARG_LAYOUT_RES_ID variable is not inside the companion object scope, so it can't be accessed statically.
Move the ARG_LAYOUT_RES_ID variable inside the companion object scope, and all will be good again.

Categories

Resources