moveCamera กับ CameraUpdateFactory.newLatLngBounds ขัดข้อง


96

ฉันกำลังใช้Android Google Maps APIใหม่

ฉันสร้างกิจกรรมที่มี MapFragment ในกิจกรรมonResumeฉันตั้งเครื่องหมายลงในวัตถุ GoogleMap จากนั้นกำหนดกรอบขอบเขตสำหรับแผนที่ซึ่งมีเครื่องหมายทั้งหมด

นี่คือการใช้รหัสหลอกต่อไปนี้:

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

การเรียกเพื่อmap.moveCamera()ทำให้แอปพลิเคชันของฉันขัดข้องด้วยสแต็กต่อไปนี้:

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)

ถ้า - แทนที่จะnewLatLngBounds()ใช้newLatLngZoom()วิธีการของโรงงานฉันใช้วิธีการดังกล่าวจะไม่เกิดกับดักเดียวกัน

เป็นonResumeสถานที่ที่ดีที่สุดในการวาดเครื่องหมายลงบนวัตถุ GoogleMap หรือฉันควรจะวาดภาพเครื่องหมายและการตั้งค่าตำแหน่งกล้องที่อื่น?

คำตอบ:


133

คุณสามารถใช้ง่ายnewLatLngBoundsวิธีการในOnCameraChangeListener ทั้งหมดจะทำงานได้อย่างสมบูรณ์และคุณไม่จำเป็นต้องคำนวณขนาดหน้าจอ เหตุการณ์นี้เกิดขึ้นหลังจากการคำนวณขนาดแผนที่ (ตามที่ฉันเข้าใจ)

ตัวอย่าง:

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
ใช้งานได้เนื่องจาก setOnCameraChangeListener รับประกันว่าจะทำงานอย่างน้อยหนึ่งครั้งหลังจากที่แผนที่ผ่านการจัดวางแล้ว? นี่คือบทพิสูจน์ในอนาคต?
Glenn Bech

3
@SteelRat นั่นคือเหตุผลที่ฉันจะต่อต้านการใช้โซลูชันนี้ คุณไม่มีทางรู้ว่าเมื่อใดที่ Google เปลี่ยนสิ่งนี้หรือแม้ว่าจะใช้งานได้ในทุกเวอร์ชันหรืออุปกรณ์ Android ก็ตาม
Glenn Bech

1
@SteelRat ฉันเห็นด้วย แต่อาจเรียกมากกว่าหนึ่งครั้งในช่วงชีวิตของกิจกรรม? ฉันแค่ตั้งข้อโต้แย้งว่าแม้ว่าจะใช้ได้ผลในกรณีนี้ แต่ก็ไม่ใช่วิธีแก้ปัญหาที่หรูหรามากนัก ฉันคิดว่า OnGlobalLayoutListener / Treelistener เป็นวิธีที่ถูกต้องกว่าในการดำเนินการนี้
Glenn Bech

4
อาจจะเพิ่มmap.moveCamera (CameraUpdateFactory.scrollBy (1, 1)); หลังจากรหัสด้านบนจะทำให้เคล็ดลับ?
MY

3
setOnCameraChangeListenerเลิกใช้แล้ว
osrl

56

คำตอบเหล่านั้นใช้ได้ดี แต่ฉันจะใช้วิธีอื่นที่ง่ายกว่านี้ หากวิธีนี้ใช้ได้ผลหลังจากจัดวางแผนที่แล้วให้รอสักครู่:

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

1
ฉันพบหลังจากการทดลองมากมายที่ฉันต้องใช้การผสมผสานระหว่างคำตอบนี้กับของ Daniele ข้างต้น บางครั้งแผนที่ยังไม่ได้โหลดบางครั้งเค้าโครงยังไม่เสร็จสมบูรณ์ ฉันไม่สามารถขยับกล้องได้อย่างที่ต้องการจนกว่าทั้งสองจะเกิดขึ้น
RobP

2
คำเตือนสำหรับวิธีนี้จากเอกสาร: "เหตุการณ์นี้จะไม่เริ่มทำงานหากแผนที่ไม่โหลดเนื่องจากปัญหาการเชื่อมต่อหรือหากแผนที่มีการเปลี่ยนแปลงอย่างต่อเนื่องและไม่โหลดเสร็จสิ้นเนื่องจากผู้ใช้โต้ตอบกับแผนที่ตลอดเวลา" (เน้นของฉัน)
stkent

1
การดำเนินการนี้ล่าช้าเกินความจำเป็นเนื่องจากรอให้ไทล์แผนที่โหลดในตำแหน่งที่ไม่ถูกต้องก่อนที่จะย้ายไปยังตำแหน่งที่ถูกต้อง
John Patterson

