将 androidx-navigation 从 v2.3.5 更新到 v2.4.0 导致地图信息窗口消失

发布于 2025-01-16 22:03:13 字数 8696 浏览 1 评论 0原文

我正在开发的一个原生 Android 应用程序有一个 ViewPager,它可以在 2 个片段之间滑动 - 其中一个片段由位置列表组成,当单击某个位置时,ViewPager 会滑动到第二个片段,该片段由带有该位置标记的地图组成,一个信息窗口,其名称如本图中所示

androidx.navigation:navigation-fragment 从 v2.3.5 更新到 v2.4.0 后,信息窗口偶尔无法出现在即使 Marker.isInfoWindowShown() 返回 true 此处显示。这是与设置标记和标记相关的代码。地图片段中的信息窗口:

public class MapFragment extends Fragment implements AppConstants {

private static final int MAP_ZOOM_DEFAULT = 15;
private static final int MAP_ZOOM = 18;
private static final double MARKER_DEFAULT_LAT = 38.536997d; // Mrak Hall
private static final double MARKER_DEFAULT_LNG = -121.749074d; // Mrak Hall

private FragmentMapBinding binding;
private SupportMapFragment supportMapFragment;
private GoogleMap map;
private Place selectedPlace;
private Marker marker;

@SuppressLint("ClickableViewAccessibility")
@Override
public View onCreateView(
    @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // Instantiate Google Map
    if (supportMapFragment == null) {
        setMap();
    }

    FragmentManager fragmentManager = getChildFragmentManager();
    fragmentManager.beginTransaction().replace(R.id.map_container, supportMapFragment).commit();

    // Inflate views and set onClick listeners
    binding = DataBindingUtil.inflate(inflater, R.layout.fragment_map, container, false);

    return binding.getRoot();
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    Analytics.logScreen(
        requireActivity(), getClass().getSimpleName(), getClass().getSimpleName());

    MainActivityViewModel viewModel =
        new ViewModelProvider(requireActivity()).get(MainActivityViewModel.class);

    viewModel
        .getSelectedPlace()
        .observe(
            getViewLifecycleOwner(),
            place -> {
                selectedPlace = place;
                setMarker(place);
            });
}

private void setMap() {
    supportMapFragment = SupportMapFragment.newInstance();
    supportMapFragment.getMapAsync(
        googleMap -> {
            map = googleMap;
            map.setOnMarkerClickListener(
                marker -> {
                    startLocationActivity();
                    return false;
                });
            map.setOnInfoWindowClickListener(marker -> startLocationActivity());
            displayUserLocation();
            map.setOnMapClickListener(new OnMapClickListener() {
                @Override
                public void onMapClick(@NonNull LatLng latLng) {
                    if (marker != null && !marker.isInfoWindowShown())
                        marker.showInfoWindow();
                }
            });
            if (selectedPlace == null) {
                setDefaultMarker();
            } else {
                setMarker(selectedPlace);
            }
        });
}

private void displayUserLocation() {
    if (ActivityCompat.checkSelfPermission(
        requireContext(), Manifest.permission.ACCESS_FINE_LOCATION)
        == PackageManager.PERMISSION_GRANTED
        && ActivityCompat.checkSelfPermission(
        requireContext(), Manifest.permission.ACCESS_COARSE_LOCATION)
        == PackageManager.PERMISSION_GRANTED) {
        map.setMyLocationEnabled(true);
    }
}

private void setDefaultMarker() {
    LatLng location = new LatLng(MARKER_DEFAULT_LAT, MARKER_DEFAULT_LNG);

    /* Some devices may have an issue/bug with the current
     * version of Google Play Services (GPS).
     * ERROR: java.lang.NullPointerException: CameraUpdateFactory is not initialized
     */
    try {
        map.moveCamera(CameraUpdateFactory.newLatLngZoom(location, MAP_ZOOM));
    } catch (Exception e) {
        Log.e(LOG_TAG, "ERROR: exception : ", e);
    }
}

private void setMarker(Place place) {
    if (map == null) {
        return;
    }

    map.clear();
    binding.mapContainer.requestFocus();
    selectedPlace = place;
    map.moveCamera(
        CameraUpdateFactory.newLatLngZoom(selectedPlace.getLatLng(), MAP_ZOOM_DEFAULT));

    MapMarkerTag markerTag = new MapMarkerTag();
    markerTag.setName(selectedPlace.getName());

    if (selectedPlace.getDrawable() == R.drawable.ic_parking_icon) {
        markerTag.setPermits(((ParkingLot) selectedPlace).getDisplayPermits());
    } else {
        markerTag.setPermits(getString(R.string.n_a));
    }

    MapInfoWindowAdapter infoWindowAdapter = new MapInfoWindowAdapter(requireActivity());
    map.setInfoWindowAdapter(infoWindowAdapter);
    marker = map.addMarker(new MarkerOptions().position(selectedPlace.getLatLng()));
    Objects.requireNonNull(marker).setTag(markerTag);
    marker.showInfoWindow();
}

private void startLocationActivity() {
    if (selectedPlace instanceof CampusLocation) {
        Intent intent = new Intent(requireActivity(), CampusLocationInfoActivity.class);
        intent.putExtra(BUNDLE_KEY_LOCATION_NAME, ((CampusLocation) selectedPlace).getName());
        intent.putExtra(BUNDLE_KEY_LOCATION_URL, ((CampusLocation) selectedPlace).getUrl());
        intent.putExtra(
            BUNDLE_KEY_LOCATION_LAT_LNG,
            ((CampusLocation) selectedPlace).getLatLngAsString());
        startActivity(intent);
    } else {
        Intent intent = new Intent(requireActivity(), ParkingLotActivity.class);
        intent.putExtra(
            BUNDLE_KEY_PARKING_LOT_DATA, ((ParkingLot) selectedPlace).getData().toString());
        startActivity(intent);
    }
}

} 以下是应用程序中使用的自定义信息窗口适配器的代码:

