moveCamera với CameraUpdateFactory.newLatLngBounds bị lỗi


96

Tôi đang sử dụng API Google Maps mới của Android .

Tôi tạo một hoạt động bao gồm một MapFragment. Trong hoạt động, onResumetôi đặt các điểm đánh dấu vào đối tượng GoogleMap và sau đó xác định một hộp giới hạn cho bản đồ bao gồm tất cả các điểm đánh dấu.

Điều này đang sử dụng mã giả sau:

LatLngBounds.Builder builder = new LatLngBounds.Builder();
while(data) {
   LatLng latlng = getPosition();
   builder.include(latlng);
}
CameraUpdate cameraUpdate = CameraUpdateFactory
   .newLatLngBounds(builder.build(), 10);
map.moveCamera(cameraUpdate);

Lệnh gọi map.moveCamera()khiến ứng dụng của tôi gặp sự cố với ngăn xếp sau:

Caused by: java.lang.IllegalStateException: 
    Map size should not be 0. Most likely, layout has not yet 

    at maps.am.r.b(Unknown Source)
    at maps.y.q.a(Unknown Source)
    at maps.y.au.a(Unknown Source)
    at maps.y.ae.moveCamera(Unknown Source)
    at com.google.android.gms.maps.internal.IGoogleMapDelegate$Stub
        .onTransact(IGoogleMapDelegate.java:83)
    at android.os.Binder.transact(Binder.java:310)
    at com.google.android.gms.maps.internal.IGoogleMapDelegate$a$a
        .moveCamera(Unknown Source)
    at com.google.android.gms.maps.GoogleMap.moveCamera(Unknown Source)
    at ShowMapActivity.drawMapMarkers(ShowMapActivity.java:91)
    at ShowMapActivity.onResume(ShowMapActivity.java:58)
    at android.app.Instrumentation
        .callActivityOnResume(Instrumentation.java:1185)
    at android.app.Activity.performResume(Activity.java:5182)
    at android.app.ActivityThread
        .performResumeActivity(ActivityThread.java:2732)

Nếu - thay vì newLatLngBounds()phương pháp nhà máy, tôi sử dụng newLatLngZoom()phương pháp thì bẫy tương tự không xảy ra.

Đây có phải là onResumenơi tốt nhất để vẽ các điểm đánh dấu vào đối tượng GoogleMap hay tôi nên vẽ các điểm đánh dấu và đặt vị trí máy ảnh ở một nơi khác?

Câu trả lời:


133

Bạn có thể sử dụng phương thức newLatLngBounds đơn giản trong OnCameraChangeListener . Tất cả sẽ hoạt động hoàn hảo và bạn không cần phải tính toán kích thước màn hình. Sự kiện này xảy ra sau khi tính toán kích thước bản đồ (theo tôi hiểu).

Thí dụ:

map.setOnCameraChangeListener(new OnCameraChangeListener() {

    @Override
    public void onCameraChange(CameraPosition arg0) {
        // Move camera.
        map.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 10));
        // Remove listener to prevent position reset on camera move.
        map.setOnCameraChangeListener(null);
    }
});

1
Điều này hoạt động vì setOnCameraChangeListener được đảm bảo chạy ít nhất một lần, sau khi Bản đồ đã được bố trí? Đây là bằng chứng trong tương lai?
Glenn Bech

3
@SteelRat Đó là lý do tại sao tôi không sử dụng giải pháp này. Bạn không bao giờ biết khi nào google thay đổi điều này, hoặc ngay cả khi nó hoạt động theo cách đó trên tất cả các phiên bản hoặc thiết bị Android.
Glenn Bech

1
@SteelRat Tôi đồng ý, nhưng nó có thể được gọi nhiều hơn một lần trong thời gian tồn tại của hoạt động? Tôi chỉ đưa ra lập luận rằng ngay cả khi nó hoạt động trong trường hợp này, nó không phải là một giải pháp rất thanh lịch. Tôi nghĩ OnGlobalLayoutListener / Treelistener là một cách đúng đắn hơn để giải quyết vấn đề này.
Glenn Bech

4
có thể thêm map.moveCamera (CameraUpdateFactory.scrollBy (1, 1)); sau đoạn mã trên sẽ thực hiện thủ thuật?
NGÀY