1
เวลาส่วนใหญ่ใช้เวลาในการยิงประมาณ 3-10 วินาทีซึ่งเป็นสิ่งที่ยอมรับไม่ได้
Nima G

นี่คือสถานที่สำหรับเคลื่อนย้ายกล้อง ฉันขอแนะนำให้ใช้ map.animateCamera แทน map.moveCamera
Bharat Dodeja

51

ตกลงฉันทำสิ่งนี้ ตามที่ระบุไว้ที่นี่ว่า API ไม่สามารถใช้การจัดวางล่วงหน้าได้

API ที่ถูกต้องที่จะใช้อธิบายได้ว่า:

หมายเหตุ: ใช้วิธีที่ง่ายกว่าเท่านั้น newLatLngBounds (ขอบเขตช่องว่าง) ในการสร้าง CameraUpdate หากจะใช้เพื่อเคลื่อนกล้องหลังจากแผนที่ผ่านการจัดวางแล้ว ในระหว่างการจัดวาง API จะคำนวณขอบเขตการแสดงผลของแผนที่ซึ่งจำเป็นสำหรับการฉายกรอบขอบเขตอย่างถูกต้อง ในการเปรียบเทียบคุณสามารถใช้ CameraUpdate ที่ส่งคืนโดยวิธีการที่ซับซ้อนกว่า newLatLngBounds (ขอบเขตความกว้างความสูงช่องว่างภายใน) ได้ตลอดเวลาแม้ว่าแผนที่จะผ่านการจัดวางแล้วก็ตามเนื่องจาก API จะคำนวณขอบเขตการแสดงผลจากอาร์กิวเมนต์ที่คุณส่งผ่าน

เพื่อแก้ไขปัญหาฉันได้คำนวณขนาดหน้าจอของฉันและระบุความกว้างและความสูงไว้ที่

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

สิ่งนี้ทำให้ฉันสามารถระบุโครงร่างล่วงหน้าของกล่องล้อมรอบได้


18
แต่ถ้าแผนที่ไม่ขึ้นทั้งหน้าจอล่ะ?
theblang

ในกรณีของฉันฉันคิดว่าหน้าจอใหญ่กว่าที่มีผลบังคับใช้และจำเป็นต้องคำนวณช่องว่างภายในอย่างรอบคอบมากขึ้นเพื่อให้มันไม่ใหญ่เกินไป เหตุผลที่ง่ายกว่ามาก
rfay

2
คุณสามารถแสดงวิธีคำนวณตามขนาดหน้าจอได้หรือไม่?
Daniel Gomez Rico

สิ่งนี้ไม่ผิดพลาด แต่ความกว้างและความสูงที่จะใช้นั้นใกล้เคียงกับการคาดเดาอย่างแท้จริงพื้นที่ที่จะวาดนั้นคาดเดาไม่ได้จริงๆ
Vince

34

วิธีแก้นั้นง่ายกว่านั้น ....

// 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));
            }
        });
    }
}

จับIllegalStateExceptionและใช้ผู้ฟังทั่วโลกในมุมมองแทน การกำหนดขนาดขอบเขตล่วงหน้า (การจัดวางล่วงหน้า) โดยใช้วิธีการอื่นหมายความว่าคุณต้องคำนวณขนาดของมุมมองไม่ใช่อุปกรณ์หน้าจอ จะจับคู่ได้ก็ต่อเมื่อคุณใช้แผนที่ของคุณแบบเต็มหน้าจอและไม่ใช้ชิ้นส่วน


จนถึงตอนนี้ฉันใช้ตัวอย่างข้อมูลเล็ก ๆ น้อย ๆ ของคุณแล้ว แต่ก็ยังล้มเหลว ... คำตอบที่ถูกต้องคือคำตอบแรก :-)
cesards

1
อย่างไร / เมื่อล้มเหลว? ไม่เคยมีปัญหากับเรื่องนี้ คำตอบแรกเพียงแค่ส่งขนาดฮาร์ดโค้ดให้เป็นทางเลือก "มันใช้งานได้" เช่นเดียวกับ "มันไม่พังอีกต่อไป" แต่มันไม่ได้ทำสิ่งเดียวกัน
Daniele Segato

