Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Expose a generic Android View Marker API #3276

Closed
18 of 19 tasks
tobrun opened this issue Dec 14, 2015 · 70 comments · Fixed by #4923
Closed
18 of 19 tasks

Expose a generic Android View Marker API #3276

tobrun opened this issue Dec 14, 2015 · 70 comments · Fixed by #4923
Labels
Android Mapbox Maps SDK for Android feature

Comments

@tobrun
Copy link
Member

tobrun commented Dec 14, 2015

Current Marker API on android is fairly limited because Markers are Gl-drawn components. I'm hearing about requests for animating markers or having the ability to do more typical Android SDK visual things with it (animations, selectors etc.).

Concurrent InfoWindows from #3127 and UserLocationView showcased that we are able to sync Android Views with the underlying MapView. The logic is their but we do not expose a generic system.

API proposal:

For native annotations views in the Android Mapbox SDK. I don't see any reason to stray far from the adapter concept what an Android developer uses on daily basis. Combining this with the existing Mapbox API for adding annotations will look like:

 // combined API
 addMarker();
 addMarkers();
 removeMarker();
 removeMarkers();
 selectMarker();
 // new API
 setMarkerViewAdapter();
 setOnMarkerViewClickListener();
 setMarkerViewItemAnimation();

Example API

Integration of the API will follow the same path as the current Marker API.

  // Add country markers
  List<BaseMarkerOptions> countries = new ArrayList<>();
  countries.add(new CountryMarkerOptions().title("China").abbrevName("ch").flagRes(R.drawable.ic_china).position(new LatLng(31.230416, 121.473701)));
  countries.add(new CountryMarkerOptions().title("United States").abbrevName("us").flagRes(R.drawable.ic_us).position(new LatLng(38.907192, -77.036871)));
  countries.add(new CountryMarkerOptions().title("Brazil").abbrevName("br").flagRes(R.drawable.ic_brazil).position(new LatLng(-15.798200, -47.922363)));
  countries.add(new CountryMarkerOptions().title("Germany").abbrevName("de").flagRes(R.drawable.ic_germany).position(new LatLng(52.520007, 13.404954)));
  mapboxMap.addMarkers(countries);

On top of that it will expose some new APIs to make ViewMarkers possible:

  // Add view marker adapter
  mapboxMap.setMarkerViewAdapter(new CountryAdapter(this));

  // Add a view marker click listener
  mapboxMap.setOnMarkerViewClickListener(new MapboxMap.OnMarkerViewClickListener() {
            @Override
            public void onMarkerClick(@NonNull Marker marker, @NonNull View view) {
                Log.d(MapboxConstants.TAG, "Country clicked " + ((CountryMarker) marker).getAbbrevName());
            }
        });

An example of the MarkerViewAdapter shown above:

    private static class CountryAdapter implements MapboxMap.MarkerViewAdapter<CountryMarker> {

        private LayoutInflater inflater;

        public CountryAdapter(@NonNull Context context) {
            this.inflater = LayoutInflater.from(context);
        }

        @Nullable
        @Override
        public View getView(@NonNull CountryMarker marker, @Nullable View convertView, @NonNull ViewGroup parent) {
            ViewHolder viewHolder;
            if (convertView == null) {
                viewHolder = new ViewHolder();
                convertView = inflater.inflate(R.layout.view_custom_marker, parent, false);
                viewHolder.flag = (ImageView) convertView.findViewById(R.id.imageView);
                viewHolder.abbrev = (TextView) convertView.findViewById(R.id.textView);
                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }
            viewHolder.flag.setImageResource(marker.getFlagRes());
            viewHolder.abbrev.setText(marker.getAbbrevName());
            return convertView;
        }

        private static class ViewHolder {
            ImageView flag;
            TextView abbrev;
        }
    }

Next steps:

  • Basic tracking view on Mapview - @tobrun
  • Research limitations + perf. impacts - @tobrun
  • Test out Android SDK animations - @tobrun
  • Look into different approaches to publicly expose the API - @tobrun
  • Introduce basic View Adapter approach - @tobrun
  • Use a View reuse pattern in adapter (SimplePool) - @tobrun
  • Integrate with selectMarker(Marker m) API - @tobrun
  • Integrate with removeMarker(Marker m) API - @tobrun
  • Hide old GL marker - @tobrun
  • Trigger invalidate on View markers when Map is fully loaded - @tobrun
  • Validate is exposed Android View Marker API matches iOS View Annotation API - @tobrun
  • Profile execution - @tobrun
  • Animation system - @tobrun
  • Select animation API - @tobrun
  • allow adding multiple adapters - @tobrun
  • Flat configurable
  • Offset configurable
  • Double check changes made to basic Marker API - @tobrun
  • API documentation - @tobrun