3
setOnCameraChangeListenerhiện không được dùng nữa
osrl 12/12/16

56

Những câu trả lời đó rất ổn, nhưng tôi sẽ đi theo một cách tiếp cận khác, một cách đơn giản hơn. Nếu phương pháp chỉ hoạt động sau khi bản đồ được sắp xếp, chỉ cần đợi nó:

map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
    @Override
    public void onMapLoaded() {
        map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30));
    }
});

1
Sau nhiều thử nghiệm, tôi nhận thấy rằng tôi cần triển khai kết hợp câu trả lời này và câu trả lời của Daniele ở trên. Đôi khi bản đồ chưa được tải, đôi khi bố cục vẫn chưa được hoàn thành. Tôi không thể di chuyển máy ảnh như mong muốn cho đến khi CẢ HAI trong số đó xảy ra.
RobP

2
Cảnh báo cho phương pháp này, từ tài liệu: "Sự kiện này sẽ không kích hoạt nếu bản đồ không bao giờ tải do sự cố kết nối hoặc nếu bản đồ liên tục thay đổi và không bao giờ tải xong do người dùng liên tục tương tác với bản đồ ." (nhấn mạnh của tôi).
stkent

1
Việc này sẽ trì hoãn lâu hơn mức cần thiết vì nó đợi các ô bản đồ tải ở vị trí sai trước khi di chuyển đến vị trí chính xác.
John Patterson

1
Hầu hết thời gian phải mất từ ​​3-10 giây để khai hỏa, điều này không thể chấp nhận được.
Nima G

Đây là nơi để di chuyển máy ảnh. Tôi khuyên bạn nên sử dụng map.animateCamera thay vì map.moveCamera
Bharat Dodeja

51

OK, tôi đã giải quyết vấn đề này. Như đã nêu ở đây rằng API không thể được sử dụng bố cục trước.

API chính xác để sử dụng được mô tả là:

Lưu ý: Chỉ sử dụng phương pháp đơn giản hơn newLatLngBounds (ranh giới, phần đệm) để tạo CameraUpdate nếu nó sẽ được sử dụng để di chuyển máy ảnh sau khi bản đồ đã được bố trí. Trong khi bố trí, API sẽ tính toán các ranh giới hiển thị của bản đồ cần thiết để chiếu chính xác hộp giới hạn. Để so sánh, bạn có thể sử dụng CameraUpdate được trả về bởi phương thức phức tạp hơn newLatLngBounds (ranh giới, chiều rộng, chiều cao, phần đệm) bất kỳ lúc nào, ngay cả trước khi bản đồ đã trải qua bố cục, vì API tính toán ranh giới hiển thị từ các đối số mà bạn vượt qua.

Để khắc phục sự cố, tôi đã tính toán kích thước màn hình của mình và cung cấp chiều rộng và chiều cao cho

public static CameraUpdate newLatLngBounds(
    LatLngBounds bounds, int width, int height, int padding)

Sau đó, điều này cho phép tôi chỉ định bố cục trước hộp giới hạn.


18
Nhưng nếu bản đồ không chiếm toàn bộ màn hình thì sao?
theblang

Trong trường hợp của tôi, tôi chỉ giả định một màn hình lớn hơn so với thực tế và cần phải tính toán phần đệm cẩn thận hơn để thực tế nó không quá lớn. Một lý do đơn giản hơn nhiều.
rfay

2
Bạn có thể chỉ cách tính nó dựa trên kích thước màn hình?
Daniel Gomez Rico

Điều này không xảy ra nhưng chiều rộng và chiều cao được sử dụng gần với các phỏng đoán thuần túy, khu vực chính xác được vẽ thực sự không thể đoán trước.
Vince

34

Giải pháp đơn giản hơn thế….

