How to add MapBox map into the fragment in bottomnavigation | Kotlin - android

I am trying to add a map to my Android App. I have created basics bottom navigation the same as in this tutorial: https://www.youtube.com/watch?v=Chso6xrJ6aU. I have only added two fragments.
Now I want to add MappBox map into one of them. I am following the Mapbox quick start guide here: https://docs.mapbox.com/android/maps/overview/?q=fragment&size=n_10_n
Everything works fine, but problem is that the Map is not working. When I add the map to the main activity XML, the map loads and works. But when I add the map to one of the fragments, the map does not load. I only see the guides when I hold ctrl in the emulator.
This is the fragment with map in in:
<?xml version="1.0" encoding="utf-8"?>
<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=".SecondFragment">
<com.mapbox.mapboxsdk.maps.MapView
android:id="#+id/map_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.mapbox.mapboxsdk.maps.MapView>
</androidx.constraintlayout.widget.ConstraintLayout>
This is part of MainActivity:
Mapbox.getInstance(this, getString(R.string.mapbox_access_token))
setContentView(R.layout.activity_main)
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNavigationView)
val navController = findNavController(R.id.fragment)
val appBarConfiguration = AppBarConfiguration(setOf(R.id.firstFragment, R.id.secondFragment))
setupActionBarWithNavController(navController, appBarConfiguration)
bottomNavigationView.setupWithNavController(navController)
mapView = findViewById(R.id.map_view)
mapView?.onCreate(savedInstanceState)
mapView?.getMapAsync { mapboxMap ->
mapboxMap.setStyle(Style.MAPBOX_STREETS) {
// Map is set up and the style has loaded. Now you can add data or make other map adjustments
}
}
Can you help me to solve this problem?

Only the fragment that owns the MapView should be interacting with the MapView - your activity should not be involved.

Related

Android Navigation Advanced Sample Dependency Injection (Hilt) on config changes issue

having a problem with Navigation Advanced Sample provided by Google developers.
Main problem is that in regular case senario for Hilt dependency injection we can simply:
Create Custom FragmentFactory
Create Custom NavHostFragment
Asigne it to FragmentContainerView by using android:name="com.package.CustomNavHostFragment"
But how I can do it by using this Navigation Advanced Sample ?
Because now on Activity recreate I'm getting typical HILT error at package.MainActivity.onCreate(MainActivity.kt:23)
EDIT:
More about the problem. By this provided simple we should use
NavigationExtension.kt and setup everything by using BottomNavigationView.setupWithNavController
And problem is that we need to use the default NavHostFragment in order to create a container for each navigation graph.
Is it possible somehow to use custom NavHostFragment? If yes, how can I overite that NavHostFragment.onCreate() mechanism?
I'm talking about this line in NavigationExtension.kt class
If I understood your problem correctly, you want to create a custom NavHostFragment? Yes, that is possible but you don't need to overwrite NavHostFragment.onCreate(). Create a custom NavHostFragment with the following steps: First, create a MainFragmentFactory
MainFragmentFactory
class MainFragmentFactory #Inject constructor(
//... your dependencies
) : FragmentFactory() {
override fun instantiate(classLoader: ClassLoader, className: String): Fragment = when(className) {
MyFragment::class.java.name -> MyFragment(//.. your dependencies)
MySecondFragment::class.java.name -> MySecondFragment(/...)
// other fragments
else -> super.instantiate(classLoader, className)
}
}
Then you need to attach your fragmentFractory to your custom MainNavHostFragment
MainNavHostFragment
#AndroidEntryPoint
class MainNavHostFragment : NavHostFragment() {
#Inject
lateinit var mainFragmentFactory: MainFragmentFactory
override fun onAttach(context: Context) {
super.onAttach(context)
childFragmentManager.fragmentFactory = mainFragmentFactory
}
}
Now you need to add your custom NavHostFragment to your activity_main.xml
Activity_main.xml
<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=".framework.ui.view.MainActivity">
<fragment
android:id="#+id/fragment_container"
<!-- Add path to your custom NavHostFragment here -->
android:name="com.example.app.framework.ui.view.utils.MainNavHostFragment"
android:layout_width="match_parent"
android:layout_height="#dimen/wrapContent"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="#+id/bottomNavigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
<!-- Your NavGraph -->
app:navGraph="#navigation/nav_main" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:itemHorizontalTranslationEnabled="false"
app:labelVisibilityMode="labeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="#menu/bottom_nav_menu_logged_out" />
</androidx.constraintlayout.widget.ConstraintLayout>
Dagger-Hilt will now create all your fragments and instantiate it.

Can I always use findNavController(mView) to find a NavController in Android Studio 3.5.2

The following sample code CameraFragment.kt and activity_main.xml is from camera-samples project.
It find the NavController using findNavController(requireActivity(), R.id.fragment_container).
I think it's a complex, can I always use findNavController(mView) to find the NavController ? just like Code A?
CameraFragment.kt
private fun updateCameraUi() {
...
Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(
CameraFragmentDirections.actionCameraToGallery(outputDirectory.absolutePath))
}
activity_main.xml
<FrameLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
tools:context=".MainActivity">
<fragment
android:id="#+id/fragment_container"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph" />
</FrameLayout>
Code A
private lateinit var mView: View
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mView=view
...
}
Navigation.findNavController(mView).navigate(
CameraFragmentDirections.actionCameraToGallery(outputDirectory.absolutePath))
You don't even need to use Navigation.findNavController(mView) when in a Fragment - you can use NavHostFragment.findNavController(this) to find the NavHostFragment from a Fragment as per the Navigate to a destination documentation.
But yes, you can also use Navigation.findNavController(mView) or use any View from within the Fragment.
You'd only want to use findNavController(requireActivity(), R.id.fragment_container) when you only have the Activity. While it works, there easier ways to do the same thing.