@ andrea.spot ฉันจำไม่ได้ว่าทำไมฉันถึงพูดว่าง่ายกว่า ฉันเดาว่าโซลูชันที่ยอมรับได้รับการแก้ไขในขณะเดียวกัน ปัจจุบันวิธีการแก้ปัญหาที่ยอมรับถือว่าแผนที่ของคุณใช้เวลาหน้าจอทั้งหมดซึ่งไม่เป็นความจริงเสมอ หากคุณไม่ตกหลุมในกรณีนั้นคุณจำเป็นต้องทราบขนาดของแผนที่หรือใช้วิธีการของฉันหรือสิ่งที่คล้ายกัน
Daniele Segato

ตรวจสอบคำตอบด้านล่างโดย Xingang Huang ซึ่งจะเพิ่มอีกเล็กน้อย
Daniele Segato

15

นี่คือการแก้ไขของฉันเพียงแค่รอจนกว่าแผนที่จะโหลดในกรณีนั้น

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
Shid

14

การเพิ่มและลบเครื่องหมายสามารถทำได้ก่อนการจัดวาง แต่การเคลื่อนกล้องไม่สามารถทำได้ (ยกเว้นใช้newLatLngBounds(boundary, padding)ตามที่ระบุไว้ในคำตอบของ OP)

สถานที่ที่ดีที่สุดในการอัปเดตกล้องเริ่มต้นอาจใช้การถ่ายภาพครั้งOnGlobalLayoutListenerเดียวตามที่แสดงในโค้ดตัวอย่างของ Googleเช่นดูข้อความที่ตัดตอนมาต่อไปนี้จากsetUpMap()ใน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));
        }
    });
}

ฉันสร้างรุ่น gradlefied ของโครงการตัวอย่างแผนที่
JJD

ฉันได้รับข้อผิดพลาดเกี่ยวกับการเปลี่ยนแปลงการวางแนว ฉันยังพบสิ่งนี้จากโค้ดตัวอย่าง แต่สิ่งนี้ไม่ได้ผลสำหรับฉัน ฉันยังคงได้รับข้อผิดพลาดเดิม
osrl

14

คำตอบที่ยอมรับใช้ไม่ได้สำหรับฉันเพราะฉันต้องใช้รหัสเดียวกันในที่ต่างๆของแอป

แทนที่จะรอให้กล้องเปลี่ยนฉันสร้างวิธีง่ายๆตามคำแนะนำของ Lee ในการระบุขนาดแผนที่ ในกรณีนี้แผนที่ของคุณมีขนาดเท่ากับหน้าจอ

// 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));

หวังว่ามันจะช่วยคนอื่น!


8

คำตอบที่ยอมรับคือตามที่ระบุไว้ในความคิดเห็นแฮ็คเล็กน้อย หลังจากใช้งานแล้วฉันสังเกตเห็นIllegalStateExceptionบันทึกในการวิเคราะห์จำนวนมากที่ยอมรับได้ วิธีแก้ปัญหาที่ฉันใช้คือการเพิ่มOnMapLoadedCallbackซึ่งCameraUpdateจะดำเนินการ การโทรกลับจะถูกเพิ่มลงในไฟล์GoogleMap . หลังจากแผนที่ของคุณโหลดจนเต็มแล้วระบบจะทำการอัพเดตกล้อง

ซึ่งจะทำให้แผนที่แสดงมุมมองการซูมออก (0,0) สั้น ๆ ก่อนดำเนินการอัปเดตกล้อง ฉันรู้สึกว่านี่เป็นสิ่งที่ยอมรับได้มากกว่าการทำให้เกิดปัญหาหรืออาศัยพฤติกรรมที่ไม่มีเอกสาร


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

ใช้มันได้ผลสำหรับฉัน


นี่เป็นเพียงศูนย์กลางของแผนที่ แต่ไม่ได้ปรับการซูมไปที่ขอบเขตแบบไดนามิก
shaithana

เราสามารถคำนวณขอบเขตสำหรับ matkers เพิ่มเติมโดยใช้ "LatLngBounds.Builder"
Ramesh Bhupathi

@RameshBhupathi แต่มันจะยังคงเป็นมุมมองแผนที่ที่มีศูนย์กลางและการซูม
Tima

4

ในบางกรณีที่เกิดขึ้นน้อยมากเลย์เอาต์ MapView จะเสร็จสิ้น แต่เลย์เอาต์ GoogleMap ยังไม่เสร็จสมบูรณ์ViewTreeObserver.OnGlobalLayoutListenerตัวเองไม่สามารถหยุดความผิดพลาด ฉันพบข้อขัดข้องกับแพ็กเกจบริการ Google Play เวอร์ชัน 5084032 กรณีที่เกิดขึ้นได้ยากนี้อาจเกิดจากการเปลี่ยนแปลงแบบไดนามิกของการมองเห็น MapView ของฉัน