public class MapInfoWindowAdapter implements GoogleMap.InfoWindowAdapter {

    private final Activity activity;

    public MapInfoWindowAdapter(Activity a) {
        this.activity = a;
    }

    @Override
    public View getInfoWindow(Marker marker) {
        View view = activity.getLayoutInflater().inflate(R.layout.map_info_window, null);

        TextView name = view.findViewById(R.id.info_window_name);
        TextView permitText = view.findViewById(R.id.info_window_permits_label);
        TextView permits = view.findViewById(R.id.info_window_permits);

        MapMarkerTag markerTag = (MapMarkerTag) marker.getTag();

        name.setText(Objects.requireNonNull(markerTag).getName());
        String permit = markerTag.getPermits();
        if (permit.equals(activity.getString(R.string.n_a))) {
            permitText.setVisibility(View.GONE);
            permits.setVisibility(View.GONE);
        } else {
            permits.setText(markerTag.getPermits());
        }

        return view;
    }

    @Override
    public View getInfoContents(@NonNull Marker marker) {
        return null;
    }
}

以下是 viewpager 的 xml 代码:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/UCDBlue"
    tools:context=".fragments.map.MapPagerFragment">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/map_pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/map_pager_tab" />

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/map_pager_tab"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:theme="@style/Widget.Design.TabLayout"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:tabIndicatorColor="@color/UCDGold"
        app:tabIndicatorHeight="4dp"
        app:tabTextAppearance="@style/TabLayoutStyle"
        app:tabTextColor="@color/UCDBlue" />

</androidx.constraintlayout.widget.ConstraintLayout>

此外,当单击底部导航视图以离开地图片段时,MapInfoWindowAdapter.getInfoWindowmarkerTagnull 的情况下调用,导致 NullPointerException 且应用程序崩溃。