// Pan to see all markers in view.
try {
    this.gmap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
} catch (IllegalStateException e) {
    // layout not yet initialized
    final View mapView = getFragmentManager()
       .findFragmentById(R.id.map).getView();
    if (mapView.getViewTreeObserver().isAlive()) {
        mapView.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {
            @SuppressWarnings("deprecation")
            @SuppressLint("NewApi")
            // We check which build version we are using.
            @Override
            public void onGlobalLayout() {
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                    mapView.getViewTreeObserver()
                        .removeGlobalOnLayoutListener(this);
                } else {
                    mapView.getViewTreeObserver()
                        .removeOnGlobalLayoutListener(this);
                }
                gmap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
            }
        });
    }
}

Bắt IllegalStateExceptionvà sử dụng trình nghe chung trên chế độ xem thay thế. Đặt trước kích thước giới hạn (bố cục trước) bằng các phương pháp khác có nghĩa là bạn phải tính kích thước của CHẾ ĐỘ XEM, không phải thiết bị màn hình. Chúng chỉ khớp nếu bạn xem toàn màn hình với bản đồ của mình và không sử dụng các mảnh vỡ.


Cho đến bây giờ tôi đã sử dụng đoạn mã nhỏ của bạn, nhưng nó vẫn không thành công ... câu trả lời chính xác là câu đầu tiên :-)
cesards

1
Làm thế nào / khi nó thất bại? không bao giờ có một vấn đề với điều này. Câu trả lời đầu tiên chỉ chuyển một kích thước được mã hóa cứng cho nó như một dự phòng. "Nó hoạt động" như trong "nó không sụp đổ nữa" nhưng nó không làm điều tương tự.
Daniele Segato

@ andrea.spot Tôi không nhớ tại sao mình lại nói đơn giản hơn. Tôi đoán giải pháp được chấp nhận đã được chỉnh sửa trong khi đó. Các hiện giải pháp chấp nhận giả định bản đồ của bạn mất tất cả các màn hình, mà không phải là luôn luôn đúng. Nếu bạn không rơi vào trường hợp đó, bạn cần phải biết kích thước của bản đồ hoặc sử dụng phương pháp của tôi hoặc một cái gì đó tương tự.
Daniele Segato

cũng kiểm tra câu trả lời bên dưới của Xingang Huang, nó bổ sung một chút cho nó.
Daniele Segato

15

Đây là bản sửa lỗi của tôi, chỉ cần đợi cho đến khi bản đồ được tải trong trường hợp đó.

final int padding = getResources().getDimensionPixelSize(R.dimen.spacing);    

try {
    mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(pCameraBounds, padding));
} catch (IllegalStateException ise) {

    mMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {

        @Override
        public void onMapLoaded() {
            mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(pCameraBounds, padding));
        }
    });
}

+1 đơn giản và hiệu quả
Shid

14

Việc thêm và xóa các điểm đánh dấu có thể được hoàn thành trước khi bố trí, nhưng không thể di chuyển máy ảnh (ngoại trừ việc sử dụng newLatLngBounds(boundary, padding)như đã nêu trong câu trả lời của OP).

Có lẽ nơi tốt nhất để thực hiện cập nhật máy ảnh ban đầu là sử dụng một lần chụp OnGlobalLayoutListenernhư được hiển thị trong mã mẫu của Google , ví dụ: xem đoạn trích sau setUpMap()trong MarkerDemoActivity.java :

// Pan to see all markers in view.
// Cannot zoom to bounds until the map has a size.
final View mapView = getSupportFragmentManager()
    .findFragmentById(R.id.map).getView();
if (mapView.getViewTreeObserver().isAlive()) {
    mapView.getViewTreeObserver().addOnGlobalLayoutListener(
    new OnGlobalLayoutListener() {
        @SuppressLint("NewApi") // We check which build version we are using.
        @Override
        public void onGlobalLayout() {
            LatLngBounds bounds = new LatLngBounds.Builder()
                    .include(PERTH)
                    .include(SYDNEY)
                    .include(ADELAIDE)
                    .include(BRISBANE)
                    .include(MELBOURNE)
                    .build();
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
              mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            } else {
              mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
            mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
        }
    });
}


Tôi gặp lỗi này khi thay đổi hướng. Tôi cũng tìm thấy điều này từ mã mẫu nhưng điều này không hiệu quả với tôi. Tôi vẫn gặp lỗi tương tự.
osrl 12/12/16

14