เพื่อแก้ปัญหานี้ผมฝังGoogleMap.OnMapLoadedCallbackใน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));
                    }
                });
            }
        }
    });
}

ที่น่าสนใจ: ถ้าฉัน refactor mapView.getViewTreeObserver()เป็นตัวแปรท้องถิ่นต่อไปนี้ข้อผิดพลาดเกิดขึ้น: "IllegalStateException: ViewTreeObserver นี้ไม่ได้มีชีวิตอยู่โทร getViewTreeObserver () อีกครั้ง" ลองทำดูไหม
JJD

หาก mapView.getViewTreeObserver (). isAlive () เกิดขึ้นเป็นเท็จฉันจะใส่ทางเลือกอื่นสำหรับ map.setOnMapLoadedCallback ()
southerton

4

ผมใช้วิธีการที่แตกต่างกันซึ่งสำหรับรุ่นล่าสุดจาก Google Maps SDK (9.6+) และอยู่บนพื้นฐานonCameraIdleListener ที่ผมเห็นเพื่อให้ห่างไกลวิธีการโทรกลับก็มักจะเรียกหลังจากonCameraIdle onMapReadyดังนั้นแนวทางของฉันจึงดูเหมือนโค้ดชิ้นนี้ (พิจารณาจากที่ใส่ไว้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

ฉันได้สร้างวิธีการรวมการเรียกกลับสองรายการ: onMapReady และ onGlobalLayout เป็นหนึ่งเดียวที่สังเกตได้ซึ่งจะปล่อยออกมาก็ต่อเมื่อทั้งสองเหตุการณ์ถูกทริกเกอร์

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


1
นี่เป็นวิธีที่สะอาดที่สุดในการแก้ปัญหานี้ ฉันทำสิ่งนี้มานานแล้วและไม่มีการแสดงข้อผิดพลาดในการวิเคราะห์แม้ในผู้ใช้ 1k คนต่อวัน +1
Skyle

2

โอเคฉันประสบปัญหาเดียวกัน ฉันมีส่วนของฉันกับ SupportmapFragment, ABS และลิ้นชักการนำทาง สิ่งที่ฉันทำคือ:

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...
}

และโดยวิธีการที่ฉันโทรresetCamera()จากonCreateViewหลังจากพองและก่อนกลับ
สิ่งนี้ทำให้เกิดข้อยกเว้นในครั้งแรก (ในขณะที่แผนที่ "มีขนาด" เป็นวิธีการพูด ... ) จากนั้นบางครั้งฉันต้องรีเซ็ตกล้องแผนที่มีขนาดอยู่แล้วและทำผ่านช่องว่างภายใน

มีอธิบายปัญหาในเอกสารระบุว่า:

อย่าเปลี่ยนกล้องด้วยการอัปเดตกล้องนี้จนกว่าแผนที่จะผ่านเลย์เอาต์ (เพื่อให้วิธีนี้สามารถกำหนดกรอบขอบเขตและระดับการซูมที่เหมาะสมได้อย่างถูกต้องแผนที่ต้องมีขนาด) มิฉะนั้นIllegalStateExceptionจะถูกโยนทิ้ง มันไม่เพียงพอสำหรับแผนที่ที่จะพร้อมใช้งาน (เช่นgetMap()ส่งคืนวัตถุที่ไม่ใช่ค่าว่าง) มุมมองที่มีแผนที่จะต้องได้รับการจัดวางเพื่อที่จะกำหนดขนาด หากคุณไม่แน่ใจว่าเกิดเหตุการณ์นี้ขึ้นให้ใช้newLatLngBounds(LatLngBounds, int, int, int)แทนและระบุขนาดของแผนที่ด้วยตนเอง

ฉันคิดว่ามันเป็นทางออกที่ดีทีเดียว หวังว่ามันจะช่วยใครบางคน


1
มีผู้ฟังคนใดบ้างที่จะฟังเมื่อเค้าโครงโหลดเต็ม เหรอ?
Sagar Nayak

มีการยกข้อยกเว้นในnewLatLngBounds()หรือในanimateCamera()หรือทั้งสองอย่าง? สาขาจับจะไม่นำไปสู่ข้อยกเว้นใหม่?
CoolMind

1

