Держите карту в центре независимо от того, где вы зажимаете масштаб на андроиде

Я ищу, чтобы сделать что-то похожее на то, как Uber обрабатывает события масштабирования. Независимо от того, где вы щелкаете на экране, он удерживает карту в центре и масштабируется по центру. Есть ли способ сделать это без какого-либо наложения над фрагментом карты? Или я должен просто отключить события карт, создать наложение над фрагментом карты и обработать все трансляции / другие события из наложения?

Solutions Collecting From Web of "Держите карту в центре независимо от того, где вы зажимаете масштаб на андроиде"

Я основал полное решение, потратив около 3 дней на поиск в Google. Мой ответ редактируется с https://stackoverflow.com/a/32734436/3693334 .

public class CustomMapView extends MapView { private int fingers = 0; private GoogleMap googleMap; private long lastZoomTime = 0; private float lastSpan = -1; private Handler handler = new Handler(); private ScaleGestureDetector scaleGestureDetector; private GestureDetector gestureDetector; public CustomMapView(Context context) { super(context); } public CustomMapView(Context context, AttributeSet attrs) { super(context, attrs); } public CustomMapView(Context context, AttributeSet attrs, int style) { super(context, attrs, style); } public CustomMapView(Context context, GoogleMapOptions options) { super(context, options); } public void init(GoogleMap map) { scaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.OnScaleGestureListener() { @Override public boolean onScale(ScaleGestureDetector detector) { if (lastSpan == -1) { lastSpan = detector.getCurrentSpan(); } else if (detector.getEventTime() - lastZoomTime >= 50) { lastZoomTime = detector.getEventTime(); googleMap.animateCamera(CameraUpdateFactory.zoomBy(getZoomValue(detector.getCurrentSpan(), lastSpan)), 50, null); lastSpan = detector.getCurrentSpan(); } return false; } @Override public boolean onScaleBegin(ScaleGestureDetector detector) { lastSpan = -1; return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { lastSpan = -1; } }); gestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDoubleTapEvent(MotionEvent e) { disableScrolling(); googleMap.animateCamera(CameraUpdateFactory.zoomIn(), 400, null); return true; } }); googleMap = map; } private float getZoomValue(float currentSpan, float lastSpan) { double value = (Math.log(currentSpan / lastSpan) / Math.log(1.55d)); return (float) value; } @Override public boolean dispatchTouchEvent(MotionEvent ev) { gestureDetector.onTouchEvent(ev); switch (ev.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_POINTER_DOWN: fingers = fingers + 1; break; case MotionEvent.ACTION_POINTER_UP: fingers = fingers - 1; break; case MotionEvent.ACTION_UP: fingers = 0; break; case MotionEvent.ACTION_DOWN: fingers = 1; break; } if (fingers > 1) { disableScrolling(); } else if (fingers < 1) { enableScrolling(); } if (fingers > 1) { return scaleGestureDetector.onTouchEvent(ev); } else { return super.dispatchTouchEvent(ev); } } private void enableScrolling() { if (googleMap != null && !googleMap.getUiSettings().isScrollGesturesEnabled()) { handler.postDelayed(new Runnable() { @Override public void run() { googleMap.getUiSettings().setAllGesturesEnabled(true); } }, 50); } } private void disableScrolling() { handler.removeCallbacksAndMessages(null); if (googleMap != null && googleMap.getUiSettings().isScrollGesturesEnabled()) { googleMap.getUiSettings().setAllGesturesEnabled(false); } } } 

И настроить MapFragment

 public class CustomMapFragment extends Fragment { CustomMapView view; Bundle bundle; GoogleMap map; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); bundle = savedInstanceState; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_map, container, false); view = (CustomMapView) v.findViewById(R.id.mapView); view.onCreate(bundle); view.onResume(); map = view.getMap(); view.init(map); MapsInitializer.initialize(getActivity()); return v; } public GoogleMap getMap() { return map; } @Override public void onResume() { super.onResume(); view.onResume(); } @Override public void onPause() { super.onPause(); view.onPause(); } @Override public void onDestroy() { super.onDestroy(); view.onDestroy(); } @Override public void onLowMemory() { super.onLowMemory(); view.onLowMemory(); } } 

