Google Maps Android API v2 – обнаружение касания на карте

Я не могу найти пример того, как перехватить привязку карты к новому API Google Maps v2.

Мне нужно знать, когда пользователь прикасается к карте, чтобы остановить stream (центрирование карты вокруг моего текущего местоположения).

@ape написал здесь ответ о том, как перехватить клики по карте, но мне нужно перехватить штрихи, а затем он предложил следующую ссылку в комментарии своего ответа. Как обрабатывать событие onTouch для карты в Google Map API v2? ,

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

Здесь это рабочий код:

Я создал class MySupportMapFragment.java

 import com.google.android.gms.maps.SupportMapFragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class MySupportMapFragment extends SupportMapFragment { public View mOriginalContentView; public TouchableWrapper mTouchView; @Override public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { mOriginalContentView = super.onCreateView(inflater, parent, savedInstanceState); mTouchView = new TouchableWrapper(getActivity()); mTouchView.addView(mOriginalContentView); return mTouchView; } @Override public View getView() { return mOriginalContentView; } } 

Я даже создал class TouchableWrapper.java:

 import android.content.Context; import android.view.MotionEvent; import android.widget.FrameLayout; public class TouchableWrapper extends FrameLayout { public TouchableWrapper(Context context) { super(context); } @Override public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: MainActivity.mMapIsTouched = true; break; case MotionEvent.ACTION_UP: MainActivity.mMapIsTouched = false; break; } return super.dispatchTouchEvent(event); } } 

В макете я заявляю это так:

  

Только для теста в основной деятельности я написал только следующее:

 public class MainActivity extends FragmentActivity { public static boolean mMapIsTouched = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } } 

