Google maps in a tabbar fragment. Kotlin - android

My project has a tabbar, each of them shows a different fragment.
One of them is a fragment which carries another one from google maps and I can't find a way to make it work.
The main error (apparently) is that
this.childFragmentManager.findFragmentById(R.id.map) as
SupportMapFragment
returns null.
My code is this.
Fragment which has the map
class SpotListFragment : SupportMapFragment(), OnMapReadyCallback {
private lateinit var mMap: GoogleMap
lateinit var api : Api
lateinit var localStorage: LocalStorage
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
// Add a marker in Sydney and move the camera
val sydney = LatLng(-34.0, 151.0)
mMap.addMarker(MarkerOptions().position(sydney).title("Marker in Sydney"))
mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney))
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view: View = inflater!!.inflate(R.layout.fragment_spot_list, container, false)
view.borrarToken.setOnClickListener {
this.localStorage.deleteAll()
val intent = Intent(activity?.applicationContext!!, LoginActivity::class.java)
startActivity(intent)
}
val mapFragment = this.childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
return view
}
The fragment xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:design="http://schemas.android.com/apk/res-auto"
tools:context=".Fragments.SpotListFragment"
android:orientation="vertical"
android:background="#color/white">
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<Button
android:id="#+id/borrarToken"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Borrar token"/>
<ListView
android:layout_gravity="bottom|center"
android:id="#+id/spotsListView"
android:layout_width="match_parent"
android:layout_height="245dp"
android:paddingBottom="60dp"
android:textColor="#color/black"
/>
</FrameLayout>
tabbar activity.
class MainNavActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val spotListFragment = SpotListFragment.newInstance()
openFragment(spotListFragment)
navigationView.setOnNavigationItemSelectedListener {
when(it.itemId) {
R.id.spotsListNav -> {
val spotListFragment = SpotListFragment.newInstance()
openFragment(spotListFragment)
true
}
R.id.searchNav -> {
true
}
R.id.createSpotNav -> {
val createSpotFragment = CreateSpotFragment.newInstance()
openFragment(createSpotFragment)
true
}
R.id.feedNav -> {
true
}
R.id.profileNav -> {
true
}
else -> {
false
}
}
}
}
private fun openFragment(fragment: Fragment) {
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.container, fragment)
transaction.addToBackStack(null)
transaction.commit()
}
}
I wish you can help me, I really don't know what to do.
Thanks!

Get rid of this. in this line of code:
old:
val mapFragment = this.childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
new:
val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment

Related

Cannot load Google map in Kotlin Fragment?

I need to load a google map into fragment. Here's my code and it's not working.
class busFragment : Fragment(), OnMapReadyCallback{
private lateinit var mMap: GoogleMap
companion object {
var mapFragment : SupportMapFragment?=null
val TAG: String = busFragment::class.java.simpleName
fun newInstance() = busFragment()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
var rootView = inflater.inflate(R.layout.fragment_bus, container, false)
childFragmentManager.findFragmentById(R.id.map) as? SupportMapFragment
mapFragment?.getMapAsync(this)
return rootView
}
override fun onMapReady(googleMap: GoogleMap?) {
mMap = googleMap!!
}
}
I added meta tag, dependencies in gradle and manifest. also give internet permission.
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_API_KEY"/>
<uses-permission android:name="android.permission.INTERNET" />
implementation 'com.google.android.gms:play-services-maps:17.0.1'
Here's my xml file of that fragment. In this I used map view.
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="busFragment">
<!-- TODO: Update blank fragment layout -->
<com.google.android.gms.maps.MapView
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout_editor_absoluteX="-1dp"
tools:layout_editor_absoluteY="114dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
This is because mapFragment does not initialize.
Replace this
childFragmentManager.findFragmentById(R.id.map) as? SupportMapFragment
mapFragment?.getMapAsync(this)
With this
mapFragment = childFragmentManager.findFragmentById(R.id.map) as? SupportMapFragment
mapFragment?.getMapAsync(this)