Câu trả lời được chấp nhận sẽ không phù hợp với tôi vì tôi phải sử dụng cùng một mã ở các vị trí khác nhau trong ứng dụng của mình.

Thay vì đợi máy ảnh thay đổi, tôi đã tạo ra một giải pháp đơn giản dựa trên gợi ý của Lee về việc chỉ định kích thước bản đồ. Đây là trường hợp bản đồ của bạn có kích thước của màn hình.

// Gets screen size
int width = getResources().getDisplayMetrics().widthPixels;
int height = getResources().getDisplayMetrics().heightPixels;
// Calls moveCamera passing screen size as parameters
map.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), width, height, 10));

Hy vọng nó sẽ giúp người khác!


8

Câu trả lời được chấp nhận, như đã chỉ ra trong các bình luận, là một chút hacky. Sau khi triển khai nó, tôi nhận thấy số lượng IllegalStateExceptionnhật ký trong phân tích vượt quá mức chấp nhận được . Giải pháp tôi đã sử dụng là thêm một OnMapLoadedCallbacktrong đó CameraUpdateđược thực hiện. Cuộc gọi lại được thêm vào GoogleMap. Sau khi bản đồ của bạn tải đầy đủ, quá trình cập nhật máy ảnh sẽ được thực hiện.

Điều này làm cho bản đồ hiển thị ngắn gọn chế độ xem thu nhỏ (0,0) trước khi thực hiện cập nhật máy ảnh. Tôi cảm thấy rằng điều này dễ chấp nhận hơn là gây ra sự cố hoặc dựa vào hành vi không có giấy tờ.


5
 map.moveCamera(CameraUpdateFactory.newLatLngZoom(bounds.getCenter(),10));

sử dụng cái này nó đã làm việc cho tôi


Thao tác này chỉ căn giữa bản đồ nhưng không tự động điều chỉnh thu phóng theo giới hạn.
Giovanni Di Gregorio

Chúng tôi có thể tính toán ranh giới cho nhiều người đạt chuẩn hơn bằng cách sử dụng "LatLngBounds.Builder".
Ramesh Bhupathi

@RameshBhupathi nhưng nó sẽ vẫn là một chế độ xem bản đồ với một trung tâm và một thu phóng.
Tima

4

Trong một số trường hợp hiếm hoi, bố cục MapView đã hoàn thành, nhưng bố cục GoogleMap chưa hoàn thành, ViewTreeObserver.OnGlobalLayoutListenerbản thân nó không thể dừng sự cố. Tôi đã gặp sự cố với gói dịch vụ Google Play phiên bản 5084032. Trường hợp hiếm gặp này có thể do sự thay đổi động của khả năng hiển thị trên MapView của tôi.

Để giải quyết vấn đề này, tôi đã nhúng GoogleMap.OnMapLoadedCallbackvào onGlobalLayout(),

if (mapView.getViewTreeObserver().isAlive()) {
    mapView.getViewTreeObserver().addOnGlobalLayoutListener(
    new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        @SuppressWarnings("deprecation")
        public void onGlobalLayout() {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            } else {
                mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
            try {
                map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 5));
            } catch (IllegalStateException e) {
                map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
                    @Override
                    public void onMapLoaded() {
                        Log.d(LOG_TAG, "move map camera OnMapLoadedCallback");
                        map.moveCamera(CameraUpdateFactory
                            .newLatLngBounds(bounds, 5));
                    }
                });
            }
        }
    });
}

Thú vị: Nếu tôi cấu trúc lại mapView.getViewTreeObserver()thành một biến cục bộ, lỗi sau sẽ xảy ra: "IllegalStateException: ViewTreeObserver này không tồn tại, hãy gọi lại getViewTreeObserver ()" . Bạn đã thử cái này chưa?
JJD

Nếu mapView.getViewTreeObserver (). IsAlive () là sai, tôi sẽ đặt thêm một dự phòng nữa cho map.setOnMapLoadedCallback ()
southherton

4

Tôi đã sử dụng cách tiếp cận khác phù hợp với các phiên bản gần đây của Google Maps SDK (9.6+) và dựa trên onCameraIdleListener . Như tôi thấy cho đến nay, phương thức gọi lại onCameraIdleluôn được gọi sau onMapReady. Vì vậy, cách tiếp cận của tôi trông giống như đoạn mã này (xem xét nó được đưa vào Activity):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // set content view and call getMapAsync() on MapFragment
}