Implement Sharing Location Places Picker same like WhatsApp without Places Picker Since it got deprecated

We had a requirement of sharing location which is a similar kind of WhatsApp with respective to User Interface & functionality.
I have come across that Google Place Picker has got deprecated and also migrated to Places Clients.
Say Like as,
and when they are trying to search,
May be, I am not good at explaining my idea but overall I am in the search of the above kind of implementation.
And also I came to know that Google Charges for the search or the places from the below Url,
https://github.com/rtchagas/pingplacepicker
What all I have tired is,
Coding Part:
Places.initialize(applicationContext, "Google API Key")
placesClient = Places.createClient(this)
val autocompleteFragment =
getSupportFragmentManager().findFragmentById(R.id.autocomplete_fragment) as AutocompleteSupportFragment
autocompleteFragment.setPlaceFields(Arrays.asList(Place.Field.ID, Place.Field.NAME));
autocompleteFragment.setOnPlaceSelectedListener(object : PlaceSelectionListener {
override fun onPlaceSelected(p0: Place) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onError(p0: Status) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
});
val intent = Autocomplete.IntentBuilder(
AutocompleteActivityMode.FULLSCREEN, fields
)
.build(this);
startActivityForResult(intent, AUTOCOMPLETE_REQUEST_CODE);
After searching for two three places I am get an error saying Retry. Don't know the reason.
Error:
Help is really appreciated.
I faced the same problem since i'm working with place picker within my app , but since google decided to remove it , i have no choice than have a work around to find a solution !
i decided to use google map to recreate a place picker close experience .
First i created the map as full screen , and i putted an ImageView (Marker) as overlay of it as xml below :
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/coordinator_layout"
xmlns:app="http://schemas.android.com/apk/res-auto">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.streetdev.goodnecity.SelectLocationPicker" />
<ImageView
android:layout_centerInParent="true"
android:src="#drawable/ic_map"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="56dp"
android:layout_height="56dp"
app:backgroundTint="#color/colorPrimaryDark"
android:src="#drawable/ic_plus"
android:layout_margin="16dp"
app:layout_anchor="#id/map"
app:layout_anchorGravity="bottom|end" />
</android.support.design.widget.CoordinatorLayout>
So whenever the user is navigating on the map , the marker will stay in the center of the map like place picker
Now all you have to do is start your map picker using startActivityForResult, you have to handle the cameraPosition with gps ( if its available ) and then as soon as you want to go back with the result ( for me i click on the Fab button ) , you can get the result this way :
LatLng latng = mMap.getCameraPosition().target;
Intent returnIntent = new Intent();
returnIntent.putExtra("result",String.valueOf(latng.latitude)+";"+String.valueOf(latng.longitude));
setResult(Activity.RESULT_OK,returnIntent);
finish();
The last step is to handle the result in onActivityResult, you get your result , you do a split using ";" to separate latitude from longitude
hope my example will help some one !

Mapbox map only semi responds to panning gestures

I have implements a mapbox MapView. It appears on screen just fine, but when I try to pan the map around, it kinds moves a few pixels on the screen in the direction I intended and then it stop while my finger is still sliding. Sometimes that would happen and sometimes it wouldn't respond at all.
When it stops, it doesn't mean it is stuck now - I can try again and it might move again but the same way.
It's very unpredictable, not consistent and clunky.
**I have to mention, that double click for zooming always works. So it's something with the swipe gesture that doesn't sit well.
I have the map hosted in a fragment and I show in in a frame inside another fragment. Could it have anything to do to the fact that it's two layers of fragment managers (fragment within fragment).
This is my map's fragment 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:mapbox="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.ImageMapViewFragment"
tools:showIn="#layout/fragment_image_expanded">
<com.mapbox.mapboxsdk.maps.MapView
android:id="#+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
mapbox:mapbox_cameraZoom="11"/>
</androidx.constraintlayout.widget.ConstraintLayout>
This is my fragment:
mapView = view.findViewById(co.getdere.R.id.mapView)
mapView?.getMapAsync { mapboxMap ->
mapboxMap.setStyle(Style.LIGHT) { style ->
style.addImage(
DERE_PIN,
BitmapUtils.getBitmapFromDrawable(resources.getDrawable(R.drawable.pin_icon))!!,
true
)
val geoJsonOptions = GeoJsonOptions().withTolerance(0.4f)
val symbolManager = SymbolManager(mapView!!, mapboxMap, style, null, geoJsonOptions)
symbolManager.iconAllowOverlap = true
sharedViewModelForImage.sharedImageObject.observe(this, Observer {
it?.let { image ->
val symbolOptions = SymbolOptions()
.withLatLng(LatLng(image.location[0], image.location[1]))
.withIconImage(DERE_PIN)
.withIconSize(1.3f)
.withZIndex(10)
.withDraggable(true)
symbolManager.create(symbolOptions)
val position = CameraPosition.Builder()
.target(LatLng(image.location[0], image.location[1]))
.zoom(10.0)
.build()
mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(position))
}
})
}
}
These are my dependencies (the last I've added trying to solve my problem but it remains, that dependency didn't change anything really)
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:7.4.0-alpha.1'
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-annotation-v7:0.6.0'
implementation 'com.mapbox.mapboxsdk:mapbox-android-gestures:0.4.0'
My problem was that even though my fragment was external, it was hosted inside a scroll layout so the gestures were lost between the two view probably