Вот простое решение для получения местоположения на основе выбора пользователя (опция «Щелчок» на карте)

  googleMap.setOnMapClickListener(new OnMapClickListener() { @Override public void onMapClick(LatLng arg0) { // TODO Auto-generated method stub Log.d("arg0", arg0.latitude + "-" + arg0.longitude); } }); 

Эта функция и многие другие теперь поддерживаются 🙂

это примечание разработчика (выпуск 4636):

В выпуске от августа 2016 года представлен набор новых слушателей смены камеры для запуска, текущего и конечного событий движения камеры. Вы также можете увидеть, почему камера движется, вызвана ли она жестов пользователя, встроенной анимацией API или движениями, контролируемыми разработчиком. Подробнее см. В руководстве по событиям смены камеры: https://developers.google.com/maps/documentation/android-api/events#camera-change-events

Также см. Примечания к выпуску: https://developers.google.com/maps/documentation/android-api/releases#august_1_2016

здесь приведен fragment кода на странице документации

 public class MyCameraActivity extends FragmentActivity implements OnCameraMoveStartedListener, OnCameraMoveListener, OnCameraMoveCanceledListener, OnCameraIdleListener, OnMapReadyCallback { private GoogleMap mMap; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my_camera); SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map); mapFragment.getMapAsync(this); } @Override public void onMapReady(GoogleMap map) { mMap = map; mMap.setOnCameraIdleListener(this); mMap.setOnCameraMoveStartedListener(this); mMap.setOnCameraMoveListener(this); mMap.setOnCameraMoveCanceledListener(this); // Show Sydney on the map. mMap.moveCamera(CameraUpdateFactory .newLatLngZoom(new LatLng(-33.87365, 151.20689), 10)); } @Override public void onCameraMoveStarted(int reason) { if (reason == OnCameraMoveStartedListener.REASON_GESTURE) { Toast.makeText(this, "The user gestured on the map.", Toast.LENGTH_SHORT).show(); } else if (reason == OnCameraMoveStartedListener .REASON_API_ANIMATION) { Toast.makeText(this, "The user tapped something on the map.", Toast.LENGTH_SHORT).show(); } else if (reason == OnCameraMoveStartedListener .REASON_DEVELOPER_ANIMATION) { Toast.makeText(this, "The app moved the camera.", Toast.LENGTH_SHORT).show(); } } @Override public void onCameraMove() { Toast.makeText(this, "The camera is moving.", Toast.LENGTH_SHORT).show(); } @Override public void onCameraMoveCanceled() { Toast.makeText(this, "Camera movement canceled.", Toast.LENGTH_SHORT).show(); } @Override public void onCameraIdle() { Toast.makeText(this, "The camera has stopped moving.", Toast.LENGTH_SHORT).show(); } } 

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

  mapTouchLayer.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Utils.logDebug(TAG, "Map touched!"); timeLastTouched = System.currentTimeMillis(); return false; // Pass on the touch to the map or shadow layer. } }); 

https://developers.google.com/maps/documentation/android/reference/com/google/android/gms/maps/GoogleMap.OnMapClickListener

См. Эту ссылку. Внедрите интерфейс и заполните метод onMapClick() или в зависимости от того, что вам нужно, и установите onMapClickListener в правильную реализацию.

 public class YourActivity extends Activity implements OnMapClickListener { @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); ... my_map.setOnMapClickListener(this) ... } public void onMapClick (LatLng point) { // Do Something } } 

У Gaucho есть отличный ответ, и, увидев множество upvotes, я понял, что может возникнуть необходимость в другой реализации:

Мне нужно было использовать слушателя, чтобы я мог реагировать на ощупь и не нужно постоянно его проверять.

Я помещаю все в один class, который можно использовать следующим образом:

 mapFragment.setNonConsumingTouchListener(new TouchSupportMapFragment.NonConsumingTouchListener() { @Override public void onTouch(MotionEvent motionEvent) { switch (motionEvent.getActionMasked()) { case MotionEvent.ACTION_DOWN: // map is touched break; case MotionEvent.ACTION_UP: // map touch ended break; default: break; // use more cases if needed, for example MotionEvent.ACTION_MOVE } } }); 

где mapfragment должен быть типа TouchSupportMapFragment и в макете xml эта строка нужна:

  

Вот class:

 package de.bjornson.maps; import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import com.google.android.gms.maps.SupportMapFragment; public class TouchSupportMapFragment extends SupportMapFragment { public View mOriginalContentView; public TouchableWrapper mTouchView; private NonConsumingTouchListener mListener; @Override public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { mOriginalContentView = super.onCreateView(inflater, parent, savedInstanceState); mTouchView = new TouchableWrapper(getActivity()); mTouchView.addView(mOriginalContentView); return mTouchView; } @Override public View getView() { return mOriginalContentView; } public void setNonConsumingTouchListener(NonConsumingTouchListener listener) { mListener = listener; } public interface NonConsumingTouchListener { boolean onTouch(MotionEvent motionEvent); } public class TouchableWrapper extends FrameLayout { public TouchableWrapper(Context context) { super(context); } @Override public boolean dispatchTouchEvent(MotionEvent event) { if (mListener != null) { mListener.onTouch(event); } return super.dispatchTouchEvent(event); } } } 
  // Initializing markerPoints = new ArrayList(); // Getting reference to SupportMapFragment of the activity_main SupportMapFragment sfm = (SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.map); // Getting Map for the SupportMapFragment map = sfm.getMap(); // Enable MyLocation Button in the Map map.setMyLocationEnabled(true); // Setting onclick event listener for the map map.setOnMapClickListener(new OnMapClickListener() { @Override public void onMapClick(LatLng point) { // Already two locations if(markerPoints.size()>1){ markerPoints.clear(); map.clear(); } // Adding new item to the ArrayList markerPoints.add(point); // Creating MarkerOptions MarkerOptions options = new MarkerOptions(); // Setting the position of the marker options.position(point); if(markerPoints.size()==1){ options.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)); }else if(markerPoints.size()==2){ options.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED)); } // Add new marker to the Google Map Android API V2 map.addMarker(options); // Checks, whether start and end locations are captured if(markerPoints.size() >= 2){ LatLng origin = markerPoints.get(0); LatLng dest = markerPoints.get(1); //Do what ever you want with origin and dest } } }); 

Для любителей Mono :

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using Android.App; using Android.Content; using Android.OS; using Android.Runtime; using Android.Util; using Android.Views; using Android.Widget; using Android.Gms.Maps; namespace apcurium.MK.Booking.Mobile.Client.Controls { public class TouchableMap : SupportMapFragment { public View mOriginalContentView; public TouchableWrapper Surface; public override View OnCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { mOriginalContentView = base.OnCreateView(inflater, parent, savedInstanceState); Surface = new TouchableWrapper(Activity); Surface.AddView(mOriginalContentView); return Surface; } public override View View { get { return mOriginalContentView; } } } public class TouchableWrapper: FrameLayout { public event EventHandler Touched; public TouchableWrapper(Context context) : base(context) { } public TouchableWrapper(Context context, IAttributeSet attrs) : base(context, attrs) { } public TouchableWrapper(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle) { } public override bool DispatchTouchEvent(MotionEvent e) { if (this.Touched != null) { this.Touched(this, e); } return base.DispatchTouchEvent(e); } } } 

@Gaucho MySupportMapFragment, очевидно, будет использоваться каким-то другим эффектом или активностью (где может быть больше элементов представления, чем fragment карты). Итак, как можно отправить это событие в следующий fragment, где он будет использоваться. Нужно ли нам снова написать интерфейс?

У меня есть более простое решение, отличное от TouchableWrapper и это работает с последней версией play-services-maps:10.0.1 . Это решение использует только события карт и не использует пользовательские представления. Не использует устаревшие функции и, вероятно, будет поддерживать несколько версий.

Сначала вам нужна переменная флага, которая хранится, если карта перемещается с помощью анимации или пользовательского ввода (это указывает, что каждый движок камеры, который не запускается анимацией, запускается пользователем)

 GoogleMap googleMap; boolean movedByApi = false; 

Ваш fragment или активность должны внедрить GoogleMap.OnMapReadyCallback , GoogleMap.CancelableCallback

 public class ActivityMap extends Activity implements OnMapReadyCallback, GoogleMap.CancelableCallback{ ... } 

и это заставляет вас реализовать методы onMapReady , onFinish , onCancel . И объект googleMap в onMapReady должен установить событие-исполнитель для перемещения камеры

 @Override public void onMapReady(GoogleMap mMap) { //instantiate the map googleMap = mMap; [...] // <- set up your map googleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() { @Override public void onCameraMove() { if (movedByApi) { Toast.makeText(ActivityMap.this, "Moved by animation", Toast.LENGTH_SHORT).show(); [...] // <-- do something whe you want to handle api camera movement } else { Toast.makeText(ActivityMap.this, "Moved by user", Toast.LENGTH_SHORT).show(); [...] // <-- do something whe you want to handle user camera movement } } }); } @Override public void onFinish() { //is called when the animation is finished movedByApi = false; } @Override public void onCancel() { //is called when the animation is canceled (the user drags the map or the api changes to a ne position) movedByApi = false; } 

И, наконец, его beter, если вы создадите общую функцию для перемещения карты

 public void moveMapPosition(CameraUpdate cu, boolean animated){ //activate the flag notifying that the map is being moved by the api movedByApi = true; //if its not animated, just do instant move if (!animated) { googleMap.moveCamera(cu); //after the instant move, clear the flag movedByApi = false; } else //if its animated, animate the camera googleMap.animateCamera(cu, this); } 

или просто каждый раз, когда вы перемещаете карту, активируйте флаг перед анимацией

 movedByApi = true; googleMap.animateCamera(cu, this); 

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

Давайте будем гением компьютера.