I am reading this from Google about using DialogFragment. Everything works until the section Creating a Custom Layout: when the call is done to display a DialogFragment with a custom layout I get a OutOfMemoryError exception.
I have just slighty modified the code from the article, and my app only contains the 3 elements below:
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
fab.setOnClickListener { view ->
showDialog()
}
}
fun showDialog() {
// Create an instance of the dialog fragment and show it
val dialog = FireMissilesDialogFragment()
dialog.show(supportFragmentManager, "NoticeDialogFragment")
}
}
FireMissilesDialogFragment.kt
class FireMissilesDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
val builder = AlertDialog.Builder(it)
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
builder.setView(layoutInflater.inflate(R.layout.dialog_layout, null))
// Add action buttons
.setPositiveButton(R.string.ok) { dialog, id ->
Log.d("FireMissiles", "TEST")
}
.setNegativeButton(R.string.cancel) { dialog, id ->
getDialog().cancel()
}
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
}
dialog_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<EditText
android:id="#+id/username"
android:inputType="textEmailAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="4dp"
android:hint="#string/ok" android:importantForAutofill="no"/>
<EditText
android:id="#+id/password"
android:inputType="textPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="16dp"
android:fontFamily="sans-serif"
android:hint="#string/cancel" android:importantForAutofill="no"/>
</LinearLayout>
My code has only these 3 elements above. I guess there is somewhere a memory leak but I cannot find it.
Does someone have an idea?
Answers to comments and solution:
#Sam I do not use any image, this project is just a attempt to use DialogFragment,
hence it is based on standard empty Activity project with fab, only thing I did not show is the activity_main.xml but it is the standard one.
#Tobias, thanks for the hint but it did not solve the problem :(
#user8159708, thank you!! that solved the problem. My code is now (and it is the only thing I changed):
class FireMissilesDialogFragment : DialogFragment() {
lateinit var mView: View
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
mView = inflater.inflate(R.layout.dialog_layout, container, false)
setDialog()
return mView
}
fun setDialog(){
activity?.let {
val builder = AlertDialog.Builder(it)
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
builder.setView(mView)
// Add action buttons
.setPositiveButton(R.string.ok) { dialog, id ->
Log.d("FireMissiles", "TEST")
}
.setNegativeButton(R.string.cancel) { dialog, id ->
Log.d("FireMissiles", "TEST")
//getDialog().cancel()
}
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
}
Don't create your dialog using an alert builder.
Remove your override of onCreateDialog.
Inflate your view in onCreateView:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.dialog_layout, container, false)
}
Add some buttons to your layout yourself, and set your onClickListeners manually after inflating your view.
This sounds like loop.
Try to remove getDialog().cancel() and just return null instead.
You don't need to explicitely close Dialoges.
I think you are doing it wrong. You don't need to use AlertDialog.Builder as you are already extending from DialogFragment.
You can follow this link.
It is in Java, but in Kotlin it will be similar.
Related
I have a simple android application, In my application, there is one image icon, and when I click the icon, I want to show context menu. My code is debugging but when I click the image button, nothing change, so context menu not work, I do not know where is the problem, Any idea will be appreciated.
MenuFragment:
class MenuFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_menu, container, false)
popupMenu()
}
private fun popupMenu() {
val popupMenu = PopupMenu(requireContext(), menu)
popupMenu.inflate(R.menu.menu)
popupMenu.setOnMenuItemClickListener {
when(it.itemId) {
R.id.menu_one -> {
Toast.makeText(requireContext(), "menu1", Toast.LENGTH_SHORT).show()
true
}
R.id.menu_two -> {
Toast.makeText(requireContext(), "menu2", Toast.LENGTH_SHORT).show()
true
}
else -> true
}
}
menu.setOnLongClickListener {
try {
val popup = PopupMenu::class.java.getDeclaredField("mPopup")
popup.isAccessible = true
val menu = popup.get(popupMenu)
menu.javaClass
.getDeclaredMethod("setForceShowIcon",Boolean::class.java)
.invoke(menu, true)
}catch(e: Exception) {
e.printStackTrace()
}finally {
popupMenu.show()
}
true
}
}
}
fragment_menu:
<ImageView
android:id="#+id/menu"
android:layout_width="wrap_content"
android:layout_height="16dp"
android:src="#drawable/ic_menu"
android:text="5:33 PM"
android:textSize="12sp"
/>
menu:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/menu_one"
android:title="Menu1"
android:icon="#drawable/ic_menu_one"
/>
<item
android:id="#+id/menu_two"
android:title="Menu2"
android:icon="#drawable/ic_menu_two"
/>
It seems you want to show the menu as soon as possible, since the function invocation is after the return statement then it never happen.
You have to leverage the fragment lifecycle. Override onViewCreated and call it there
override... onViewCreated(...) {
super....
popUpMenu()
}
The method onViewCreated happens immediately after the view is created. If you need it when the view is ready to interact onStart. In both cases, make sure backward navigation doesn't show it again by mistake.
Before I begin, I have read similarly worded posts like this one, but they are not working for me.
As I explain in the title, I get an NPE when I click the positive button ("ok" in this case). If anybody can point out what I am doing incorrectly that'll be great! Below is a abstracted version of my setup
MainFragment.kt
class MainFragment: DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?) =
AlertDialog.Builder(context!!)
.setView(FragmentMainBinding.inflate(LayoutInflater.from(context)).root)
.setTitle(R.string.title_main)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok) { _, _ -> onPositiveButtonTapped() }
.create()
private fun onPositiveButtonTapped() {
val g = arrayListOf(ground_nbr_edit_text.text.toString()) // NullPointerException
// ...
}
}
fragment_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="#+id/ground_nbr_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>
DialogFragment calls onCreateView() after onCreateDialog() and then uses that view for the dialog's content, replacing whatever you had already set in onCreateDialog(). So move your view creation into onCreateView():
class MainFragment: DialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?) =
FragmentMainBinding.inflate(inflater).root
override fun onCreateDialog(savedInstanceState: Bundle?) =
AlertDialog.Builder(context!!)
.setTitle(R.string.title_main)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok) { _, _ -> onPositiveButtonTapped() }
.create()
private fun onPositiveButtonTapped() {
val g = arrayListOf(ground_nbr_edit_text.text.toString()) // NullPointerException
// ...
}
}
0. Problem
This question by gave me the idea to implement the Data Binding Library for the purpose of opening a Link in a Webview-Fragment on click of a Button in a Bottom Sheet Fragment.
I was able to implement the Data Binding as seen in the other Question (Link), but the WebView doesn't load the new URL when a Button is clicked in said Bottom Sheet Fragment. I get the feedback from the console that the Button was clicked and the LiveData was changed though.
So, I thought the WebView does reload automatically when the LiveData changes but that doesn't seem to be the case...so I do not see my Error and not sure if I implemented everything correctly.
Hopefully someone can help me.
1. Respective Classes
1.1. WebViewFragment
class WebviewFragment : Fragment() {
private lateinit var webView: WebView
companion object {
fun newInstance() = WebviewFragment()
}
private lateinit var viewModel: WebViewViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding: FragmentMainWebviewBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_main_webview, container, false)
viewModel = ViewModelProvider(this).get(WebViewViewModel::class.java)
binding.webViewModel = viewModel
binding.lifecycleOwner = this
return binding.root
}
#SuppressLint("SetJavaScriptEnabled", "JavascriptInterface")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
webView = webViewMain
webView.settings.javaScriptEnabled = true
}
}
1.2. WebViewModel
class WebViewViewModel : ViewModel() {
val webViewUrl = MutableLiveData<String>().apply{ value = "file:///android_asset/html_files/gallery_page.html" }
companion object WebViewUrlLoader {
#BindingAdapter("loadUrl")
#JvmStatic
fun WebView.setUrl(url: String) {
this.loadUrl(url)
}
}
}
1.3. WebView Layout (XML)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="webViewModel"
type="com.example.ui.main.webview.WebViewViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/main_screen_webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainFragmentGalleryView"
>
<WebView
android:id="#+id/webViewMain"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:loadUrl="#{webViewModel.webViewUrl}"
android:paddingBottom="52dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
1.4. BottomSheet Fragment
class BottomSheetFragment : BottomSheetDialogFragment() {
private var fragmentView: View? = null
private lateinit var viewModel: WebViewViewModel
companion object {
fun newInstance() = BottomSheetFragment()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
fragmentView = inflater.inflate(R.layout.view_modal_bottom_sheet, container, false)
return fragmentView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(WebViewViewModel::class.java)
initView()
}
override fun getTheme(): Int {
return R.style.Theme_NoWiredStrapInNavigationBar
}
private val mBottomSheetBehaviorCallback: BottomSheetCallback = object : BottomSheetCallback() {
var isBottomSheetUp = false
override fun onSlide(bottomSheet: View, slideOffset: Float) {
//TODO("Not yet implemented")
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
isBottomSheetUp = false
dismiss()
} else isBottomSheetUp = true
}
}
override fun setupDialog(dialog: Dialog, style: Int) {
//super.setupDialog(dialog, style)
val contentView =
View.inflate(context,
R.layout.view_modal_bottom_sheet, null)
dialog.setContentView(contentView)
val layoutParams =
(contentView.parent as View).layoutParams as CoordinatorLayout.LayoutParams
val behavior = layoutParams.behavior
if (behavior != null && behavior is BottomSheetBehavior<*>) {
behavior.setBottomSheetCallback(mBottomSheetBehaviorCallback)
}
}
private fun initView() {
action_my_pictures.setOnClickListener {
viewModel.webViewUrl.value = "https://www.google.com/"
Log.d("BottomSheet", "Button 1 Clicked ${viewModel.webViewUrl.value}")
}
action_favorites.setOnClickListener {
viewModel.webViewUrl.value = "https://www.hotmail.de/"
Log.d("BottomSheet", "Button 2 Clicked ${viewModel.webViewUrl.value}")
}
action_ranking.setOnClickListener {
viewModel.webViewUrl.value = "https://amazon.com/"
Log.d("BottomSheet", "Button 3 Clicked ${viewModel.webViewUrl.value}")
}
action_hall_of_fame.setOnClickListener {
viewModel.webViewUrl.value = "https://m.daum.net/"
Log.d("BottomSheet", "Button 4 Clicked ${viewModel.webViewUrl.value}")
}
action_liked_pictures.setOnClickListener {
viewModel.webViewUrl.value = "https://m.nate.com/"
Log.d("BottomSheet", "Button 5 Clicked ${viewModel.webViewUrl.value}")
}
action_events.setOnClickListener {
viewModel.webViewUrl.value = "https://www.danawa.com/"
Log.d("BottomSheet", "Button 6 Clicked ${viewModel.webViewUrl.value}")
}
action_close_bottom_sheet.setOnClickListener {
dismiss()
}
}
}
1.5. BottomSheet Layout (XML)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="webViewModel"
type="com.example.ui.main.webview.WebViewViewModel" />
</data>
<LinearLayout
android:id="#+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/bottomNavigationViewBackground"
android:orientation="vertical"
app:behavior_hideable="true"
app:behavior_peekHeight="auto"
app:layout_behavior="#string/bottom_sheet_behavior"
app:behavior_fitToContents="true">
[7 Image/Icons as Buttons]
Example:
<LinearLayout
android:id="#+id/action_my_pictures"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:orientation="vertical"
android:clickable="true"
android:focusable="true">
<ImageView
android:id="#+id/icon_my_pictures"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_gravity="center_horizontal"
android:src="#drawable/ic_user_color" />
<TextView
android:id="#+id/icon_my_pictures_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:singleLine="true"
android:text="My Gallery"
android:paddingTop="6dp"
android:textAlignment="center"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</layout>
2. LOG output (for Button Clicks)
D/BottomSheet: Button 1 Clicked https://www.google.com/
D/BottomSheet: Button 2 Clicked https://www.hotmail.de/
D/BottomSheet: Button 3 Clicked https://amazon.com/
D/BottomSheet: Button 4 Clicked https://m.daum.net/
D/BottomSheet: Button 4 Clicked https://m.daum.net/
D/BottomSheet: Button 5 Clicked https://m.nate.com/
D/BottomSheet: Button 6 Clicked https://www.danawa.com/
A lot of thanks in advance.
I think problem is in scope of life that you provide for viewModel.
Currently you code looks like this:
viewModel = ViewModelProvider(this).get(WebViewViewModel::class.java)
This code is invoked inside two different fragments, and because ViewModelProvider is initialized with current instance of fragment (this). Your view model is only available in this scope (fragment scope). So at the end for each fragment you will get new ViewModel. To share ViewModel between fragments you should use different approach:
Instantiate ViewModelProvider with activity. (ViewModel will be share across all fragments inside activity)
Instantiate ViewModelProvider with parentFragment. (ViewModel will be shared across all child fragments)
sample:
viewModel = ViewModelProvider(requireActivity()).get(WebViewViewModel::class.java)
viewModel = ViewModelProvider(parentFragment).get(WebViewViewModel::class.java)
please also check:
How to scope ViewModels properly?
https://developer.android.com/reference/android/arch/lifecycle/ViewModelProviders
I have a TabLayout with 3 fragments inside. Fragment 0 contains details, 1 is a description and 2 is location.
But no matter how I try to call the fragment Ids inside my carDetailsActivity or I try to pass the value from the activity to the fragments it keeps calling on null and giving the following result:
Tab Fragment
Now I tried multiple ways one of them to Bundle the text value from the activity to the fragment but I still got null "IllegaleStateException". (which was the best option to my knowledge) but it still didnt work.
This is how I want it to look (the design is in arabic):
Here is my Activity where im calling them at the moment but I just get empty strings (had to give them ? because the app kept crashing if I didnt):
class CarDetailsActivity : BaseActivity() {
val TAG = "CarDetailsActivity"
override fun onCreate(savedInstanceState: Bundle?) {
val car = intent.extras!!.getParcelable<Car>(DETAILS_TRANSFER) as Car
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_car_details)
activateToolbar(true)
carPriceDetails?.text = car.price.toString()
carDate?.text = car.date
carCategory?.text = car.category
carModel?.text = car.brandModel
carYear?.text = car.modelYear
carKilometer?.text = car.kilometer.toString()
carGear?.text = car.gearType
carFuel?.text = car.fuelType
val fragmentAdapter = FragmentCarInfo(supportFragmentManager)
viewPager.adapter = fragmentAdapter
tabLayout.setupWithViewPager(viewPager)
carBrand.text = car.brand
carModel.text = car.brandModel
carYear.text = car.modelYear
Picasso.with(this).load(car.image)
.error(R.drawable.ic_cars)
.placeholder(R.drawable.ic_cars)
.into(carImage)
}
}
Here is the main Fragment that contains that layout that contains the details:
class InfoFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_car_details_info, container, false)
}
}
I have asked this similar question before but it was'nt clear enough so I hope this was a better way to post my problem.
-EDIT
My tabLayout viewPager uses this
class FragmentCarInfo (fm : FragmentManager) : FragmentPagerAdapter(fm){
override fun getItem(position: Int): Fragment {
return when (position) {
0-> {
InfoFragment()
}
1 -> {
AboutFragment()
}
else -> {
return LocationFragment()
}
}
}
override fun getCount(): Int {
return 3
}
override fun getPageTitle(position: Int): CharSequence? {
return when (position) {
0-> "Details"
1-> "About"
else -> {
return "Locations"
}
}
}
}
First of all, I would recommend ditching the old and nasty TabLayout and switch to the new ViewPager2 instead of the old ViewPager. UI wise, instead of the TabLayout, create custom 3 buttons inside a LinearLayout or ConstraintLayout.
Now, in your case I would use the ViewPager2 with a FragmentStateAdapter because it was designed to work well with Fragments and you can create as many of them as you need. I even had a project where I had 80 fragments created with a FragmentStateAdapter.
Second of all, you need a newInstance constructor for your fragments as a safe pattern. If you don't use that, you might get some exceptions later in your app, but other than that you will also use the function to pass your data to each fragment.
Here is how I do it:
class FragmentStateAdapter(
fragmentManager: FragmentManager,
lifecycle: Lifecycle,
car: Car
) : FragmentStateAdapter(fragmentManager, lifecycle) {
override fun getItemCount(): Int = 3
override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> InfoFragment.newInstance(car)
1 -> AboutFragment.newInstance(car)
else -> LocationFragment.newInstance(car)
}
}
Basically I am passing your car object (presuming it's the only data object that needs to reach the fragments) as a parameter to the FragmentStateAdapter, locking the itemCount to 3 because you have 3 fragments and then returning an instance of each fragment in the override method createFragment(). Now, creating the instance functions:
class InfoFragment : Fragment() {
companion object {
private const val BUNDLE_KEY = "car_object"
fun newInstance(car: Car) = InfoFragment().apply {
arguments = Bundle().apply {
putParcelable<Car>(BUNDLE_KEY, car)
}
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_car_details_info, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val car = arguments.getParcelable<Car>(BUNDLE_KEY)
}
}
And this is how you get your car object inside your fragments. Basically in the newInstance() function you create a new instance of your fragment and pass data to it via bundle. Inside your activity this is how you initialise your adapter:
val adapter = FragmentStateAdapter(supportFragmentManager, lifecycle, car)
Now, about the replacement for the tab view, do a custom LinearLayout like this:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="1.0">
<Button
android:id="#+id/button1"
style="#style/Widget.AppCompat.Button.Borderless"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.333"
android:minWidth="0dp"
android:minHeight="0dp"
android:paddingTop="14dp"
android:paddingBottom="14dp"
android:text="Details"
android:textAllCaps="true" />
<Button
android:id="#+id/button2"
style="#style/Widget.AppCompat.Button.Borderless"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.333"
android:minWidth="0dp"
android:minHeight="0dp"
android:paddingTop="14dp"
android:paddingBottom="14dp"
android:text="About"
android:textAllCaps="true" />
<Button
android:id="#+id/button3"
style="#style/Widget.AppCompat.Button.Borderless"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.333"
android:minWidth="0dp"
android:minHeight="0dp"
android:paddingTop="14dp"
android:paddingBottom="14dp"
android:text="Locations"
android:textAllCaps="true" />
and then set the initial color of the buttons after your own preference with android:background="#COLOR_OR_CHOICE"
In activity, add the three buttons in an ArrayList of buttons and use this listener on the viewPager2:
viewPager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
// here, get the position of the viewPager2 and change the color of the current button using the arrayList index that matches the viewPager2 position.
})
Then, on every button's click listener, do viewPager2.setCurrentItem(position_you_want_to_go_to, true) where true is a check for a smooth scroll.
Hope this helps.
Shouldn't you be assigning the values to the fields in your fragment?
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?