2015-03-30 1 views
11

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

+0

Пожалуйста звезда Эта функция запроса, чтобы позволить Google знать, что нам нужно: https://issuetracker.google.com/issues/69795937 – dan

ответ

18

Я основал полное решение, потратив около 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, у меня нет устройства для тестирования).

+3

Что такое чувство математики .log (1.55d) '? И всего расчета масштабирования '(Math.log (currentSpan/lastSpan)/Math.log (1.55d))'? – NOSTRA

+0

Я использовал это .., но у него есть проблема с «двойным нажатием удерживать слайд вверх или вниз». –

+0

эта работа не работает на android 6.0 и выше –

2

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

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

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

+0

Спасибо за ответ. Да, это то, что я думал, что мне нужно будет сделать. Я попробовал несколько хакерских способов сделать повторное центрирование, как вы сказали. И ты совершенно прав, сумасшедший. haha – SoCmodder

6

Вот код для того, что 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);

+0

Можете добавить несколько подробностей. Я добавил карту в свою деятельность. Используя [this] (http://stackoverflow.com/questions/27504606/how-to-implement-draggable-map-like-uber-android/30346234#30346234), и я хочу добавить в нее вышеприведенную функцию. – Shvet

+0

В последнее время я решил эту проблему лучше, я отредактирую свой ответ, чтобы отразить все эти изменения, но, к сожалению, сегодня я не могу – Odaym

+0

Хорошо. Если вы можете помочь, это будет хорошо. Я застрял в этой точке прямо сейчас :) – Shvet

0

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

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

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

Смежные вопросы