有谁知道为什么以及如何更新 androidx.navigation:navigation-fragment 依赖项会导致这样的错误?

先感谢您。

A native android app I am working on has a ViewPager that slides between 2 fragments- one consisting of a list of locations and when a location is clicked, the ViewPager slides to the second fragment which consists of a map with a marker for that location and an info window with its name as seen in this image.

After updating androidx.navigation:navigation-fragment from v2.3.5 to v2.4.0, the info window occasionally fails to appears above the marker even though Marker.isInfoWindowShown() returns true shown here. This is code pertaining to setting the marker & info window in the map fragment:

public class MapFragment extends Fragment implements AppConstants {

private static final int MAP_ZOOM_DEFAULT = 15;
private static final int MAP_ZOOM = 18;
private static final double MARKER_DEFAULT_LAT = 38.536997d; // Mrak Hall
private static final double MARKER_DEFAULT_LNG = -121.749074d; // Mrak Hall

private FragmentMapBinding binding;
private SupportMapFragment supportMapFragment;
private GoogleMap map;
private Place selectedPlace;
private Marker marker;

@SuppressLint("ClickableViewAccessibility")
@Override
public View onCreateView(
    @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // Instantiate Google Map
    if (supportMapFragment == null) {
        setMap();
    }

    FragmentManager fragmentManager = getChildFragmentManager();
    fragmentManager.beginTransaction().replace(R.id.map_container, supportMapFragment).commit();

    // Inflate views and set onClick listeners
    binding = DataBindingUtil.inflate(inflater, R.layout.fragment_map, container, false);

    return binding.getRoot();
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    Analytics.logScreen(
        requireActivity(), getClass().getSimpleName(), getClass().getSimpleName());

    MainActivityViewModel viewModel =
        new ViewModelProvider(requireActivity()).get(MainActivityViewModel.class);

    viewModel
        .getSelectedPlace()
        .observe(
            getViewLifecycleOwner(),
            place -> {
                selectedPlace = place;
                setMarker(place);
            });
}

private void setMap() {
    supportMapFragment = SupportMapFragment.newInstance();
    supportMapFragment.getMapAsync(
        googleMap -> {
            map = googleMap;
            map.setOnMarkerClickListener(
                marker -> {
                    startLocationActivity();
                    return false;
                });
            map.setOnInfoWindowClickListener(marker -> startLocationActivity());
            displayUserLocation();
            map.setOnMapClickListener(new OnMapClickListener() {
                @Override
                public void onMapClick(@NonNull LatLng latLng) {
                    if (marker != null && !marker.isInfoWindowShown())
                        marker.showInfoWindow();
                }
            });
            if (selectedPlace == null) {
                setDefaultMarker();
            } else {
                setMarker(selectedPlace);
            }
        });
}

private void displayUserLocation() {
    if (ActivityCompat.checkSelfPermission(
        requireContext(), Manifest.permission.ACCESS_FINE_LOCATION)
        == PackageManager.PERMISSION_GRANTED
        && ActivityCompat.checkSelfPermission(
        requireContext(), Manifest.permission.ACCESS_COARSE_LOCATION)
        == PackageManager.PERMISSION_GRANTED) {
        map.setMyLocationEnabled(true);
    }
}

private void setDefaultMarker() {
    LatLng location = new LatLng(MARKER_DEFAULT_LAT, MARKER_DEFAULT_LNG);

    /* Some devices may have an issue/bug with the current
     * version of Google Play Services (GPS).
     * ERROR: java.lang.NullPointerException: CameraUpdateFactory is not initialized
     */
    try {
        map.moveCamera(CameraUpdateFactory.newLatLngZoom(location, MAP_ZOOM));
    } catch (Exception e) {
        Log.e(LOG_TAG, "ERROR: exception : ", e);
    }
}

private void setMarker(Place place) {
    if (map == null) {
        return;
    }

    map.clear();
    binding.mapContainer.requestFocus();
    selectedPlace = place;
    map.moveCamera(
        CameraUpdateFactory.newLatLngZoom(selectedPlace.getLatLng(), MAP_ZOOM_DEFAULT));

    MapMarkerTag markerTag = new MapMarkerTag();
    markerTag.setName(selectedPlace.getName());

    if (selectedPlace.getDrawable() == R.drawable.ic_parking_icon) {
        markerTag.setPermits(((ParkingLot) selectedPlace).getDisplayPermits());
    } else {
        markerTag.setPermits(getString(R.string.n_a));
    }

    MapInfoWindowAdapter infoWindowAdapter = new MapInfoWindowAdapter(requireActivity());
    map.setInfoWindowAdapter(infoWindowAdapter);
    marker = map.addMarker(new MarkerOptions().position(selectedPlace.getLatLng()));
    Objects.requireNonNull(marker).setTag(markerTag);
    marker.showInfoWindow();
}

private void startLocationActivity() {
    if (selectedPlace instanceof CampusLocation) {
        Intent intent = new Intent(requireActivity(), CampusLocationInfoActivity.class);
        intent.putExtra(BUNDLE_KEY_LOCATION_NAME, ((CampusLocation) selectedPlace).getName());
        intent.putExtra(BUNDLE_KEY_LOCATION_URL, ((CampusLocation) selectedPlace).getUrl());
        intent.putExtra(
            BUNDLE_KEY_LOCATION_LAT_LNG,
            ((CampusLocation) selectedPlace).getLatLngAsString());
        startActivity(intent);
    } else {
        Intent intent = new Intent(requireActivity(), ParkingLotActivity.class);
        intent.putExtra(
            BUNDLE_KEY_PARKING_LOT_DATA, ((ParkingLot) selectedPlace).getData().toString());
        startActivity(intent);
    }
}

}
Here is the code for the custom info window adapter used in the app:

