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

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

Ответ 1

Я основал полное решение, потратив около 3 дней на поиск в google. Мой ответ редактируется из fooobar.com/questions/25754/....

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

Ответ 2

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

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

    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);
        }
    }
    
  • Где бы вы ни захватили ваш 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();
        }
    }
    
  • Внутри моего Map_Fragment (который, в конце концов, будет сидеть внутри FrameLayout в действии, поддерживающем перенос ящика для навигации и фрагментации для переключения представлений)

    mMapFragment = CustomMap_Fragment.newInstance();
    getChildFragmentManager().beginTransaction().replace(R.id.map_container, mMapFragment).commit();
    
  • Теперь, наконец, внутри этого же фрагмента, где я только что получил свою карту, приемник 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);

Ответ 3

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

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

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

Ответ 4

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

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

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