PhoneGap + API Google Maps v3: не отображается (вообще) на Android
Ситуация
В настоящее время я разрабатываю приложение для клиента, который хочет иметь интеграцию с Google Maps. Он хочет, чтобы карта показывала маршрут, откуда бы он ни находился в своем офисе.
Я работаю над Windows 8 без какой-либо IDE (используя Sublime Text 2).
Мне удалось заставить его работать a) в моем браузере Chrome локально, б) в эмуляторе Ripple для PhoneGap/Cordova > 2.0.0. Тем не менее, он просто не будет работать на моем Android-телефоне (HTC Sensation) всякий раз, когда я пытаюсь. Это сводит меня с ума, и я вот-вот брошу его и найду другое, "тупое" решение (например, статическая карта или интерфейс geo: url).
Прежде чем я попытался реализовать карту, я запустил полный пример Geolocation PhoneGap, нашел здесь. Я отметил, что мой Android-телефон правильно отображал текущую позицию (lat/long/timestamp и т.д.). Таким образом, я считаю, что на моем телефоне были установлены правильные разрешения (Location → и т.д.).
Проблема
Карты Google вообще не отображаются на моем устройстве Android. Я вижу красный фон (для отладки), поэтому я знаю, что высота и ширина в порядке. Но я не вижу никаких признаков карт google (без кнопок, наложений, сеток или чего-то еще).
Код
HTML-код для загрузки jQuery, Cordova и Maps API v3:
<script type="text/javascript" src="js/jquery-1.10.0.min.js" ></script>
<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?v=3&sensor=true&language=da"></script>
Это HTML, который я использую для размещения карты:
<div id="map-canvas"
style="display:block;
overflow:hidden;
position:relative;
border:1px solid #aaaaaa;
width: 100%;
height: 400px;
background: red;">
</div>
<div id="map-panel" style="width:100%; height:90%; position:relative; "></div>
И вот мой полный JS Google Maps (в собственном файле):
var map,
userPosition,
officeLocation,
directionsDisplay,
directionsService;
google.maps.event.addDomListener(window, 'load', setup);
function setup() {
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
navigator.geolocation.getCurrentPosition(onSuccess, onError, {enableHighAccuracy:true});
}
}
function onSuccess(position) {
userPosition = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
navigator.notification.alert("Found user position");
initializeMaps();
//$('#map-canvas').css({'height': $(window).height()/2, 'width': '99%'});
}
function onError(error) {
navigator.notification.alert("code: " + error.code + ",\n" +
"message: " + error.message);
}
function initializeMaps() {
directionsDisplay = new google.maps.DirectionsRenderer();
directionsService = new google.maps.DirectionsService();
officeLocation = new google.maps.LatLng(55.689403, 12.521281);
var myOptions = {
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: officeLocation
};
map = new google.maps.Map(document.getElementById('map-canvas'), myOptions);
directionsDisplay.setMap(map);
if (userPosition != '') {
var userPosMarker = new google.maps.Marker({
position: userPosition,
map: map,
title: "Din Placering"
});
calculateRoute();
}
else {
navigator.notification.alert("userPosition is null");
}
}
function calculateRoute() {
//navigator.notification.alert("calculateRoute");
var request = {
origin: userPosition,
destination: officeLocation,
travelMode: google.maps.DirectionsTravelMode["DRIVING"]
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setPanel(document.getElementById('map-panel'));
directionsDisplay.setDirections(response);
//navigator.notification.alert("Show directions");
}
else {
navigator.notification.alert("Got status NOT OK from google");
}
});
}
function reloadGoogleMap() {
if (map === null || map === undefined) {
navigator.notification.alert("map is %s", map);
}
else {
var currCenter = map.getCenter();
google.maps.event.trigger(map, "resize");
map.setCenter(currCenter);
map.setZoom(12);
//navigator.notification.alert("reloaded map");
}
}
Это мой код инициализации (помещен в нижней части моего тега):
<script type="text/javascript" charset="utf-8">
//navigator.notification.alert("listen for deviceready");
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
//navigator.notification.alert("device ready");
...
// calls other initialization functions
...
initMaps();
..
}
</script>
И у меня есть эти (среди прочих) в моем AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.INTERNET" />
И у меня есть это в моем файле config.xml(находится в /assets/www/config.xml):
<access origin="*" />
...
<feature name="http://api.phonegap.com/1.0/device"/>
<feature name="http://api.phonegap.com/1.0/geolocation"/>
<feature name="http://api.phonegap.com/1.0/notification"/>
Мне кажется, что мой метод onSuccess никогда не вызывается, но я не вижу никаких предупреждений о том, что позиция пользователя недействительна. На самом деле, единственное уведомление на моем телефоне, которое я вижу, это:
map is %s, undefined
В эмуляторе пульсации я получаю "Найденную позицию пользователя" всякий раз, когда загружаю приложение.
Пожалуйста, помогите!
[EDIT]
Я забыл упомянуть, что я использую Adobe http://build.phonegap.com для фактического создания приложения.
[EDIT2]
Теперь я попытался использовать ключ API Google следующим образом:
<script src="http://maps.google.com/maps/api/js?key=AIzaSyB1uhDdWjtNEl9K35lJtuq5Sw2BjKR8-OM&sensor=false" type="text/javascript"></script>
Но, увы, никаких изменений. Все еще ничего.
[EDIT3]
Вот мой полный androidManifest.xml
:
<code>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:windowSoftInputMode="adjustPan"
package="com.alphastagestudios.danmarksflyttemand" android:versionName="1.0" android:versionCode="2" android:hardwareAccelerated="true">
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:xlargeScreens="true"
android:resizeable="true"
android:anyDensity="true"
/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.RECORD_VIDEO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<application android:icon="@drawable/icon" android:label="@string/app_name"
android:hardwareAccelerated="true"
android:debuggable="true">
<activity android:name="DanmarksFlyttemandApp" android:label="@string/app_name"
android:theme="@android:style/Theme.Black.NoTitleBar"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="17"/>
<permission android:name="com.alphastagestudios.danmarksflyttemand.permission.MAPS_RECEIVE" android:protectionLevel="signature"/>
<uses-permission android:name="com.alphastagestudios.danmarksflyttemand.permission.MAPS_RECEIVE"/>
</manifest>
</code>
[EDIT4]
Теперь я попытался создать минимальный пример с геолокацией PhoneGap и базовой картой google. Я создал его вручную через Eclipse с Cordova v. 2.9.0 (самый новый).
Странно, что пример геолокации PhoneGap сам по себе хорошо работал, но когда я вводил код карты Google, он все переставал работать. Я пробовал этот минимальный пример с ключом google API и без него. Нет разницы.
Это index.html, который я использовал:
<!DOCTYPE html>
<html>
<head>
<title>Device Properties Example</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<style>
body, html, #map-canvas {
width: 100%;
height: 400px;
margin: 0;
padding: 0;
}
</style>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>
<script type="text/javascript" charset="utf-8">
// Wait for device API libraries to load
document.addEventListener("deviceready", onDeviceReady, false);
// device APIs are available
function onDeviceReady() {
navigator.geolocation.getCurrentPosition(onSuccess, onError);
}
// onSuccess Geolocation
function onSuccess(position) {
var element = document.getElementById('geolocation');
element.innerHTML = 'Latitude: ' + position.coords.latitude + '<br />' +
'Longitude: ' + position.coords.longitude + '<br />' +
'Altitude: ' + position.coords.altitude + '<br />' +
'Accuracy: ' + position.coords.accuracy + '<br />' +
'Altitude Accuracy: ' + position.coords.altitudeAccuracy + '<br />' +
'Heading: ' + position.coords.heading + '<br />' +
'Speed: ' + position.coords.speed + '<br />' +
'Timestamp: ' + position.timestamp + '<br />';
initMap();
}
// onError Callback receives a PositionError object
function onError(error) {
alert('code: ' + error.code + '\n' +
'message: ' + error.message + '\n');
}
var map,
userPosition,
officeLocation,
directionsDisplay,
directionsService;
function initMap() {
//directionsDisplay = new google.maps.DirectionsRenderer();
//directionsService = new google.maps.DirectionsService();
officeLocation = new google.maps.LatLng(55.689403, 12.521281);
var myOptions = {
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: officeLocation
};
map = new google.maps.Map(document.getElementById('map-canvas'), myOptions);
//directionsDisplay.setMap(map);
}
</script>
</head>
<body>
<p id="geolocation">Finding geolocation...</p>
<div id="map-canvas"></div>
</body>
</html>
[EDIT5]
Теперь я попытался создать приложение с помощью PhoneGap (Cordova) v. 2.6.0, 2.9.0 и 2.8.1 - никто не работал. Геолокация телефона работает нормально, но карты Google не отображаются. Я вижу только серый цвет по умолчанию, где он должен быть.
Ответы
Ответ 1
Я никогда не решал это напрямую, но я начал использовать Intel App Framework, в котором был пример плагина для карт Google (исходная ссылка мертва, см. ниже). Этот плагин отлично работает для меня, и я смог добавить к нему указания.
Код, который я использовал, приведен ниже. Никаких разрешений или каких-либо других корректировок не требуется. См. Комментарии в коде. Карта будет отображаться независимо от успеха или ошибки геолокации, но если она получит текущую позицию, она добавит направления к карте. В противном случае он показывает статическую точку.
// @author Ian Maffett
// @copyright App Framework 2012
// Modified by [email protected] - 2013
// - Added markers & directions
(function () {
var gmapsLoaded = false; //internal variable to see if the google maps API is available
//We run this on document ready. It will trigger a gmaps:available event if it ready
// or it will include the google maps script for you
$(document).ready(function () {
if(window["google"]&&google.maps){
$(document).trigger("gmaps:available");
gmapsLoaded = true;
return true;
}
var gmaps = document.createElement("script");
gmaps.src = "http://maps.googleapis.com/maps/api/js?v=3.exp&sensor=true&callback=gmapsPluginLoaded";
// Updated API url
$("head").append(gmaps);
window["gmapsPluginLoaded"] = function () {
$(document).trigger("gmaps:available");
gmapsLoaded = true;
}
});
//Local cache of the google maps objects
var mapsCache = {};
//We can invoke this in two ways
//If we pass in positions, we create the google maps object
//If we do not pass in options, it returns the object
// so we can act upon it.
$.fn.gmaps = function (opts) {
if (this.length == 0) return;
if (!opts) return mapsCache[this[0].id];
//Special resize event
if(opts=="resize"&&mapsCache[this[0].id])
{
var map = mapsCache[this[0].id];
var center = map.getCenter();
google.maps.event.trigger(map, "resize");
map.setCenter(center);
map.setZoom(13);
// Extended resize to recenter and reset zoom
return map;
}
//loop through the items and create the new gmaps object
for (var i = 0; i < this.length; i++) {
new gmaps(this[i], opts);
}
};
//This is a local object that gets created from the above.
var gmaps = function (elem, opts) {
var createMap = function () {
var officePos = new google.maps.LatLng(55.689403, 12.521281);
if (!opts || Object.keys(opts).length == 0) {
opts = {
zoom: 13,
center: officePos,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
}
mapsCache[elem.id] = new google.maps.Map(elem, opts);
// Added marker for static location
var officeMarker = new google.maps.Marker({
position: officePos,
map: mapsCache[elem.id],
title: 'Danmarks Flyttemand ApS'
});
// Added custom event listener for availability of userPosition
$(document).one('userPositionAvailable', function(evt, userPos) {
if (userPos != null && userPos != '') {
addDirections(mapsCache[elem.id], userPos);
}
});
}
// Adds directions on gmap from userPos to a fixed position
var addDirections = function(gmap, userPos) {
var officePos = new google.maps.LatLng(55.689403, 12.521281); // fixed position
var userMarker = new google.maps.Marker({
icon: {
path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW,
strokeColor: "green",
scale: 5
},
position: userPos,
map: gmap,
title: 'Your location'
});
var directionsService = new google.maps.DirectionsService();
var directionsDisplay = new google.maps.DirectionsRenderer({suppressMarkers: true});
directionsDisplay.setMap(gmap);
directionsDisplay.setPanel(document.getElementById('googledirections')); // add id here
var request = {
origin: userPos,
destination: officePos,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
}
});
}
//If we try to create a map before it is available
//listen to the event
if (!gmapsLoaded) {
$(document).one("gmaps:available", function () {
createMap()
});
} else {
createMap();
}
}
})(af);
function onGeoSuccess(position) {
var userPos = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
$(document).trigger('userPositionAvailable', userPos);
}
function onGeoError(error) {
navigator.notification.alert('Geolocation error: ' + error.message);
}
function initMaps() {
navigator.geolocation.getCurrentPosition(onGeoSuccess, onGeoError);
var mapOptions = {
zoom: 13,
center: new google.maps.LatLng(55.689403, 12.521281),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
$('#googlemap').gmaps(mapOptions); // update id selector
$('#googlemap-panel').on('loadpanel', function() { // update event for other framework and id selector
$('#googlemap').gmaps('resize'); // update id selector
});
}
[EDIT]
Похоже, что исходная ссылка для примера плагина мертва, поэтому вот быстро найденная новая ссылка (непроверенная и только снятая быстро): Новая демонстрация плагина Google Maps для Intel App-Framework
Ответ 2
У меня была такая же проблема, и оказалось, что я не обновляю белый список после того, как создал проект phonegap. Телефонная связь отклонила URL-адрес Google Maps JS, поэтому он никогда не загружался и не выполнялся.
Например:
<access origin="*.google.com"/>
<access origin="*.googleapis.com"/>
Ответ 3
Для меня оказалось, что указание ширины и высоты div карты в процентах не работает. Указание их как px или vw/vh сработало.
Странная вещь - как и в оригинальном вопросе - заключалась в том, что процентные размеры работали в браузере и на iPhone, но не на Android. Поэтому я был убежден, что проблема была что-то другое - возможно, разрешения или сетевые подключения. Но именно процентные размеры сделали div невидимым.
Ответ 4
По какой-то причине в файле assets/www/directory есть другой файл config.xml
Убедитесь, что вы изменяете правильный файл при добавлении URL-адреса в белый список.
Это правильный вариант:
андроид/Рез/XML/config.xml