Android: How to open a Fragment with Navigation Graph without replacing the current Fragment

I am wondering if there is a way to add another fragment to the screen without replacing the old one with the Navigation Graph.
I want to do this because the new fragment will just be open for a bit just to check some details and when the user returns to the previous fragment, I want them to be in the same spot they left off.
I have read around but I couldn't find one that does it without replacing the original. I have tried to just add it to the nav_graph_fragment which I try to do this in the newFrag() function in the CurrentFragment section but it doesn't work.
Main Activty
class MainActivity : AppCompatActivity() {
private lateinit var toolbar: Toolbar
private val navController by lazy {
(supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment).navController
}
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
toolbar = findViewById(R.id.mainToolBar)
setSupportActionBar(toolbar)
appBarConfiguration = AppBarConfiguration(navController.graph, null)
setupActionBarWithNavController(navController, appBarConfiguration)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val inflater: MenuInflater = menuInflater
inflater.inflate(R.menu.main_menu_bar, menu)
return true
}
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
if (item.itemId == navController.currentDestination?.id)
return true
return item.onNavDestinationSelected(navController) || super.onOptionsItemSelected(item)
}
}
Main Activity xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.MainActivity">
<include
layout="#layout/appbar"
android:id="#+id/mainToolBar"/>
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#id/mainToolBar"
app:layout_constraintBottom_toBottomOf="parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
CurrentFragment
class CurrentFragment : Fragment() {
private lateinit var currentFragmentViewModel: CurrentFragmentViewModel
private lateinit var binding: CurrentFragmentBinding
private lateinit var recyclerView: RecyclerView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.current_fragment, container, false)
currentFragmentViewModel = ViewModelProvider(this).get(CurrentFragmentViewModel::class.java)
binding.lifecycleOwner = this
binding.viewmodel = currentFragmentViewModel
binding.accountButton.setOnClickListener {
Log.d("TEST", "button clicked")
newFrag()
}
return binding.root
}
// Here is where I am trying to open the new fragment
private fun newFrag() {
val newFrag = Fragment()
val transaction: FragmentTransaction = parentFragmentManager.beginTransaction()
transaction.hide(this)
transaction.add(R.id.nav_host_fragment, newFrag).commit()
}
}
Nav Graph
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/mainFragment">
<fragment
android:id="#+id/mainFragment"
android:name="com.example.app.ui.main.MainFragment"
android:label="MainFragment">
<action
android:id="#+id/action_mainFragment_to_currentFragment"
app:destination="#id/currentFragment" />
</fragment>
<fragment
android:id="#+id/currentFragment"
android:name="com.example.app.ui.search.CurrentFragment"
android:label="currentFragment" >
</fragment>

Activity crashes on inflate when using a Map Fragment (kotlin)

