如何在Fragment中获取MainActivity的Context与MapView以实例化NavFun
问题描述
我创建了CreatePathFragment,希望通过EditText输入的数值作为参数,调用NavFun类的routePath方法。NavFun的构造函数需要传入MainActivity的Context和MapView对象,我想在Fragment的按钮点击事件中调用该方法,但不清楚如何获取这两个对象来实例化NavFun。期望实现的逻辑如下:
val navFun: NavFun = NavFun(context, mapView) navFun.routePath(p1lati, p1long, p2lati, p2long) navFun.mapView.invalidate()
CreatePathFragment代码
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER private const val ARG_PARAM1 = "param1" private const val ARG_PARAM2 = "param2" /** * A simple [Fragment] subclass. * Use the [CreatePathFragment.newInstance] factory method to * create an instance of this fragment. */ class CreatePathFragment : Fragment() { private var param1: String? = null private var param2: String? = null private lateinit var editTextLatitudineP1: EditText private lateinit var editTextLongitudineP1: EditText private lateinit var editTextLatitudineP2: EditText private lateinit var editTextLongitudineP2: EditText 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 { val view: View = inflater.inflate(R.layout.fragment_create_path, container, false) // create path between editText points editTextLatitudineP1 = view.findViewById<EditText>(R.id.editTextLatitudineP1) editTextLongitudineP1 = view.findViewById<EditText>(R.id.editTextLongitudineP1) editTextLatitudineP2 = view.findViewById<EditText>(R.id.editTextLatitudineP2) editTextLongitudineP2 = view.findViewById<EditText>(R.id.editTextLongitudineP2) val b = view.findViewById<Button>(R.id.location) b.setOnClickListener{ try { Log.d("Clicked", "Clicked") val p1lati: Double = editTextLatitudineP1.text.toString().toDouble() val p1long: Double = editTextLongitudineP1.text.toString().toDouble() val p2lati: Double = editTextLatitudineP2.text.toString().toDouble() val p2long: Double = editTextLongitudineP2.text.toString().toDouble() println("latitudine p1: $p1lati, longitudine p1: $p1long") println("latitudine p2: $p2lati, longitudine p2: $p2long") navFun.routePath(p1lati, p1long, p2lati, p2long) navFun.mapView.invalidate() } catch (e: Exception) { Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show() } } // Inflate the layout for this fragment return view } 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 CreatePathFragment. */ @JvmStatic fun newInstance(param1: String, param2: String) = CreatePathFragment().apply { arguments = Bundle().apply { putString(ARG_PARAM1, param1) putString(ARG_PARAM2, param2) } } } }
NavFun类代码
class NavFun(private val context: Context, private val mapView: MapView) { private lateinit var road: Road val context1: Context = context val mapView1: MapView = mapView // function to create path between two points fun routePath(p1Latit: Double, p1Laong: Double, p2Latit: Double, p2Laong: Double){ println("inizio") val roadManager: RoadManager = OSRMRoadManager(context, "lolloMaps") OSRMRoadManager.MEAN_BY_FOOT println("passo1 - creoArrayList") val waypoints = arrayListOf<GeoPoint>() println("passo2 - CreoPuntiEAggiungoInArrayList") val startPoint: GeoPoint = GeoPoint(p1Latit, p1Laong) //43.12628, 12.04705 waypoints.add(startPoint) val endPoint: GeoPoint = GeoPoint(p2Latit, p2Laong) //43.12124, 11.97211 waypoints.add(endPoint) println("passo3 - CreoStrada") road = roadManager.getRoad(waypoints) if (road.mStatus != Road.STATUS_OK){ Toast.makeText(context, "Errore nel caricamento di road - status = " + road.mStatus, Toast.LENGTH_SHORT).show() } println("passo4 - CreoPolilinea") val roadOverlay: Polyline = RoadManager.buildRoadOverlay(road) println("passo5 - AggiungoPolilineaAllaMappa") mapView.overlays.add(roadOverlay) mapView.invalidate() // create checkpoints along the route val nodeIcon: Drawable? = ResourcesCompat.getDrawable(mapView.resources, R.drawable.marker_node, null) for (i: Int in road.mNodes.indices){ val node: RoadNode = road.mNodes[i] val nodeMarker: Marker = Marker(mapView) nodeMarker.position = node.mLocation nodeMarker.icon = nodeIcon nodeMarker.title = "Passo $i" mapView.overlays.add(nodeMarker) // add information in checkpoint windows nodeMarker.snippet = node.mInstructions nodeMarker.subDescription = Road.getLengthDurationText(context,node.mLength, node.mDuration) // var icon: Drawable = resources.getDrawable(R.drawable.ic_continue) // nodeMarker.image = icon } } // function to add Marker private fun mioMarker(lati: Double, longi: Double, i: Int) { val pinMarker = Marker(mapView) val geoPoint = GeoPoint(lati, longi) pinMarker.position = geoPoint pinMarker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER) pinMarker.title = "Title" pinMarker.subDescription = "io sono il pin #$i con coordinate $lati, $longi." pinMarker.isDraggable = true mapView.overlays.add(pinMarker) mapView.invalidate() } }
MainActivity代码
class MainActivity : AppCompatActivity() { private var backPressedTime = 0L private lateinit var toggle: ActionBarDrawerToggle private lateinit var drawerLayout: DrawerLayout private lateinit var mapView : MapView private lateinit var myLocationNewOverlay: MyLocationNewOverlay private lateinit var compassOverlay: CompassOverlay private lateinit var mapController: IMapController override fun onCreate(savedInstanceState: Bundle?) { // disabilita la policy di strictMode nella onCreate per non fare chiamate network in async tasks val policy: StrictMode.ThreadPolicy = StrictMode.ThreadPolicy.Builder().permitAll().build() StrictMode.setThreadPolicy(policy) super.onCreate(savedInstanceState) //richiesta per gestire i permessi requestPermission() // inizializza la configurazione di osmdroid, non funziona se non si importa org.osmdroid.config.Configuration.* getInstance().load(this, PreferenceManager.getDefaultSharedPreferences(this)) //crea la mappa setContentView(R.layout.activity_main) mapView = findViewById<MapView>(R.id.map) mapView.setTileSource(TileSourceFactory.MAPNIK) mapView.zoomController.setVisibility(CustomZoomButtonsController.Visibility.NEVER) // crea MapController e setta posizione iniziale mapController = mapView.controller // crea overlay posizione myLocationNewOverlay = MyLocationNewOverlay(GpsMyLocationProvider(this), mapView) myLocationNewOverlay.enableMyLocation() myLocationNewOverlay.enableMyLocation() myLocationNewOverlay.isDrawAccuracyEnabled = true myLocationNewOverlay.runOnFirstFix { runOnUiThread { mapController.animateTo(myLocationNewOverlay.myLocation) mapController.setZoom(9.0) } } mapView.overlays.add(myLocationNewOverlay) //set user agent Configuration.getInstance().userAgentValue = "lolloMaps" // controllo println(myLocationNewOverlay.myLocation) println("creato") // attiva bussola, Non Funziona! compassOverlay = CompassOverlay(this, InternalCompassOrientationProvider(this), mapView) compassOverlay.enableCompass() mapView.overlays.add(compassOverlay) // attivare griglia latitudine e longitudine // val overlay = LatLonGridlineOverlay2() // mapView.overlays.add(overlay) // abilita gesture rotazione e zoom val rotationGestureOverlay = RotationGestureOverlay(mapView) rotationGestureOverlay.isEnabled mapView.setMultiTouchControls(true) mapView.overlays.add(rotationGestureOverlay) // abilita mia posizione myLocationNewOverlay = MyLocationNewOverlay(GpsMyLocationProvider(this), mapView) myLocationNewOverlay.enableMyLocation() mapView.overlays.add(myLocationNewOverlay) // abilita overlay scala val dm : DisplayMetrics = resources.displayMetrics val scaleBarOverlay = ScaleBarOverlay(mapView) scaleBarOverlay.setCentred(true) scaleBarOverlay.setScaleBarOffset(dm.widthPixels / 2, 10) mapView.overlays.add(scaleBarOverlay) // abilita comandi sulla mappa, single tap e long press val mapEventsReceiver: MapEventsReceiverImpl = MapEventsReceiverImpl() val mapEventsOverlay: MapEventsOverlay = MapEventsOverlay(mapEventsReceiver) mapView.overlays.add(mapEventsOverlay) //-------------------------------------------------------------------------------------------- // codice per menu laterale drawerLayout = findViewById(R.id.drawerLayout) val navView: NavigationView = findViewById(R.id.nav_view) toggle = ActionBarDrawerToggle(this, drawerLayout, R.string.open, R.string.close) drawerLayout.addDrawerListener(toggle) toggle.syncState() supportActionBar?.setDisplayHomeAsUpEnabled(true) navView.setNavigationItemSelectedListener { it.isChecked = true when(it.itemId){ R.id.nav_create_path -> { Toast.makeText(applicationContext, "Clicked Crea Percorso", Toast.LENGTH_SHORT).show() replaceFreagment(CreatePathFragment(), it.title.toString()) } R.id.nav_saved_paths -> { Toast.makeText(applicationContext, "Clicked Percorsi Salvati", Toast.LENGTH_SHORT).show() replaceFreagment(SavedPathsFragment(), it.title.toString()) } R.id.nav_center_map -> { Toast.makeText(applicationContext, "Clicked Centra Mappa", Toast.LENGTH_SHORT).show() myLocationNewOverlay.runOnFirstFix { runOnUiThread { mapController.animateTo(myLocationNewOverlay.myLocation) mapController.setZoom(9.0) } } } R.id.nav_trash -> Toast.makeText(applicationContext, "Clicked Pulisci Mappa", Toast.LENGTH_SHORT).show() R.id.nav_settings -> Toast.makeText(applicationContext, "Clicked Impostazioni", Toast.LENGTH_SHORT).show() R.id.nav_share -> Toast.makeText(applicationContext, "Clicked Condividi", Toast.LENGTH_SHORT).show() R.id.nav_contact_us -> Toast.makeText(applicationContext, "Clicked Contattaci", Toast.LENGTH_SHORT).show() R.id.nav_exit -> Toast.makeText(applicationContext, "Clicked Esci", Toast.LENGTH_SHORT).show() } true } } override fun onResume() { super.onResume() mapView.onResume() } override fun onPause() { super.onPause() mapView.onPause() } //funzione per controllare se ho i permessi private fun hasPermission() : Boolean { // ritorna true quando abbiamo i permessi return ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED } //funzione per richiedere i permessi private fun requestPermission() { //aggiungo i permessi ad una lista val permission = mutableListOf<String>() // se non ho i permessi if (!hasPermission()) { // aggiungo i permesi alla lista permission.add(Manifest.permission.ACCESS_FINE_LOCATION) } // concede i permessi nella lista if (permission.isNotEmpty()) { ActivityCompat.requestPermissions(this, permission.toTypedArray(), 0) } } // fragments function private fun replaceFreagment(fragment: Fragment, title: String) { val fragmentManager: FragmentManager = supportFragmentManager val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction() fragmentTransaction.replace(R.id.frameLayout, fragment) fragmentTransaction.addToBackStack(null).commit() drawerLayout.closeDrawers() setTitle(title) } // side menu function override fun onOptionsItemSelected(item: MenuItem): Boolean { if (toggle.onOptionsItemSelected(item)){ return true } return super.onOptionsItemSelected(item) } override fun onBackPressed() { if (backPressedTime + 2000 > System.currentTimeMillis()) { super.onBackPressed() }else{ Toast.makeText(this, "Premere ancora per tornare indietro", Toast.LENGTH_SHORT).show() } backPressedTime = System.currentTimeMillis() } }
解决方案
方式一:直接通过Activity引用获取MapView
Fragment可以通过requireActivity()获取绑定的Activity实例,强转为MainActivity后直接拿到mapView,Context使用requireContext()即可:
- 在CreatePathFragment中添加NavFun变量并初始化:
private lateinit var navFun: NavFun override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val mainActivity = requireActivity() as MainActivity navFun = NavFun(requireContext(), mainActivity.mapView) }
- 修改按钮点击事件中的Toast上下文(Fragment不能直接作为Context传入):
Toast.makeText(requireContext(), "Error", Toast.LENGTH_SHORT).show()
方式二:接口回调解耦(推荐)
如果不想让Fragment直接依赖MainActivity,可定义接口让Activity实现,提供获取MapView的方法:
- 创建接口:
interface MapProvider { fun getMapView(): MapView }
- 修改MainActivity实现该接口:
class MainActivity : AppCompatActivity(), MapProvider { // ... 原有代码 ... override fun getMapView(): MapView { return mapView } }
- 在CreatePathFragment中获取接口实例并初始化NavFun:
private lateinit var navFun: NavFun private lateinit var mapProvider: MapProvider override fun onAttach(context: Context) { super.onAttach(context) mapProvider = context as MapProvider } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) navFun = NavFun(requireContext(), mapProvider.getMapView()) }
额外优化建议
- 删除
NavFun中冗余的context1和mapView1变量,直接使用构造函数传入的context和mapView。 routePath方法中的roadManager.getRoad(waypoints)是网络请求,需放在后台线程执行,可改用Coroutine避免阻塞UI。
内容的提问来源于stack exchange,提问作者George Jung
相关产品推荐
相关产品推荐