@incanus @bleege @zugaldia @cammace @danswick

@tobrun tobrun added Android Mapbox Maps SDK for Android feature request labels Dec 14, 2015
@tobrun tobrun added this to the android-v2.4.0 milestone Dec 14, 2015
@zugaldia
Copy link
Member

Related: #3116

@tobrun
Copy link
Member Author

tobrun commented Dec 15, 2015

Related: #1125

@tobrun
Copy link
Member Author

tobrun commented Jan 11, 2016

To make this a bit more concrete:

Concurrent InfoWindows from #3127 and UserLocationView showcased that we are able to sync Android Views with the underlying MapView

The syncing is performed in these following lines of code:

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
            mCompassView.update(getDirection());
            mUserLocationView.update();
            for (InfoWindow infoWindow : mInfoWindows) {
                infoWindow.update();
            }
        }

Instead of updating these Views one by one,
I would like to iterate a List<View> an update every entry.
An entry will be updated based on type of interface implemented by the developer.
This interface is responsible for defining update behaviour (eg. following a certain LatLng, expose an update method, ... etc.). We than can also expose add/remove methods for end developers.

@bleege
Copy link
Contributor

bleege commented Jun 2, 2016

While working on a related Marker issue I noticed that BaseMarkerViewOptions has a method called flat(boolean flat) in it that doesn't appear to have any connection with Core GL for adding Markers to the Map. I see this is part of the Google Maps API's MarkerOptions so that's likely why it was implemented. Regardless, I wanted to see what it'd do so I added a counter example to it's test in the TestApp's MarkerViewActivity and it didn't appear to do anything. If that's the case we should reconsider shipping this method (it's new to 4.1.0) as it's one thing to be complete with the Google Maps API but it's another thing to ship a method that doesn't have any function. Maybe I'm missing something though. Thoughts?

screen shot 2016-06-02 at 5 52 59 pm
Test Code

device-2016-06-02-173725
Results

@bleege bleege reopened this Jun 2, 2016
@1ec5
Copy link
Contributor

1ec5 commented Jun 2, 2016

The flat property is intended to make the marker lie flat against a tilted map rather than standing up.

@bleege
Copy link
Contributor

bleege commented Jun 2, 2016

@1ec5 Thanks for clarifying! I re-ran the test and that's what it does.

device-2016-06-02-183106

@1ec5
Copy link
Contributor

1ec5 commented Jun 2, 2016

As it happens, that transformation is incorrect: see #5218.

@p-fischer
Copy link

Hi @tobrun @bleege !
I'm looking into the Mapbox Android API. I'm strongly interested in using it along with MarkerViews. However I'm still having a bit of trouble in understanding the implementation requirements. Can you point me to a documentation/tutorial/example that shows the usage?
The country marker example has helped me quite a bite so far. Is the full source code available? I'm especially interested in seeing the implementation of CountryMarker and CountryMarkerOptions.
I'm thankful for any help. Cheers

@bleege
Copy link
Contributor

bleege commented Jun 8, 2016

@p-fischer Thanks for considering Mapbox! Markers are have undergone a big change in the 4.1.0 code base so confusion isn't surprising. 😄 We won't have official documentation / examples of this ready on mapbox.com until we ship 4.1.0 Final (likely in the next week or so). In the meantime though I'd recommend exploring the code in the TestApp as it's the Android app that we use internally to build these API into the SDK. As such it's always the most up to date reference materials that we have. We're currently working on the release-android-v4.1.0 branch so the TestApp code that's going to be most useful for you can be found here:

TestApp Home Page
https://github.com/mapbox/mapbox-gl-native/tree/release-android-v4.1.0/platform/android/MapboxGLAndroidSDKTestApp

Annotation (which Markers are part of) Examples
https://github.com/mapbox/mapbox-gl-native/tree/release-android-v4.1.0/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation

Marker InfoWindow Examples
https://github.com/mapbox/mapbox-gl-native/tree/release-android-v4.1.0/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow

As always, we produce nightly builds (called SNAPSHOTS) that anyone can use to get the latest updated code from the day! Instructions on how to install them can be found on the Mapbox Android SDK Web site.

@p-fischer
Copy link

Thanks @bleege , I finally could get a marker view to be displayed :)