iOS equivalent for Android's include control

I am designing an cross-platform app for iOS aswell Android with a shared logic using MVVMCross (5.1.1).
Throughout my app I have a fixed toolbar at the top displaying the current view's title aswell a button. Below the bar the interface is changing from view to view
The Android part:
On Android I created a reuseable layout which I embed in my current layout using include.
In my portable project I have a BaseViewModel which has the properties the reuseable toolbar layout binds to. Every other ViewModel derives from this base class. This way I can have all bindable properties of a displayed screen in one ViewModel without the need of nesting but see for yourself:
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="#+id/toolbar"
layout="#layout/toolbar_login" />
<RelativeLayout
android:id="#+id/parentLoginLayout"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/toolbar">
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
app:MvxBind="Text Pin"
/>
</RelativeLayout>
</RelativeLayout>
toolbar_login.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar_login"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="0dp"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
android:minWidth="25px"
android:minHeight="25px">
<TextView
app:MvxBind="Click ToolbarMenuCommand"
/>
<!-- some other -->
</android.support.v7.widget.Toolbar>
ViewModels.cs
using System.Threading.Tasks;
using Mobile.Helpers;
using ViewModels.Base;
using MvvmCross.Core.Navigation;
using MvvmCross.Core.ViewModels;
using Plugin.MessageBox;
namespace Mobile.ViewModels
{
public abstract class BaseViewModel : MvxViewModel
{
protected void NavigateToMainView()
{
NavigateTo<MainViewModel>();
}
private readonly IMvxNavigationService _navigationService;
protected BaseViewModel(IMvxNavigationService navigationService)
{
_navigationService = navigationService;
}
public IMvxCommand ToolbarMenuCommand => new MvxCommand(OnMenuButtonClick);
protected abstract void OnMenuButtonClick();
}
public class LoginViewModel : BaseViewModel
{
private bool _menuVisibility;
private string _pin;
public LoginViewModel(IMvxNavigationService navigationService) : base(navigationService)
{
}
public bool MenuVisibility
{
get => _menuVisibility;
set => SetProperty(ref _menuVisibility, value);
}
public string Pin
{
get => _pin;
set => SetProperty(ref _pin, value);
}
protected override void OnMenuButtonClick()
{
MenuVisibility = !MenuVisibility;
}
}
}
The iOS part:
I am not entirely sure how to realise above behavior on iOS. I hope someone has a good idea or a good example project for me which I can take a look at. In general it is no problem to refactorise the ViewModels incase my idea is just not possible at iOS.
A few facts about the iOS project:
I am not using storyboards but single .xib's being independent
from each other
In my .xib's files I use autolayout constraints for positioning and
sizing entirely
A few ideas I already had (cant test them right now):
1. idea:
Create a base .xib with the above bar, the constraints aswell the
outlets
Create each new xib Design based on the previously created file
This would mean I need to adjust every view incase I decide to change something about the toolbar but so far I found no other way to embed a .xib in another .xib without having two different ViewControllers. Also I read that inheritance cause problems with outlets.
2. idea
Each .xib has an empty view at the top which acts as a container for
the toolbar
Have a Base ViewController which constructs the toolbar from code and
adds it as a child to the container view, and binds the properties
from the BaseViewModel
In a previous iOS project I noticed that adding views to the layout can cause problems with autolayout. Probably also a not that good solution?
3. idea
Create a xib with the toolbar and a container below and use it as a master page which would probably mean having a MasterViewModel with the toolbar properties and a nested ChildViewModel.
This is probably the way to go but I have to admit that I have no clue what is the best way to approach it (stil pretty new to iOS and MVVMCross).
Does someone have a few useful hints for me? Thanks a lot!
From what I understood I think you should try to use ScrollView for iOS part and try to imitate the ViewPager's behavior from Android, an example.

Categories

Resources