I have a small app, where i have a fragment AchievementFragment and in there i have a few imageButtons. I want to make it so that when i click on one of them, a toast appears on the screen, but i have a problem with just the imageButton itself. I tried following a few online tutorials like this one: https://www.geeksforgeeks.org/imagebutton-in-kotlin/, but when i try to use
val imgbtn = findViewById<ImageButton>(R.id.imageBtn)
i get unresolved findViewById reference error.
You can't use directly findViewById in fragments, you have to use it with root view, in your onCreateView you are returning the root view. Your other views are inside the root view. So if you want to access a view inside root you should use like this before returning the root view
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.fragment_blank, container, false)
val imgbtn = root.findViewById<ImageButton>(R.id.imageBtn)
return root
}
val imgbtn = (activity as "activity name").findViewById<ImageButton>(R.id.imageBtn)
replace "activity name" with your activity.
OR
Use View Binding instead of findViewById.
https://developer.android.com/topic/libraries/view-binding
Related
I have a project with 2 fragments. I am looking to pass an iterable from the first fragment to the second. Using navArgs is not an option, since it makes the program crash. Bundle seems to only work with primary data types. Is there a way to go about it, without using some super hacky solution, like passing a string of all the data separated by commas as a string?
The modern way to do this is with a ViewModel (here and here or with the FragmentResult API (last link). Otherwise you're looking at doing it manually through the parent Activity - call a function on the Activity that passes your data to the other Fragment, that kind of thing.
If these Fragments are in separate Activities then you're looking at making your data Parcelable so it can go in a Bundle, or serialisation (e.g. the Kotlin Serialization library) so you can put it in a Bundle as a String, or persist it on disk so you can load it from the next Activity. Serialisation libraries are a robust way of turning objects and data into a stream of text (and other formats if you like) but there's nothing wrong with a String and some separator character if it's all you need, e.g. storing a list of indices or IDs
You can use a shared view model.
In your first fragment:
<code>class FirstFragment : Fragment() {
private lateinit var viewModel: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(requireActivity()).get(SharedViewModel::class.java)
viewModel.setData(yourIterable)
}
}
</code>
In your second fragment:
<code>class SecondFragment : Fragment() {
private lateinit var viewModel: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(requireActivity()).get(SharedViewModel::class.java)
val data = viewModel.getData()
}
}
</code>
And your <code>SharedViewModel</code>:
<code>class SharedViewModel : ViewModel() {
private val _data
Here's the code of my DialogFragment.kt
class CustomDialogCalendarFragment: DialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val binding: FragmentCustomDialogCalendarBinding = DataBindingUtil.inflate(
inflater, R.layout.fragment_custom_dialog_calendar, container, false)
val application = requireNotNull(this.activity).application
//Create an instance of the ViewModelFactory
val dataSource = ReservationDatabase.getInstance(application).ReservationDatabaseDao
val viewModelFactory = CustomDialogCalendarViewModelFactory(dataSource)
//Get a reference to the ViewModel associated with this fragment
val customDialogViewModel =
ViewModelProvider(
this, viewModelFactory).get(CustomDialogCalendarViewModel::class.java)
// To use the View Model with data binding, you have to explicitly
// give the binding object a reference to it.
binding.customDialogCalendarViewModel = customDialogViewModel
customDialogViewModel.navigateToSharedResourceCalendar.observe(viewLifecycleOwner) {
if (it == true) {
customDialogViewModel.doneNavigating()
}
}
binding.submitCreateEventButton.setOnClickListener { view: View ->
customDialogViewModel.onSetReservation(binding.nameEvent.text.toString())
this.findNavController().navigate(CustomDialogCalendarFragmentDirections.actionCustomDialogCalendarFragmentToSharedResourceCalendarFragment())
}
//HERE'S THE NULL POINTER EXCEPTION
getDialog()!!.getWindow()?.setBackgroundDrawableResource(R.drawable.round_corner)
return binding.root
}
override fun onStart() {
super.onStart()
val width = (resources.displayMetrics.widthPixels * 0.85).toInt()
//HERE'S THE NULL POINTER EXCEPTION
dialog!!.window?.setLayout(width, ViewGroup.LayoutParams.WRAP_CONTENT)
}
}
I don't understand why the getDialog() method returns null in every point of code on the onCreateView.
This class is called by another Fragment.
I have the same kind of DialogFragment in the project that does the same operations with no error.
So I don't know why this DialogFragment doesn't do the same.
Thank you!
I'm trying to transition from using AppcomatActivity to Fragment, because I'm updating my app but I've run into a problem. I get a "Unresolved reference: supportFragmentManager" and "Unresolved reference: setSupportActionBar"(That's all the Logcat shows me) when I try to run my app. Now I chose to switch to using fragments, because I also want to change the apps UI, usage is quicker than the previous version. Anyway here's my code:
Recorder Fragment
package com.khumomashapa.notes.fragments
import android.os.Build
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toolbar
import androidx.annotation.RequiresApi
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import androidx.viewpager.widget.ViewPager
import com.astuetz.PagerSlidingTabStrip
import com.khumomashapa.notes.R
import kotlinx.android.synthetic.main.toolbar3.*
class RecorderFragment : Fragment() {
private var tabs: PagerSlidingTabStrip? = null
private var pager: ViewPager? = null
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.title = "Recorder";
pager = pager?.findViewById<View>(R.id.pager) as ViewPager
pager!!.adapter = MyAdapter(supportFragmentManager)
tabs = tabs?.findViewById<View>(R.id.tabs) as PagerSlidingTabStrip
tabs!!.setViewPager(pager)
val toolbar = toolbar?.findViewById<View>(R.id.toolbar) as Toolbar
toolbar.popupTheme = R.style.ThemeOverlay_AppCompat_Light
setSupportActionBar(toolbar)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_recorder, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
inner class MyAdapter(fm: FragmentManager?) : FragmentPagerAdapter(
fm!!
) {
private val titles = arrayOf(
getString(R.string.tab_title_record),
getString(R.string.tab_title_saved_recordings)
)
override fun getItem(position: Int): Fragment {
when (position) {
0 -> {
return RecordFragment.newInstance(position)
}
1 -> {
return FileViewerFragment.newInstance(position)
}
}
return null!!
}
override fun getCount(): Int {
return titles.size
}
override fun getPageTitle(position: Int): CharSequence? {
return titles[position]
}
}
companion object {
private val LOG_TAG = RecorderFragment::class.java.simpleName
}
}
The purpose of this class is to show a view pager that can switch between to other fragments I've already created. I was able to fix the other errors relating to this like the "Unresolved reference for findViewById" and the "MyAdapter class".
For a Fragment, there are actually two relevant fragment managers. Deciding which one to use depends on your use case.
The Child Fragment Manager
A fragment has a child fragment manager which is responsible for managing its child/nested fragments. You can obtain this with:
Fragment.getChildFragmentManager()
The Parent's Fragment Manager
A fragment also holds reference to it's parent's fragment manager. If the fragment is a direct child of an activity then his represents the activities fragment manager. Otherwise if the fragment is a child of another fragment, it represents the child fragment manager of the parent fragment. This can be obtained with:
Fragment.getParentFragmentManager()
Note that although Fragment has the method Fragment.getFragmentManager(), this is deprecated in favour of Fragment.getParentFragmentManager() so it shouldn't be used.
You can also technically get the activities fragment manager regardless by obtaining a reference to the fragment's activity with Fragment.getAcitivity() and then calling Activity.getSupportFragmentManager(). But generally the parent fragment manager is more useful and clear.
enter image description hereI just have this:
package com.example.world
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener() {
// Do some work here
}
}
}
But on this line:
button.setOnClickListener() {
// Do some work here
}
it says:
Unresolved reference: button
And I dont see the hint for important the correct library.
So what kind of library I have to import then?
Thank you
if I do it like this:
val btn_click = findViewById(R.id.button) as Button
btn_click.setOnClickListener(){
Toast.makeText(this#MainActivity, "YOu clicked on me", Toast.LENGTH_LONG).show()
}
Then it works.
But that is not necessarily I have read.
You dont have to make a referenct to the button.
So you just can do this:
button.setOnClickListener... but what is the correct library for that?
You can't use 'abstract' button, you need to find it in layout by element id (You can have many buttons in your layout, how you will find correct button without id?) If you wouldn't like to find layout elements by id, you can take a look on Android View Binding here: https://developer.android.com/topic/libraries/view-binding.
I'm learning Android studio with Kotlin, I have setup one listview in Oncreate, it list out user data, then I have another function onOptionItemSelected which add/delete user data item, the problem is : after I add/delete from another function, the data on listview cannot be updated:
Here is my code:
class MainActivity : AppCompatActivity() {
private lateinit var listView : ListView
override fun onCreate(savedInstanceState: Bundle?) {
.....
listView = findViewById(R.id.listview_main)
val adapter2 = listViews(this, array_firstname, array_lastname,array_age)
listView.adapter = adapter2
....
override fun onOptionsItemSelected(item: MenuItem): Boolean {
........
GlobalScope.launch {
db1.UsersDao().insertAll(User_tb(0,firstName, lastName, userAge))
(listView.adapter as listViews).notifyDataSetChanged()
}
With this, I got error "Only the original thread that created a view hierarchy can touch its views."
I searched internet and found I need to use runOnUiThread, then I below part under "listView.adapter = adapter2", but it still does not work:
Thread(Runnable {
this#MainActivity.runOnUiThread(java.lang.Runnable {
(listView.adapter as listViews).notifyDataSetChanged()
})
}).start()
I guess I did not understand runOnUiThread correctly but cannot figure out how, could somebody help?
Thanks!
You don't need to mess with Threads directly since you're using coroutines.
Replace this:
GlobalScope.launch {
db1.UsersDao().insertAll(User_tb(0,firstName, lastName, userAge))
(listView.adapter as listViews).notifyDataSetChanged()
}
with this:
lifecycleScope.launch {
withContext(Dispatchers.IO) {
db1.UsersDao().insertAll(User_tb(0,firstName, lastName, userAge))
}
listView.adapter.notifyDataSetChanged() // your cast was unnecessary
}
lifecycleScope runs on the UI thread, except where you wrap the code using withContext to run in the background. So after the withContext block is done, it automatically returns to the main UI thread to run your last line.
This scope also gives you leak protection. If the Activity is closed before the job is done, the coroutine is automatically cancelled and the view elements can be freed to the GC. It won't try to run the last line that updates the UI.
Also, a tip: Your class names should start with a capital letter so they are clearly distinguishable from variable/property names. And they should be more descriptive. For example, I would change the listviews class name to something like UserListAdapter.