@Override
public void onMapReady(GoogleMap googleMap) {
    map = googleMap;
    map.setOnCameraIdleListener(this);
    // other initialization stuff
}

@Override
public void onCameraIdle() {
    /* 
       Here camera is ready and you can operate with it. 
       you can use 2 approaches here:

      1. Update the map with data you need to display and then set
         map.setOnCameraIdleListener(null) to ensure that further events
         will not call unnecessary callback again.

      2. Use local boolean variable which indicates that content on map
         should be updated
    */
}

4

Tôi đã tạo một cách để kết hợp hai lệnh gọi lại: onMapReady và onGlobalLayout thành một có thể quan sát duy nhất sẽ chỉ phát ra khi cả hai sự kiện đã được kích hoạt.

https://gist.github.com/abhaysood/e275b3d0937f297980d14b439a8e0d4a


1
Trên thực tế, đó là cách sạch sẽ nhất để giải quyết vấn đề này. Tôi đang làm việc này trong một thời gian dài và không có lỗi nào được trả lại trong phân tích ngay cả trên ứng dụng 1.000 người dùng mỗi ngày. +1
Skyle

2

Được rồi, tôi đang gặp phải vấn đề tương tự. Tôi có phân đoạn của mình với SupportmapFragment, ABS và ngăn điều hướng. Những gì tôi đã làm là:

public void resetCamera() {

    LatLngBounds.Builder builderOfBounds = new LatLngBounds.Builder();
    // Set boundaries ...
    LatLngBounds bounds = builderOfBounds.build();
    CameraUpdate cu;
    try{
        cu = CameraUpdateFactory.newLatLngBounds(bounds,10);
        // This line will cause the exception first times 
        // when map is still not "inflated"
        map.animateCamera(cu); 
        System.out.println("Set with padding");
    } catch(IllegalStateException e) {
        e.printStackTrace();
        cu = CameraUpdateFactory.newLatLngBounds(bounds,400,400,0);
        map.animateCamera(cu);
        System.out.println("Set with wh");
    }

    //do the rest...
}

Và, nhân tiện, tôi đang gọi resetCamera()từ onCreateViewsau khi thổi phồng và trước khi quay lại.
Những gì điều này làm là bắt ngoại lệ lần đầu tiên (trong khi bản đồ "có kích thước" như một cách nói ...) và sau đó, những lần khác tôi cần đặt lại máy ảnh, bản đồ đã có kích thước và thực hiện điều đó thông qua đệm.

Vấn đề được giải thích trong tài liệu , nó nói:

Không thay đổi máy ảnh với bản cập nhật máy ảnh này cho đến khi bản đồ đã được bố trí (để phương pháp này xác định chính xác hộp giới hạn và mức thu phóng thích hợp, bản đồ phải có kích thước). Nếu không một IllegalStateExceptionsẽ được ném. Nó KHÔNG đủ để bản đồ có sẵn (tức là getMap()trả về một đối tượng không rỗng); chế độ xem chứa bản đồ cũng phải được bố trí sao cho các kích thước của nó đã được xác định. Nếu bạn không thể chắc chắn rằng điều này đã xảy ra, hãy sử dụng newLatLngBounds(LatLngBounds, int, int, int)thay thế và cung cấp các kích thước của bản đồ theo cách thủ công.

Tôi nghĩ đó là một giải pháp khá tốt. Hy vọng nó sẽ giúp một ai đó.


1
có bất kỳ người nghe nào để nghe khi bố cục được tải đầy đủ không. ?
Sagar Nayak

Ngoại lệ được nêu ra trong newLatLngBounds()hoặc trong animateCamera(), hoặc trong cả hai? Không phải nhánh bắt sẽ dẫn đến một ngoại lệ mới?
CoolMind

1