หากคุณจำเป็นต้องรอให้การโต้ตอบของผู้ใช้เริ่มต้นขึ้นให้ใช้OnMapLoadedCallbackตามที่อธิบายไว้ในคำตอบก่อนหน้านี้ แต่หากคุณต้องการเพียงระบุตำแหน่งเริ่มต้นสำหรับแผนที่คุณก็ไม่จำเป็นต้องมีวิธีแก้ปัญหาใด ๆ ที่ระบุไว้ในคำตอบเหล่านั้น ทั้งสองอย่างMapFragmentและMapViewสามารถยอมรับกGoogleMapOptionsเมื่อเริ่มต้นที่สามารถระบุตำแหน่งเริ่มต้นได้ เคล็ดลับเดียวคืออย่ารวมไว้ในเลย์เอาต์ของคุณโดยตรงเพราะระบบจะเรียกสิ่งเหล่านี้โดยไม่มีตัวเลือก แต่จะเริ่มต้นแบบไดนามิก

ใช้สิ่งนี้ในเค้าโครงของคุณ:

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

และแทนที่ส่วนใน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();

นอกจากจะเริ่มได้เร็วขึ้นแล้วจะไม่มีแผนที่โลกปรากฏขึ้นก่อนและมีการเคลื่อนกล้องเป็นครั้งที่สอง แผนที่จะเริ่มต้นด้วยตำแหน่งที่ระบุโดยตรง


0

อีกวิธีหนึ่งที่ต้องการ (สมมติว่ามุมมองสูงสุดของคุณคือ FrameLayout ที่ตั้งชื่อrootContainerแม้ว่าจะใช้งานได้ตราบเท่าที่คุณเลือกคอนเทนเนอร์ที่อยู่บนสุดเสมอไม่ว่าจะมีประเภทหรือชื่อใดก็ตาม):

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

การปรับเปลี่ยนฟังก์ชั่นกล้องของคุณให้ใช้งานได้layoutDoneก็ต่อเมื่อtrueจะช่วยแก้ปัญหาทั้งหมดของคุณโดยไม่ต้องเพิ่มฟังก์ชันพิเศษหรือต่อสายตรรกะให้กับlayoutListenerตัวจัดการ


0

ฉันพบว่าสิ่งนี้ใช้งานได้และง่ายกว่าวิธีแก้ปัญหาอื่น ๆ :

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);
            }
        });
    }
}

การดำเนินการนี้จะพยายามโทรอีกครั้งหลังจากเรียกใช้เค้าโครงแล้ว อาจรวมถึงความปลอดภัยเพื่อหลีกเลี่ยงการวนซ้ำที่ไม่สิ้นสุด


0

ทำไมไม่ใช้สิ่งนี้:

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);
}

ทำไมnewLatLngBounds(builder.build(), padding)ไม่ลองจับภายใน? ข้อยกเว้นนี้เกิดขึ้นในmoveCamera()?
CoolMind

0

มีคลาสผู้ช่วยใน repo ของ Google Maps ที่คุณสามารถใช้ประโยชน์ได้ - รอให้ทั้งเลย์เอาต์และแผนที่พร้อมก่อนที่จะแจ้งให้โทรกลับด้วย GoogleMap:

แหล่งที่มาดั้งเดิมอยู่ที่นี่:

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

มีการใช้งาน Kotlin ด้วย:

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

ตามที่ระบุในOnCameraChangeListener () จะเลิก , setOnCameraChangeListenerเลิกตอนนี้ ดังนั้นคุณควรแทนที่ด้วยหนึ่งในสามวิธี:

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

ในกรณีของฉันฉันใช้OnCameraIdleListenerและข้างในฉันลบออกเพราะมันถูกเรียกใช้ครั้งแล้วครั้งเล่าในทุกการเคลื่อนไหว

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)
}

อัปเดต

ฉันลบออกgoogleMap.setOnCameraIdleListenerในโครงการของฉันเพราะบางครั้งมันไม่ได้ถูกเรียกเมื่อมีการแสดงแผนที่ แต่ยังคงgoogleMap.setOnCameraIdleListener(clusterManager)อยู่


ขอบคุณสำหรับการตอบกลับของคุณฉันคิดหาวิธีแก้ปัญหาเกือบจะเหมือนกันแล้ว
Arslan Maqbool

@ArslanMaqbool ขอบคุณ! ฉันอยู่ระหว่างดำเนินการและจะขอบคุณหากคุณแบ่งปันตัวแปรของคุณ
CoolMind

ความสุขของฉัน @CoolMind
Arslan Maqbool

0

ฉันมีข้อผิดพลาดเดียวกันกับการพยายามเรียก mMap.animateCamera ฉันแก้ไขได้โดยการเรียกsetOnMapLoadedCallback callback ในฟังก์ชันonMapReadyตามที่แสดงด้านล่าง

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

   // other codes go there

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

ควรใช้งานได้ดี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.