Наконец, в вашей деятельности:

 .... <fragment android:id="@+id/map" class="yourpackage.CustomMapFragment" android:layout_width="match_parent" android:layout_height="match_parent"/> ... 

Я уже тестировал на Android 4.1 (API 16) и последний, он работает нормально и плавно. (О API <16, у меня нет какого-либо устройства для тестирования).

Вот код, о котором думает MechEthan.

  1. Сначала вам нужно обнаружить двойное касание на оверлейном представлении.

     public class TouchableWrapper extends FrameLayout { private final GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDoubleTap(MotionEvent e) { //Notify the event bus (I am using Otto eventbus of course) that you have just received a double-tap event on the map, inside the event bus event listener EventBus_Singleton.getInstance().post(new EventBus_Poster("double_tapped_map")); return true; } }; public TouchableWrapper(Context context) { super(context); mGestureDetector = new GestureDetectorCompat(context, mGestureListener); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { mGestureDetector.onTouchEvent(ev); return super.onInterceptTouchEvent(ev); } } 
  2. Где бы вы ни захватили ваш mapView, оберните это mapView внутри TouchableWrapper, созданного выше. Это то, как я это делаю, потому что у меня возникает вопрос о необходимости добавления mapFragment в другой фрагмент, поэтому для этого требуется специальный файл SupportMapFragment

     public class CustomMap_Fragment extends SupportMapFragment { TouchableWrapper mTouchView; public CustomMap_Fragment() { super(); } public static CustomMap_Fragment newInstance() { return new CustomMap_Fragment(); } @Override public View onCreateView(LayoutInflater arg0, ViewGroup arg1, Bundle arg2) { View mapView = super.onCreateView(arg0, arg1, arg2); Fragment fragment = getParentFragment(); if (fragment != null && fragment instanceof OnMapReadyListener) { ((OnMapReadyListener) fragment).onMapReady(); } mTouchView = new TouchableWrapper(getActivity()); mTouchView.addView(mapView); return mTouchView; } public static interface OnMapReadyListener { void onMapReady(); } } 
  3. Внутри моего Map_Fragment (который, в конце концов, будет сидеть внутри FrameLayout в действии, поддерживающем перенос ящиков и фрагментов для переключения видов)

     mMapFragment = CustomMap_Fragment.newInstance(); getChildFragmentManager().beginTransaction().replace(R.id.map_container, mMapFragment).commit(); 
  4. Теперь, наконец, внутри этого же фрагмента, где я только что получил свою карту, приемник EventBus выполнит следующее действие, когда получит «double_tapped_map»:

     @Subscribe public void eventBus_ListenerMethod(AnswerAvailableEvent event) { //Construct a CameraUpdate object that will zoom into the exact middle of the map, with a zoom of currentCameraZoom + 1 unit zoomInUpdate = CameraUpdateFactory.zoomIn(); //Run that with a speed of 400 ms. map.animateCamera(zoomInUpdate, 400, null); } 

Примечание. Чтобы добиться этого, вы отключите zoomGestures на своей карте (это означает, что вы делаете myMap.getUiSettings().setZoomGesturesEnabled(false); . Если вы этого не сделаете, вы сможете быстро нажать на карте очень быстро Вы увидите, что он будет уменьшаться от центра, потому что реализация двойного нажатия точно такая же, как у меня в первом ответе, который я опубликовал, а это то, что они вычитают текущее время с предыдущего времени переключения, поэтому в этом окне вы можете Третий кран, и он не будет инициировать событие события события, а карта google поймает его вместо этого, поэтому отключите жест увеличения.

Но тогда вы увидите, что pinch-in / out больше не будет работать, и вы также должны справиться с пинчем, который я также сделал, но мне нужно еще 1 час, и я не получил время, чтобы сделать это, но 100% я Будет обновлять ответ, когда я это сделаю.

СОВЕТ : Убер также отключил поворот жесты на карте. map.getUiSettings().setRotateGesturesEnabled(false);

Лично я отключил бы только жесты увеличения на карте, обнаружил бы щепотку на оверлее, а затем передал все остальное на карту.

API google-maps v2 API не имеет ничего конкретного для пользовательской обработки масштабирования. Хотя я уверен, что вы можете что-то вводить, использование метода наложения изолирует вас от изменений в картах Google и позволяет вам легче поддерживать других поставщиков карт, если это необходимо.

(Только для полноты: вы могли бы также обрабатывать события изменения камеры и повторного центра, но это было бы жалким и плохим пользовательским интерфейсом.)

Вы можете использовать LatLngBounds, чтобы ограничить перемещение карты с нужной позиции. (Вы можете установить как северо-восточный, так и юго-западный угол границы одной и той же точки).

Пожалуйста, проверьте приведенную ниже ссылку.

https://developers.google.com/maps/documentation/android-api/views

Измененный ответ @hunghd
Я отключил двойное нажатие, чтобы увеличить, только разрешить щепотку

 public class MapViewKeepCenterOnZoom extends MapView { private int fingers = 0; private MapboxMap mMapboxMap; private long lastZoomTime = 0; private float lastSpan = -1; private Handler handler = new Handler(); private ScaleGestureDetector scaleGestureDetector; public MapViewKeepCenterOnZoom(Context context) { super(context); } public MapViewKeepCenterOnZoom(Context context, AttributeSet attrs) { super(context, attrs); } public MapViewKeepCenterOnZoom(Context context, AttributeSet attrs, int style) { super(context, attrs, style); } public void init(MapboxMap map) { scaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.OnScaleGestureListener() { @Override public boolean onScale(ScaleGestureDetector detector) { if (lastSpan == -1) { lastSpan = detector.getCurrentSpan(); } else if (detector.getEventTime() - lastZoomTime >= 50) { lastZoomTime = detector.getEventTime(); mMapboxMap.animateCamera(CameraUpdateFactory.zoomBy( getZoomValue(detector.getCurrentSpan(), lastSpan)), 50, null); lastSpan = detector.getCurrentSpan(); } return false; } @Override public boolean onScaleBegin(ScaleGestureDetector detector) { lastSpan = -1; return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { lastSpan = -1; } }); mMapboxMap = map; mMapboxMap.getUiSettings().setDoubleTapGesturesEnabled(false); } private float getZoomValue(float currentSpan, float lastSpan) { double value = (Math.log(currentSpan / lastSpan) / Math.log(1.55d)); return (float) value; } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_POINTER_DOWN: fingers = fingers + 1; break; case MotionEvent.ACTION_POINTER_UP: fingers = fingers - 1; break; case MotionEvent.ACTION_UP: fingers = 0; break; case MotionEvent.ACTION_DOWN: fingers = 1; break; } if (fingers > 1) { disableScrolling(); } else if (fingers < 1) { enableScrolling(); } if (fingers > 1) { return scaleGestureDetector.onTouchEvent(ev); } else { return super.dispatchTouchEvent(ev); } } private void enableScrolling() { if (mMapboxMap != null && !mMapboxMap.getUiSettings().isScrollGesturesEnabled()) { handler.postDelayed(new Runnable() { @Override public void run() { mMapboxMap.getUiSettings().setZoomGesturesEnabled(true); mMapboxMap.getUiSettings().setScrollGesturesEnabled(true); } }, 50); } } private void disableScrolling() { handler.removeCallbacksAndMessages(null); if (mMapboxMap != null && mMapboxMap.getUiSettings().isScrollGesturesEnabled()) { mMapboxMap.getUiSettings().setZoomGesturesEnabled(false); mMapboxMap.getUiSettings().setScrollGesturesEnabled(false); } } }