Nếu bạn cần đợi các tương tác của người dùng có thể bắt đầu, hãy sử dụng OnMapLoadedCallbacknhư được mô tả trong các câu trả lời trước. Tuy nhiên, nếu tất cả những gì bạn cần là cung cấp vị trí mặc định cho bản đồ, thì không cần bất kỳ giải pháp nào được nêu trong các câu trả lời đó. Cả hai MapFragmentMapViewcó thể chấp nhận một GoogleMapOptionslúc bắt đầu có thể cung cấp vị trí mặc định. Mẹo duy nhất là không bao gồm chúng trực tiếp trong bố cục của bạn vì hệ thống sẽ gọi chúng mà không có tùy chọn sau đó nhưng để khởi tạo chúng động.

Sử dụng cái này trong bố cục của bạn:

<FrameLayout
    android:id="@+id/map"
    android:name="com.google.android.gms.maps.SupportMapFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

và thay thế phân đoạn trong onCreateView():

GoogleMapOptions options = new GoogleMapOptions();
options.camera(new CameraPosition.Builder().target(location).zoom(15).build());
// other options calls if required

SupportMapFragment fragment = (SupportMapFragment) getFragmentManager().findFragmentById(R.id.map);
if (fragment == null) {
  FragmentTransaction transaction = getFragmentManager().beginTransaction();
  fragment = SupportMapFragment.newInstance(options);
  transaction.replace(R.id.map, fragment).commit();
  getFragmentManager().executePendingTransactions();
}
if (fragment != null)
  GoogleMap map = fragment.getMap();

Ngoài việc bắt đầu nhanh hơn, sẽ không có bản đồ thế giới được hiển thị đầu tiên và máy ảnh di chuyển thứ hai. Bản đồ sẽ bắt đầu trực tiếp với vị trí được chỉ định.


0

Một cách tiếp cận khác sẽ giống như (Giả sử chế độ xem trên cùng của bạn là FrameLayout được đặt tên rootContainer, mặc dù nó sẽ hoạt động miễn là bạn luôn chọn vùng chứa trên cùng của mình bất kể loại hoặc tên nó có):

((FrameLayout)findViewById(R.id.rootContainer)).getViewTreeObserver()
    .addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        public void onGlobalLayout() {
            layoutDone = true;
        }
    });

Sửa đổi các chức năng máy ảnh của bạn để làm việc chỉ khi layoutDonetruesẽ giải quyết tất cả các vấn đề của mình mà không cần phải thêm chức năng bổ sung hoặc dây lên logic để layoutListenerxử lý.


0

Tôi thấy cách này hoạt động và đơn giản hơn các giải pháp khác:

private void moveMapToBounds(final CameraUpdate update) {
    try {
        if (movedMap) {
            // Move map smoothly from the current position.
            map.animateCamera(update);
        } else {
            // Move the map immediately to the starting position.
            map.moveCamera(update);
            movedMap = true;
        }
    } catch (IllegalStateException e) {
        // Map may not be laid out yet.
        getWindow().getDecorView().post(new Runnable() {
            @Override
            public void run() {
                moveMapToBounds(update);
            }
        });
    }
}

Thao tác này sẽ thử lại cuộc gọi sau khi bố cục đã chạy. Có thể bao gồm một sự an toàn để tránh một vòng lặp vô hạn.


0

Tại sao không chỉ sử dụng một cái gì đó như thế này:

CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(builder.build(), padding);
try {
    map.moveCamera(cameraUpdate);
} catch (Exception e) {
    int width = getResources().getDisplayMetrics().widthPixels;
    int height = getResources().getDisplayMetrics().heightPixels;
    cameraUpdate = CameraUpdateFactory.newLatLngBounds(builder.build(), width, height, padding);
    map.moveCamera(cameraUpdate);
}

Tại sao newLatLngBounds(builder.build(), padding)không bên trong try-catch? Ngoại lệ này có xảy ra trong moveCamera()không?
CoolMind

0

Có một lớp người trợ giúp trong kho Google Maps mà bạn có thể tận dụng - lớp này đợi cả bố cục và bản đồ sẵn sàng trước khi thông báo gọi lại với GoogleMap:

Nguồn gốc ở đây:

https://github.com/googlemaps/android-samples/blob/7ee737b8fd6d39c77f8b3716ba948e1ce3730e61/ApiDemos/java/app/src/main/java/com/example/mapdemo/OnMapAndViewReadyListener.java

