I am in an activity fragment whereby i want to display a toast widget after the commands for the submit button have been met and completed.
the code:
class HomeFragment : Fragment() {
private val currentUserDocRef = Firebase.firestore.collection("users")
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_home, container, false)
view.apply {
submitbutton.setOnClickListener {
FirestoreUtil.updateCurrentUser(
edittextPersonname.text.toString(),
editTextBio.text.toString(),
editTextTextEmailAddress.text.toString(),
edittextage.text.toString()
)
}
return view
}
}
no error is present in my code however on trying to declare a toast widget i get an error. code:
Toast.makeText(this#HomeFragment, "saving", Toast.LENGTH_SHORT).show()
the error:
You need a context to show toast, here is the code :
Toast.makeText(this#HomeFragment.requireActivity(), "saving", Toast.LENGTH_SHORT).show()
Thanks
The context should not be nullable type. The error shows that it is a type mismatch.
Option 1:
Toast.makeText(context!!, "saving", Toast.LENGTH_SHORT).show()
The !! (not-null assertion operator) is used to denote the variable is not null.
Option 2:
Using let and safe calls
context?.let{ context->
Toast.makeText(context, "saving", Toast.LENGTH_SHORT).show()
}
Refer: https://kotlinlang.org/docs/reference/null-safety.html for more details
Related
I know there is a lateinit or lazy keyword in Kotlin to prevent indiscriminate initialization and thus minimize wasted resources.
I wanted to use the lazy keyword to use findViewById when necessary events occur.
However, if I use the lazy keyword, nothing happens. It doesn't even cause an error.
Conversely, when findViewId is normally used in onCreateView, click event occurs normally.
Why doesn't lazy work?
class BodyPartDialogFragment : DialogFragment(), View.OnClickListener{
private val ll: LinearLayout? by lazy { view?.findViewById(R.id.ll_body_part) }
// private lateinit var button: Button
private val button: Button? by lazy { view?.findViewById(R.id.start) }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view: View = inflater.inflate(R.layout.fragment_body_part_dialog, container, false)
// ll = view.findViewById(R.id.ll_body_part)
// button = view.findViewById(R.id.start)
ll?.apply { clipToOutline = true }
button?.setOnClickListener { // Nothing Happened
Toast.makeText(context, "Noting Selected", Toast.LENGTH_SHORT).show()
}
return view
}
getView() that is behind the view property returns whatever you returned from onCreateView(). When you access view inside onCreateView(), it hasn't yet returned anything and hence a null is returned, and your ?. safecall becomes a no-op.
You can use a lazy approach like this after onCreateView(), such as in onViewCreated().
It looks like you may be initializing things in the wrong order.
Consider that renaming a local variable always preserves semantics, so let's modify your code a little:
private val ll: LinearLayout? by lazy { view?.findViewById(R.id.ll_body_part) }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val someRandomView: View = inflater.inflate(R.layout.fragment_body_part_dialog, container, false)
ll?.apply { clipToOutline = true }
button?.setOnClickListener { // Nothing Happened
Toast.makeText(context, "Noting Selected", Toast.LENGTH_SHORT).show()
}
return someRandomView
}
Do you see the issue? ll is being initialized with a view that hasn't been assigned yet in onCreateView.
view (or really getView()) is the view that is returned from onCreateView(). You're trying to access that before you have returned from onCreateView() so it returns null, and your lazy value is then also null. You can make this work by accessing it later, ie. in onViewCreated()
class BodyPartDialogFragment : DialogFragment(), View.OnClickListener{
private val ll: LinearLayout? by lazy { view?.findViewById(R.id.ll_body_part) }
// private lateinit var button: Button
private val button: Button? by lazy { view?.findViewById(R.id.start) }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view: View = inflater.inflate(R.layout.fragment_body_part_dialog, container, false)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
ll?.apply { clipToOutline = true }
button?.setOnClickListener { // Nothing Happened
Toast.makeText(context, "Noting Selected", Toast.LENGTH_SHORT).show()
}
}
}
This is more clear if you use requireView() since it returns a non-null View and rather throws an exception, so your app would have crashed with the error message did not return a View from onCreateView() or this was called before onCreateView().
You can do this to get access to View in the future using 'by lazy'
private val previewImage by lazy { requireActivity().findViewById<ImageView>(R.id.ivImage) }
Then you can use it like
previewImage.setImageURI(imageUri)
I have a DialogFragment which name is Dialog. How can I invoke it from a function which is out of activity and fragment in Android?
This is the DialogFragment:
class Dialog: DialogFragment() {
private var array = arrayOf("Yes", "No")
var a = ""
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView = inflater.inflate(R.layout.activity_main, container)
val myListView = rootView.findViewById(R.id.listview_1) as ListView
myListView.adapter = ArrayAdapter(context!!, R.layout.list_item, array)
myListView.setItemChecked(0,true)
val okbutton = rootView.findViewById<Button>(R.id.ok)
var cancelbutton = rootView.findViewById<Button>(R.id.cancel)
var title = rootView.findViewById<TextView>(R.id.title)
title.text="Choose one option"
cancelbutton.setOnClickListener { dismiss() }
okbutton.setOnClickListener {
Toast.makeText(context, "OK", Toast.LENGTH_LONG).show()
}
myListView.setOnItemClickListener { adapterView,
view,
position,
l
->
Toast.makeText(context, "${array[position]}", Toast.LENGTH_LONG).show()
}
return rootView
}
}
And this is function which I want to invoke Dialog from there:
private val fm = supportFragmentManager
fun TestFunction() {
Dialog().show(fm, "")
}
But supportFragmentManager is in red and is not recognized in the function.
supportFragmentManager is variable in Activity or fragment class so you can not use without being in Activity class or fragment
if you want to test it use Espresso for test UI
if you use it in the business layer it will be against clean code so please make interface contain show function that implemented in Activity or fragment in business class that invoked when logic has done
if you insist to make it business so please pass a context and cast it to your Activity class
At present, I use Code B to display a message in a Fragment class, it works well.
I hope to use Code A to do it, so I write Code C, but Code C is wrong, how can I fix Code C ? Thanks!
Code A
import org.jetbrains.anko.*
class UIFragmentCamera : Fragment() {
private fun updateCameraUi() {
toast("Hello")
}
}
Code B
import org.jetbrains.anko.*
class UIFragmentCamera : Fragment() {
private fun updateCameraUi() {
requireContext().toast("Hello")
}
}
Code C
fun Fragment.toast(info:String) {
requireActivity().toast(info)
}
Make sure you imported androidx.fragment.app.Fragment or android.app.Fragment in the extension function defined file
requireActivity returns FragmentActivity whereas requireContext returns Context. I believe your toast is another extension function which display message based on the type Context
Code C
fun Context.toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
fun Fragment.toast(info: String) {
requireContext().toast(info)
}
Option - 1: If you want to create extension function of Fragment class you have to do it in this way
fun Fragment.toast(message: String) {
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
}
And from inside fragment you can call this like below:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//Here it is
toast("Hello")
return super.onCreateView(inflater, container, savedInstanceState)
}
Option - 2: You can create extension function for Context class like this
fun Context.toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
Then you can call this from Fragment like below:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//Here it is, both are valid
requireActivity().toast("Hello")
requireContext().toast("World")
return super.onCreateView(inflater, container, savedInstanceState)
}
You can also call this from Activity like below:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
toast("Hello World")
}
import android.widget.Toast
import androidx.fragment.app.Fragment
fun Fragment.toast(message :String ){
Toast.makeText(requireContext(),message,Toast.LENGTH_SHORT).show()
}
In my application i want show message when fragment has show.
I used viewPager and BottomNavBar for show 4 fragments!
I want when click on BottomNavBar items show fragment and i want when visibility fragment show message.
I write below codes :
class HomeRegisteredFragment : Fragment() {
lateinit var toolbarTile: TextView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_home_registered, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//Initialize
activity?.let {
toolbarTile = it.findViewById(R.id.homePage_toolbarTitle)
}
//Set title
toolbarTile.text = resources.getString(R.string.registered)
context?.let { ContextCompat.getColor(it, R.color.blue_active) }?.let {
toolbarTile.setTextColor(it)
}
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
if (isVisibleToUser) {
Log.e("showFragLog", "Show")
context?.let { Toast.makeText(it, "Show", Toast.LENGTH_SHORT).show() }
}
}
}
In my above codes, when click on my BottomNavBar for show fragment, show me Log message but not show Toast message.
When click on another BottomNavBar items and again click on previous BottomNavBar item, then show Toast message.
I think in first time not initialize context in setUserVisibleHint method.
How can i initialize context for show Toast in every time?
I changed your codes with below codes :
class HomeRegisteredFragment : Fragment() {
lateinit var toolbarTile: TextView
lateinit var handler: Handler
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_home_registered, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//Initialize
activity?.let {
toolbarTile = it.findViewById(R.id.homePage_toolbarTitle)
}
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
if (isVisibleToUser) {
//Initialize
handler = Handler()
//Set delay
handler.postDelayed({
Toast.makeText(requireContext(),"Show",Toast.LENGTH_SHORT).show()
}, 10)
}
}
}
First you should use requireContext() instead of context() for avoid from memory leak.
For show Toast for every time, you can initialize handler in setUserVisibleHint , then after some delay run your code!
I hope help you
Storing context in a variable is a horrible practive and most of the times leads to memory leaks, use requireContext() this method was introduced in Support Library 27.1.0. Nowdays most likely you will have a newer version or even using androidx so there is no excuse for storing a context
If you are looking for application context to show the toast message, try the below way and see if it works. Also, initialize it onCreate method so you have the activity context at that point.
val appContext = context!!.applicationContext
O have a similar trouble here. I have one Activity with multiple Fragments, and I need a ListView to show some employes.
But when I call the Adapter class, I don't know how to pass the context variable:
binding.listviewCoordenacoes.isClickable = true
binding.listviewCoordenacoes.adapter = CoordenadorAdapter(requireContext().applicationContext as Activity, arrayListCoordenador)
binding.listviewCoordenacoes.setOnClickListener{}
In the examples in general, it works in Activities. If not possible, I will create an Activity and put it in that.
So I have the following ImageButton in a fragment:
<ImageButton
android:id="#+id/moneyBtn"
style="#android:style/Widget.Holo.Light.ImageButton"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="40dp"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:src="#drawable/monkey"
android:background="#null"/>
And the following fragmentActivity.kt
class Home : Fragment() {
override public fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view: View? = inflater.inflate(R.layout.fragment_home, container, false)
val moneyButton: ImageButton = view?.findViewById(R.id.moneyBtn) as ImageButton
val result = MyAppApplication()
var money = result.money
moneyButton.setOnClickListener(View.OnClickListener {
Toast.makeText(activity, "TESTING BUTTON CLICK 1", Toast.LENGTH_SHORT).show()
})
return view
}
I also tried to use the "normal" Kotline setOnClickListener
moneyButton.setOnClickListener {
Toast.makeText(activity, "TESTING BUTTON CLICK 1", Toast.LENGTH_SHORT).show()
}
The App dosent crash and dosent freeze, it just dont work
I also tried to replace the Toast with a throw, but that wont be exceuted either.
Maybe you can find my mistake?
Try initializing your click listener in onActivityCreated. It's called after onCreateView so it'll ensure that view is inflated.
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val moneyButton: ImageButton = activity.findViewById(R.id.moneyBtn) as ImageButton
moneyButton.setOnClickListener {
Toast.makeText(activity, "TESTING BUTTON CLICK 1", Toast.LENGTH_SHORT).show()
}
}
Had the same problem and it can be done like following codes:
In your Fragment:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val rootView = inflater.inflate(R.layout.fragment_home, container, false)
val imgViewButton = rootView.findViewById<ImageButton>(R.id.moneyBtn) // Use the current view
imgViewButton?.setOnClickListener() {
Toast.makeText(activity, "Clicked!", Toast.LENGTH_SHORT).show()
}
return rootView
}
The reason is the View which we weren't get it in the right way. But, with these codes, it sets the current View and then after all, returns the view which works perfectly.
Strange, I just tried your code snippets on my platform, and the Toast pops up just fine... Try changing the first arg of Toast to view.context instead so it'd look something like:
Toast.makeText(view.context, "TESTING BUTTON CLICK 1", Toast.LENGTH_SHORT).show()
Let me know if that makes a difference.
First of all, do you even know that this code is executed? That the listener is set? Maybe try to use the Log class (should be the same in Kotlin as in Java).Also,
val result = MyAppApplication()
var money = result.money
looks suspicious to me. Are you trying to create a new Application instance?