I am making an app that has many map interactions, and one of them is seeing a little map inside an Activity where the users will be able to select a point in the map and send a request to our API.
My problem is, after I made the fragment to load the google maps and added it to the activity that should show it, the Activity itself started to crash when landing on it.
Debugging the app, I see the crash occurs on the 'SetContentView' of the Activity.
Here is the XML for the activity:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FaleConoscoActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="#style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay"/>
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout
android:id="#+id/ll_main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="25dp"
android:paddingTop="70dp"
android:paddingRight="25dp"
android:paddingBottom="40dp"
app:layout_anchor="#+id/ll_main_layout"
app:layout_anchorGravity="center">
//Some EditTexts and Buttons
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<fragment
android:id="#+id/fragment"
android:name="com.example.dumper2.MapFragment"
tools:layout="#layout/fragment_map"
android:layout_width="match_parent"
android:layout_height="294dp" />
</FrameLayout>
</LinearLayout>
...
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Kotlin code for the activity:
class CadastroDePontoActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_cadastro_de_ponto)
}
//Some other interactions
}
And here are the XML and Kotlin for the fragment:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MapFragment">
<fragment
android:name="com.google.android.gms.maps.SupportMapFragment"
android:id="#+id/mapFrag"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
Kotlin:
class MapFragment : Fragment(), OnMapReadyCallback {
private var param1: String? = null
private var param2: String? = null
private var listener: OnFragmentInteractionListener? = null
private lateinit var mMap: GoogleMap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val rootView = inflater.inflate(R.layout.fragment_map, container,
false)
val mapFragment = childFragmentManager.findFragmentById(R.id.mapFrag)as SupportMapFragment
mapFragment.getMapAsync(this)
return rootView
}
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is OnFragmentInteractionListener) {
listener = context
} else {
throw RuntimeException(context.toString() + " must implement
OnFragmentInteractionListener")
}
}
override fun onDetach() {
super.onDetach()
listener = null
}
interface OnFragmentInteractionListener {
// TODO: Update argument type and name
fun onFragmentInteraction(uri: Uri)
}
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* #param param1 Parameter 1.
* #param param2 Parameter 2.
* #return A new instance of fragment MapFragment.
*/
// TODO: Rename and change types and number of parameters
#JvmStatic
fun newInstance(param1: String, param2: String) =
MapFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
Without the crashlog it's just guessing, but I don't see your activity extending OnFragmentInteractionListener, so in onAttach it would throw a RuntimeException.

Android kotlin.TypeCastException: null cannot be cast to non-null type com.google.android.gms.maps.SupportMapFragment

I'm trying to code my current app in Kotlin, but I get null cannot be casted to non-null type. I've tried a lot of different stuff, but I keep getting the same problem. Not sure what to do. Any help would be appreciated!
Code:
class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var mMap: GoogleMap
private lateinit var button: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_maps)
val mapFragment = supportFragmentManager
.findFragmentById(R.id.mapFragment) as SupportMapFragment
mapFragment.getMapAsync(this)
}
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap;
setUpMap();
}
fun setUpMap() {
val et = findViewById<EditText>(R.id.editText);
val et2 = findViewById<EditText>(R.id.editText2);
val lat = et.getText().toString().toDouble();
val lng = et2.getText().toString().toDouble();
//val ll = LatLng(lat, lng)
button = findViewById(R.id.button) as Button
button.setOnClickListener {
goToLocation(lat, lng, 11.0f);
}
}
fun goToLocation(lat:Double, lng:Double, zoom:Float) {
val ll = LatLng(lat, lng);
val update = CameraUpdateFactory.newLatLngZoom(ll, zoom);
mMap.addMarker(MarkerOptions().position(ll).title("Marquette, Michigan"))
mMap.moveCamera(update);
}
XML:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="16dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
tools:layout="#layout/activity_maps">
<EditText
android:id="#+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName" />
<EditText
android:id="#+id/editText2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName" />
<Button
android:id="#+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Submit"/>
<!-- android:onClick="locate"/> -->
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:id="#+id/mapFragment"
/>
Logs:
Caused by: kotlin.TypeCastException: null cannot be cast to non-null type com.google.android.gms.maps.SupportMapFragment
at com.example.nrice.mapsproject.MapsActivity.onCreate(MapsActivity.kt:38)
Try This
val mapFragment = childFragmentManager.findFragmentById(R.id.map_frag) as SupportMapFragment
mapFragment.getMapAsync(this)
The compilation error is pointing to the following line in your code:
val mapFragment = supportFragmentManager
.findFragmentById(R.id.mapFragment) as SupportMapFragment
Here, you are typecasting something which is nullable, i.e. the return value of findFragmentById, to something that you have defined as non-nullable type, i.e. SupportMapFragment
Ideally, you should read about Kotlin null handling. It is quick read and should not take time and will clear your understanding here.
And, when you come back, you will see that there are more than one of ways of fixing your code, such as:
(activity.supportFragmentManager.findFragmentById(fragmentId) as SupportMapFragment?)?.let {
it.getMapAsync(this)
}
Quick tip: The difference between a nullable and non-nullable type in Kotlin is a ? postfixed to the type. So, here, SupportMapFragment? specifies that the type is SupportMapFragment and it can be null.
if you are using SupportMapFragment in a Fragment
you have to write "childFragmentManager.findFragmentById(R.id.myMap)" statement in
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
not in
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
val mapFragment = supportFragmentManager.findFragmentById(R.id.mapFragment) as? SupportMapFragment
mapFragment?.getMapAsync(this)
For reference, you should read this
fix here in kotlin
private lateinit var mMap: GoogleMap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val mapFragment: SupportMapFragment? = map as SupportMapFragment?
mapFragment?.getMapAsync(this)
}
override fun onMapReady(googleMap: GoogleMap?) {
if (googleMap != null) {
mMap = googleMap
}
val sydney = LatLng(-34.0, 151.0)
mMap.addMarker(MarkerOptions().position(sydney).title("Marker in Sydney"))
mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney))
}