public class MapInfoWindowAdapter implements GoogleMap.InfoWindowAdapter {

    private final Activity activity;

    public MapInfoWindowAdapter(Activity a) {
        this.activity = a;
    }

    @Override
    public View getInfoWindow(Marker marker) {
        View view = activity.getLayoutInflater().inflate(R.layout.map_info_window, null);

        TextView name = view.findViewById(R.id.info_window_name);
        TextView permitText = view.findViewById(R.id.info_window_permits_label);
        TextView permits = view.findViewById(R.id.info_window_permits);

        MapMarkerTag markerTag = (MapMarkerTag) marker.getTag();

        name.setText(Objects.requireNonNull(markerTag).getName());
        String permit = markerTag.getPermits();
        if (permit.equals(activity.getString(R.string.n_a))) {
            permitText.setVisibility(View.GONE);
            permits.setVisibility(View.GONE);
        } else {
            permits.setText(markerTag.getPermits());
        }

        return view;
    }

    @Override
    public View getInfoContents(@NonNull Marker marker) {
        return null;
    }
}

Here is the xml code for the viewpager:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/UCDBlue"
    tools:context=".fragments.map.MapPagerFragment">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/map_pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/map_pager_tab" />

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/map_pager_tab"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:theme="@style/Widget.Design.TabLayout"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:tabIndicatorColor="@color/UCDGold"
        app:tabIndicatorHeight="4dp"
        app:tabTextAppearance="@style/TabLayoutStyle"
        app:tabTextColor="@color/UCDBlue" />

</androidx.constraintlayout.widget.ConstraintLayout>

Furthermore, when clicking the bottom navigation view to navigate away from the map fragment, the MapInfoWindowAdapter.getInfoWindow is called with markerTag being null resulting in a NullPointerException and the app crashing.

Does anybody know why and how updating the androidx.navigation:navigation-fragment dependency would cause such a bug?

Thank you in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文