Có một triển khai Kotlin quá:

https://github.com/googlemaps/android-samples/blob/master/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/OnMapAndViewReadyListener.kt

public class OnMapAndViewReadyListener implements OnGlobalLayoutListener, OnMapReadyCallback {

/** A listener that needs to wait for both the GoogleMap and the View to be initialized. */
public interface OnGlobalLayoutAndMapReadyListener {
    void onMapReady(GoogleMap googleMap);
}

private final SupportMapFragment mapFragment;
private final View mapView;
private final OnGlobalLayoutAndMapReadyListener devCallback;

private boolean isViewReady;
private boolean isMapReady;
private GoogleMap googleMap;

public OnMapAndViewReadyListener(
        SupportMapFragment mapFragment, OnGlobalLayoutAndMapReadyListener devCallback) {
    this.mapFragment = mapFragment;
    mapView = mapFragment.getView();
    this.devCallback = devCallback;
    isViewReady = false;
    isMapReady = false;
    googleMap = null;

    registerListeners();
}

private void registerListeners() {
    // View layout.
    if ((mapView.getWidth() != 0) && (mapView.getHeight() != 0)) {
        // View has already completed layout.
        isViewReady = true;
    } else {
        // Map has not undergone layout, register a View observer.
        mapView.getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    // GoogleMap. Note if the GoogleMap is already ready it will still fire the callback later.
    mapFragment.getMapAsync(this);
}

@Override
public void onMapReady(GoogleMap googleMap) {
    // NOTE: The GoogleMap API specifies the listener is removed just prior to invocation.
    this.googleMap = googleMap;
    isMapReady = true;
    fireCallbackIfReady();
}

@SuppressWarnings("deprecation")  // We use the new method when supported
@SuppressLint("NewApi")  // We check which build version we are using.
@Override
public void onGlobalLayout() {
    // Remove our listener.
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
    } else {
        mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }
    isViewReady = true;
    fireCallbackIfReady();
}

private void fireCallbackIfReady() {
    if (isViewReady && isMapReady) {
        devCallback.onMapReady(googleMap);
    }
}
}

0

Như đã nêu trong OnCameraChangeListener () không được dùng nữa , setOnCameraChangeListenerhiện không được dùng nữa. Vì vậy, bạn nên thay thế nó bằng một trong ba mtehods:

  • GoogleMap.OnCameraMoveStartedListener
  • GoogleMap.OnCameraMoveListener
  • GoogleMap.OnCameraIdleListener

Trong trường hợp của tôi, tôi đã sử dụng OnCameraIdleListenervà bên trong tôi đã loại bỏ nó vì nó được gọi đi gọi lại trên bất kỳ chuyển động nào.

googleMap.setOnCameraIdleListener {
    googleMap.setOnCameraIdleListener(null) // It removes the listener.
    googleMap.moveCamera(track)
    googleMap.cameraPosition
    clusterManager!!.cluster()
    // Add another listener to make ClusterManager correctly zoom clusters and markers.
    googleMap.setOnCameraIdleListener(clusterManager)
}

CẬP NHẬT

Tôi đã xóa googleMap.setOnCameraIdleListenertrong dự án của mình, vì đôi khi nó không được gọi khi bản đồ được hiển thị, nhưng được giữ lại googleMap.setOnCameraIdleListener(clusterManager).


Cảm ơn bạn đã trả lời. Tôi đã tìm ra giải pháp gần giống như thế này.
Arslan Maqbool

@ArslanMaqbool, cảm ơn! Tôi đang xử lý ngay bây giờ và sẽ biết ơn bạn nếu bạn chia sẻ biến thể của mình.
CoolMind

niềm vui của tôi @CoolMind
Arslan Maqbool

0

Tôi đã gặp lỗi tương tự khi cố gắng gọi mMap.animateCamera. Tôi đã giải quyết nó bằng cách gọi lệnh gọi lại setOnMapLoadedCallback trong hàm onMapReady của tôi như được hiển thị bên dưới

public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

   // other codes go there

    mMap.setOnMapLoadedCallback(() -> {
            //Your code where exception occurs goes here...
            
        });
}

Điều này sẽ hoạt động tốt.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.