button.setOnClickListener in fragment is not working

this is my layout -> fragment_three.xml
<ScrollView
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/supportback">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
//some Textviews here...
<Button
android:id="#+id/sendButton"
android:layout_width="58dp"
android:layout_height="51dp"
android:layout_gravity="right"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:clickable="true"
android:background="#drawable/send_email"/>
</LinearLayout>
</ScrollView>
and this is my fragment:
class fragmentC : Fragment() {
companion object {
fun newInstance():Fragment {
var fb :fragmentC = fragmentC()
return fb
}}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val rootView = inflater.inflate(R.layout.fragment_three,container,false)
val btn = rootView.findViewById(R.id.sendButton) as Button
btn.setOnClickListener {
Toast.makeText(context,"test",Toast.LENGTH_LONG).show()
}
return rootView
}}
no error, no exception, nothing wrong in the logcat.
I could not find the problem yet, any help please ?
edit1
this is the layout where the fragment should be work in:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity"
android:id="#+id/activity_main">
<FrameLayout
android:id="#+id/frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/navigation"
android:animateLayoutChanges="true">
</FrameLayout>
<android.support.design.widget.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#389486"
app:menu="#menu/bottom_view">
</android.support.design.widget.BottomNavigationView>
</RelativeLayout>
P.S : there are tow other fragments but it's blank right now i didn't touch it yet
edit2
the main activity class
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
navigation.setOnNavigationItemSelectedListener (object : BottomNavigationView.OnNavigationItemSelectedListener{
override fun onNavigationItemSelected(item: MenuItem): Boolean {
when(item.itemId){
R.id.bot_list ->{
drawerLayout.openDrawer(Gravity.START)
}
R.id.bot_home ->{
var ft : FragmentTransaction = supportFragmentManager.beginTransaction()
ft.replace(R.id.frame_layout,fragmentA.newInstance())
ft.commit()
}
R.id.bot_cart ->{
var ft : FragmentTransaction = supportFragmentManager.beginTransaction()
ft.replace(R.id.frame_layout,fragmentB.newInstance())
ft.commit() }
R.id.bot_message ->{
//here is the required fragment
var ft : FragmentTransaction = supportFragmentManager.beginTransaction()
ft.replace(R.id.frame_layout,fragmentC.newInstance())
ft.commit() }
}
return true
}
})
var ft : FragmentTransaction = supportFragmentManager.beginTransaction()
ft.replace(R.id.frame_layout,fragmentA.newInstance())
ft.commit()
}
}
i don't know what caused the problem but modifying the code like below solve it
class fragmentC : Fragment(),View.OnClickListener {
companion object {
fun newInstance():Fragment {
var fb :fragmentC = fragmentC()
return fb
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val rootView = inflater.inflate(R.layout.fragment_three, container, false)
val location: Button = rootView.findViewById(R.id.sendButton)
location.setOnClickListener(this)
return rootView
}
override fun onClick(v: View) {
Toast.makeText(context,"test",Toast.LENGTH_LONG).show()
}
}

Categories

Resources