The issues I'm now having are:

  1. Markers are not drawn immediately. Only when I pan the map, or if I call invalidateViewMarkers on the MarkerViewManager.
  2. The Marker pop ups in the top left corner for a short while before moving to its actual position (might be related to 1?!)
  3. Panning the map is disabled when touching the map on a marker view. This is possible on a regular marker. I think the touch event should be delegated to the MapView if not consumed.
    4.The TestApp crashed as soon as I selected an Activity holding a map. I got an exception saying:

[...]
android.view.InflateException: Binary XML file line #14: Error inflating class com.mapbox.mapboxsdk.maps.MapView
[...]
Caused by: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.mapbox.mapboxsdk.testapp-1/base.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]] couldn't find "libmapbox-gl.so"

@bleege
Copy link
Contributor

bleege commented Jun 8, 2016

@p-fischer Glad that you're up and running! If you can share some of the code that's causing these problems for you we'd be happy to help you figure them out.

@bleege bleege mentioned this issue Jun 8, 2016
14 tasks
@p-fischer
Copy link

Hi @bleege , I basically copied the classes CountryAdapter, CountryMarker, CountryMarkerOptions, CountryMarkerView, and CountryMarkerViewOptions from the TestApp, you pointed me to, and put a map view in a fragment in my app.
I'd like to get the TestApp running (and not crashing) to check the behavior observed in my app (1. - 3.) in the TestApp. Unfortunately, the TestApp crashes with the exception mentioned in 4. Do you have any idea what the exception means?

@Schumi09
Copy link

Schumi09 commented Jun 9, 2016

Hi, depending on what you are trying to achieve it is meanwhile also possible to use some default image adapter and all you have to do is

MarkerViewOptions options = new MarkerViewOptions();
options.icon(iconFactory.fromResource(R.drawable.default_marker));
options.flat(true);
options.position(new LatLng(51.963611, 7.613142));
options.rotation(90.0f);
map.addMarker(options);

so you could also set up a more complex Drawable that you can use as Icon then.

@bleege
Copy link
Contributor

bleege commented Jun 9, 2016

@p-fischer Looking at the log I think the first place to start is to make sure that you're building the proper ABI of Core GL that your device is expecting. This is the libmapbox-gl.so part of the error log. We've got instructions for how to set up your local development environment and specifically running the TestApp that show how to build this part of the Mapbox Android SDK. If you can take a look at those and confirm that things are setup as needed we can then keep looking into problem.

Setup Project Locally Instructions
https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/CONTRIBUTING_OSX.md

Running The TestApp Instructions
https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/CONTRIBUTING_OSX.md#running-the-testapp

@bleege
Copy link
Contributor

bleege commented Jun 9, 2016

@Schumi09 Thanks for showing that as a way to get the default marker to load in the current iteration. So everyone know we're planning on fixing that in #5276.

@p-fischer
Copy link

@bleege I got the TestApp running, thanks. However, I copied the TestApp Module and referenced the SDK the SNAPSHOT as a dependency. For lazy people like me, it might be handy to have the Test App in a separate repository referencing the SDK only through a dependency ;)

I did observe that issue that the map can not be panned starting touch on a marker view on the Test App as well. I think that should be possible because you want the marker view behavior to be consistent with the behavior of the ordinary markers.

@bleege
Copy link
Contributor

bleege commented Jun 10, 2016

@p-fischer Glad that you were able to get things running. I agree it'd be helpful to have the TestApp in a separate repository but it'd also have to continue living in it's current spot as it's how day to day development has to be done because the SDK is an Android Library. FWIW, we're starting to add more examples to the Demo App if you're interested in these types of things. https://github.com/mapbox/mapbox-android-demo

I did observe that issue that the map can not be panned starting touch on a marker view on the Test App as well. I think that should be possible because you want the marker view behavior to be consistent with the behavior of the ordinary markers.

Yep, I was able to recreate this too in the TestApp. As the gif shows below I tried panning underneath the Mapbox logo and it worked and then I tried panning by touching the Mapbox logo and it didn't. Meanwhile this same process worked when I tried panning on the red GL based marker. At first glance it sounds like the gestures are being consumed at the View Annotation level and not being forwarded on to the MapView. We'll keep looking into this as you're correct that the behavior should be consistent with the GL markers.

device-2016-06-10-093049 mp4

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Android Mapbox Maps SDK for Android feature
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants