Intereting Posts
Формат Android XML для всех устройств (малый / нормальный / большой / большой и т. Д.) Список стран Android с флагами и доступность для получения мобильных кодов iso Просмотр прокрутки не изменяет размер при изменении содержимого детского вида Версии объектов в firebase Как добавить веб-просмотр crosswalk в моем собственном модуле библиотеки Android? Гладкая прокрутка и Fling с помощью NestedScrollView, AppBarLayout и CoordinatorLayout Создание полноэкранного пользовательского Toast Пользовательский шрифт для вкладок ActionBarSherlock Как получить имя Android AVD от имени устройства adb Когда onCreateOptionsMenu происходит в действии ActionBar? Android: как отображать предварительный просмотр камеры с помощью обратного вызова? Службы Google Play – вход – идентификатор ID клиента debug vs release Android Add Native support – неразрешенные jni.h, android / log.h и т. Д. Андроидный веб-просмотр мигает один раз при загрузке данных Выполнение javascript в фоновом режиме с использованием телефонной заставки

Отключение центра MapFragment для анимации, перемещающей как целевой лат / lng, так и уровень масштабирования

У меня есть интерфейс, который имеет MapFragment с прозрачным надписью поверх него. Карта занимает весь экран, а вид – только левая треть экрана. В результате, по умолчанию «центр» карты отключен. Когда кто-то нажимает на маркер, я хочу центрировать этот маркер в полностью видимой области MapFragment (не в центре самого MapFragment).

Поскольку это сложно описать словами, позвольте мне использовать несколько снимков. Предположим, что так выглядит мой пользовательский интерфейс:

Карта по умолчанию

Когда пользователь нажимает на маркер, я хочу как центрировать его, так и увеличивать масштаб, чтобы увидеть его ближе. Без каких-либо корректировок, это то, что вы получите:

Карта с неправильно центрированным маркером

Я хочу, чтобы маркер был центрирован, но в пространстве справа, вот так:

Карта с правильно центрированным маркером

Это очень легко достичь, если вы не меняете уровень масштабирования с помощью проекции карты:

// Offset the target latitude/longitude by preset x/y ints LatLng target = new LatLng(latitude, longitude); Projection projection = getMap().getProjection(); Point screenLocation = projection.toScreenLocation(target); screenLocation.x += offsetX; screenLocation.y += offsetY; LatLng offsetTarget = projection.fromScreenLocation(screenLocation); // Animate to the calculated lat/lng getMap().animateCamera(CameraUpdateFactory.newLatLng(offsetTarget)); 

Однако, если вы одновременно меняете уровень масштабирования, приведенные выше вычисления не работают (поскольку смещения lat / lng изменяются при разных уровнях масштабирования).

Позвольте мне запустить список попыток:

  • Быстрое изменение уровня масштабирования, выполнение вычислений, возврат к исходной позиции камеры, а затем анимация. К сожалению, внезапное изменение камеры (даже если это только на долю секунды), к сожалению, очень очевидно, и я бы хотел избежать мерцания.

  • Наложение двух MapFragments поверх друг друга, если вы выполните вычисления, пока отображается другое. Я обнаружил, что MapFragments на самом деле не построены для наложения друг на друга (на этом пути есть неизбежные ошибки).

  • Изменение размера экрана x / y на разницу в квадрате масштабирования. Теоретически это должно работать, но оно всегда выключено совсем немного (~ .1 широта / долгота, которого достаточно, чтобы уйти).

Есть ли способ вычисления offsetTarget даже при изменении уровня масштабирования?

Solutions Collecting From Web of "Отключение центра MapFragment для анимации, перемещающей как целевой лат / lng, так и уровень масштабирования"

Последняя версия библиотеки игровых сервисов добавляет функцию setPadding() в GoogleMap . Это дополнение будет проецировать все движения камеры в центр офсетной карты. В этом документе также описывается, как ведет себя поведение клавиатуры.

Теперь вопрос, заданный здесь, теперь можно просто решить, добавив левое дополнение, эквивалентное ширине левого макета.

 mMap.setPadding(leftPx, topPx, rightPx, bottomPx); 

Изменить: приведенный ниже код предназначен для устаревших карт v1. В этом SO-ответе говорится, как выполнять аналогичные действия в v2: как получить диапазон широты / долготы в Google Map V2 для Android

Основные методы, которые вам нужны, должны работать в long / lat и процентах вместо пикселей. Прямо сейчас, mapview будет увеличивать масштаб вокруг центра карты, а затем переходить к «повторному» выводу в открытой области. Вы можете получить ширину после увеличения в градусах, коэффициент в некотором смещении экрана, а затем повторно разместить карту. Ниже приведен пример скриншота приведенного ниже кода в действии после нажатия кнопки «+»: перед Введите описание изображения здесь после Введите описание изображения здесь Основной код:

 @Override public void onZoom(boolean zoomIn) { // TODO Auto-generated method stub controller.setZoom(zoomIn ? map.getZoomLevel()+1 : map.getZoomLevel()-1); int bias = (int) (map.getLatitudeSpan()* 1.0/3.0); // a fraction of your choosing map.getLongitudeSpan(); controller.animateTo(new GeoPoint(yourLocation.getLatitudeE6(),yourLocation.getLongitudeE6()-bias)); } 

Весь код:

  package com.example.testmapview; import com.google.android.maps.GeoPoint; import com.google.android.maps.ItemizedOverlay; import com.google.android.maps.MapActivity; import com.google.android.maps.MapController; import com.google.android.maps.MapView; import android.os.Bundle; import android.app.Activity; import android.graphics.drawable.Drawable; import android.view.Menu; import android.widget.ZoomButtonsController; import android.widget.ZoomButtonsController.OnZoomListener; public class MainActivity extends MapActivity { MapView map; GeoPoint yourLocation; MapController controller; ZoomButtonsController zoomButtons; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); map=(MapView)findViewById(R.id.mapview); map.setBuiltInZoomControls(true); controller = map.getController(); zoomButtons = map.getZoomButtonsController(); zoomButtons.setOnZoomListener(new OnZoomListener(){ @Override public void onVisibilityChanged(boolean arg0) { // TODO Auto-generated method stub } @Override public void onZoom(boolean zoomIn) { // TODO Auto-generated method stub controller.setZoom(zoomIn ? map.getZoomLevel()+1 : map.getZoomLevel()-1); int bias = (int) (map.getLatitudeSpan()* 1.0/3.0); // a fraction of your choosing map.getLongitudeSpan(); controller.animateTo(new GeoPoint(yourLocation.getLatitudeE6(),yourLocation.getLongitudeE6()-bias)); } }); //Dropping a pin, setting center Drawable marker=getResources().getDrawable(android.R.drawable.star_big_on); MyItemizedOverlay myItemizedOverlay = new MyItemizedOverlay(marker); map.getOverlays().add(myItemizedOverlay); yourLocation = new GeoPoint(0, 0); controller.setCenter(yourLocation); myItemizedOverlay.addItem(yourLocation, "myPoint1", "myPoint1"); controller.setZoom(5); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_main, menu); return true; } @Override protected boolean isRouteDisplayed() { // TODO Auto-generated method stub return false; } } 

И базовый макет:

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.testmapview" android:versionCode="1" android:versionName="1.0" > <meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="YOURKEYHERE:)"/> <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16" /> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <uses-library android:name="com.google.android.maps"/> <activity android:name="com.example.testmapview.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> 

Если это не полностью ответит на ваш вопрос, сообщите мне, так как это был забавный эксперимент, с которым можно было поиграть.

Я нашел решение, которое действительно подходит для проблемы. Решение состоит в том, чтобы вычислить центр смещения в требуемом масштабировании от смещения оригинала, а затем анимировать камеру карты. Для этого сначала переместите камеру карты на желаемый масштаб, вычислите смещение для этого уровня масштабирования, а затем восстановите исходное масштабирование. После вычисления нового центра мы можем сделать анимацию с помощью CameraUpdateFactory.newLatLngZoom.

Надеюсь, это поможет!

 private void animateLatLngZoom(LatLng latlng, int reqZoom, int offsetX, int offsetY) { // Save current zoom float originalZoom = mMap.getCameraPosition().zoom; // Move temporarily camera zoom mMap.moveCamera(CameraUpdateFactory.zoomTo(reqZoom)); Point pointInScreen = mMap.getProjection().toScreenLocation(latlng); Point newPoint = new Point(); newPoint.x = pointInScreen.x + offsetX; newPoint.y = pointInScreen.y + offsetY; LatLng newCenterLatLng = mMap.getProjection().fromScreenLocation(newPoint); // Restore original zoom mMap.moveCamera(CameraUpdateFactory.zoomTo(originalZoom)); // Animate a camera with new latlng center and required zoom. mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(newCenterLatLng, reqZoom)); } 

Материал проектирования API не так уж и полезен, я столкнулся с той же проблемой и перевернул мой собственный.

 public class SphericalMercatorProjection { public static PointD latLngToWorldXY(LatLng latLng, double zoomLevel) { final double worldWidthPixels = toWorldWidthPixels(zoomLevel); final double x = latLng.longitude / 360 + .5; final double siny = Math.sin(Math.toRadians(latLng.latitude)); final double y = 0.5 * Math.log((1 + siny) / (1 - siny)) / -(2 * Math.PI) + .5; return new PointD(x * worldWidthPixels, y * worldWidthPixels); } public static LatLng worldXYToLatLng(PointD point, double zoomLevel) { final double worldWidthPixels = toWorldWidthPixels(zoomLevel); final double x = point.x / worldWidthPixels - 0.5; final double lng = x * 360; double y = .5 - (point.y / worldWidthPixels); final double lat = 90 - Math.toDegrees(Math.atan(Math.exp(-y * 2 * Math.PI)) * 2); return new LatLng(lat, lng); } private static double toWorldWidthPixels(double zoomLevel) { return 256 * Math.pow(2, zoomLevel); } } 

Используя этот код, вы можете преобразовать карту вокруг новой цели (то есть не центра). Я решил использовать систему опорных точек, где (0,5, 0,5) является значением по умолчанию и представляет собой середину экрана. (0,0) вверху слева (1, 1) слева внизу.

 private LatLng getOffsetLocation(LatLng location, double zoom) { Point size = getSize(); PointD anchorOffset = new PointD(size.x * (0.5 - anchor.x), size.y * (0.5 - anchor.y)); PointD screenCenterWorldXY = SphericalMercatorProjection.latLngToWorldXY(location, zoom); PointD newScreenCenterWorldXY = new PointD(screenCenterWorldXY.x + anchorOffset.x, screenCenterWorldXY.y + anchorOffset.y); newScreenCenterWorldXY.rotate(screenCenterWorldXY, cameraPosition.bearing); return SphericalMercatorProjection.worldXYToLatLng(newScreenCenterWorldXY, zoom); } 

В принципе, вы используете проекцию, чтобы получить координацию XY в мире, а затем сметете эту точку и верните ее в LatLng. Затем вы можете передать это на свою карту. PointD – простой тип, который содержит x, y как удваивает, а также делает поворот.

 public class PointD { public double x; public double y; public PointD(double x, double y) { this.x = x; this.y = y; } public void rotate(PointD origin, float angleDeg) { double rotationRadians = Math.toRadians(angleDeg); this.x -= origin.x; this.y -= origin.y; double rotatedX = this.x * Math.cos(rotationRadians) - this.y * Math.sin(rotationRadians); double rotatedY = this.x * Math.sin(rotationRadians) + this.y * Math.cos(rotationRadians); this.x = rotatedX; this.y = rotatedY; this.x += origin.x; this.y += origin.y; } } 

Обратите внимание, что если вы одновременно обновляете опору и местоположение карты, важно использовать новый подшипник в getOffsetLocation.

Легким способом было бы связать анимацию камеры. Сначала вы обычно увеличиваете изображение до изображения # 2 с помощью animateCamera с 3 параметрами и в CancellableCallback.onFinish вы выполняете расчеты проецирования и оживляете свой код для изменения центра.

Я считаю, что (не проверял) это будет выглядеть очень хорошо, если вы установите некоторые хорошие значения для продолжительности анимации для обеих анимаций. Возможно, вторая анимация должна быть намного быстрее, чем первая. Кроме того, если ваш прозрачный, наложенный вид не отображается, когда маркер не выбран, хорошим UX будет оживлять его слева слева во время второй анимации.

Трудным способом было бы реализовать свою собственную сферическую рекламу Меркатора, как тот, который использует Maps API v2. Если вам требуется только сдвиг долготы, это не должно быть так сложно. Если ваши пользователи могут изменить подшипник, и вы не хотите, чтобы при масштабировании его сбросить до 0, вам также потребуется сдвиг широты, и это, вероятно, будет немного сложнее реализовать.

Я также думаю, что было бы неплохо иметь функциональность Maps API v2, где вы можете получить проекцию для любого CameraPosition и не только текущую, чтобы вы могли отправить запрос функции здесь: http://code.google.com/p/gmaps- Api-issues / issues / list? Can = 2 & q = apitype = Android2 . Вы можете легко использовать его в своем коде для необходимого эффекта.

Для установки маркера мы используем Overlays в Android, In overlay class ==> on draw method помещаем эту вещь

boundCenter (маркер);

И в конструкторе также упоминается вот так:

  public xxxxxxOverlay(Drawable marker, ImageView dragImage, MapView map) { super(boundCenter(marker)); . . . . . } 

У меня почти такая же проблема, как и вы (только мое смещение вертикально), и я пробовал решение, которое не работает должным образом, но может помочь вам (нам) найти лучшее решение.

Я сделал это, чтобы переместить камеру карты, чтобы получить проекцию на цель, и применить там смещение.

Я поместил код в класс Utils, и он выглядит так:

 public static CameraBuilder moveUsingProjection(GoogleMap map, CameraBuilder target, int verticalMapOffset) { CameraPosition oldPosition = map.getCameraPosition(); map.moveCamera(target.build(map)); CameraBuilder result = CameraBuilder.builder() .target(moveUsingProjection(map.getProjection(), map.getCameraPosition().target, verticalMapOffset)) .zoom(map.getCameraPosition().zoom).tilt(map.getCameraPosition().tilt); map.moveCamera(CameraUpdateFactory.newCameraPosition(oldPosition)); return result; } public static LatLng moveUsingProjection(Projection projection, LatLng latLng, int verticalMapOffset) { Point screenPoint = projection.toScreenLocation(latLng); screenPoint.y -= verticalMapOffset; return projection.fromScreenLocation(screenPoint); } 

Затем вы можете использовать CameraBuilder, возвращенный этими методами для анимации, вместо исходной (не смещенной) цели.

Он работает (более или менее), но имеет две большие проблемы:

  • Карта иногда мерцает (это самая большая проблема, потому что она делает взлома непригодной)
  • Это взлом, который может работать сегодня и может не работать завтра (я знаю, что тот же самый алгоритм работает на iOS, например).

Поэтому я не пошел на это, но мне хотелось подумать об альтернативе: если бы у вас была идентичная карта, но скрытая, вы могли бы использовать эту карту для промежуточных вычислений, а затем использовать конечный результат на видимой карте.

Я думаю, что эта альтернатива будет работать, но это ужасно, так как вам нужно будет использовать две идентичные карты с накладными расходами, которые прилагаются к ней, для вас и для устройства.

Как я уже сказал, у меня нет окончательного решения для этого, но, возможно, это немного помогает.

Надеюсь, это поможет!

Я решил проблему, вычислив вектор расстояния, используя значения LatLng двух неподвижных точек на карте, затем создав еще один LatLng, используя позицию Marker, этот вектор и заданное значение отношения. В этом есть много объектов, однако это не зависит от текущего уровня масштабирования и вращения карты и может просто отлично работать для вас!

 // Just a helper function to obtain the device's display size in px Point displaySize = UIUtil.getDisplaySize(getActivity().getWindowManager().getDefaultDisplay()); // The two fixed points: Bottom end of the map & Top end of the map, // both centered horizontally (because I want to offset the camera vertically... // if you want to offset it to the right, for instance, use the left & right ends) Point up = new Point(displaySize.x / 2, 0); Point down = new Point(displaySize.x / 2, displaySize.y); // Convert to LatLng using the GoogleMap object LatLng latlngUp = googleMap.getProjection().fromScreenLocation(up); LatLng latlngDown = googleMap.getProjection().fromScreenLocation(down); // Create a vector between the "screen point LatLng" objects double[] vector = new double[] {latlngUp.latitude - latlngDown.latitude, latlngUp.longitude - latlngDown.longitude}; // Calculate the offset position LatLng latlngMarker = marker.getPosition(); double offsetRatio = 1.0/2.5; LatLng latlngOffset = new LatLng(latlngMarker.latitude + (offsetRatio * vector[0]), latlngMarker.longitude + (offsetRatio * vector[1])); // Move the camera to the desired position CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLng(latlngOffset); googleMap.animateCamera(cameraUpdate); 

Я имел дело с одной и той же задачей и закончил аналогичное решение, как ваш последний вариант:

Изменение размера экрана x / y на разницу в квадрате масштабирования. Теоретически это должно работать, но оно всегда выключено совсем немного (~ .1 широта / долгота, которого достаточно, чтобы уйти).

Но вместо смещения точки деления на 2<<zoomDifference Я вычисляю смещение как различия между значениями долготы и широты. Они double поэтому он дает мне достаточно хорошую точность. Поэтому попробуйте конвертировать ваши смещения в lat / lng, а не ваше местоположение в пиксели, и просто выполните один и тот же расчет.


отредактированный

Наконец, мне удалось решить это по-другому (наиболее подходящий, как я считаю). Map API v2 имеет параметры заполнения . Просто настройте его, чтобы камера полностью не скрывала часть MapView . Затем камера будет отражать центр заполненной области после animateCamera()