将 androidx-navigation 从 v2.3.5 更新到 v2.4.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.getInfoWindow
在 markerTag
为 null
的情况下调用,导致 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论