在 MapView 中使用 Bottomsheet 时出错
Getting Error using Bottomsheet using in MapView
我创建了一个 MapView,我在其中使用从数据库调用的经度和纬度绘制了多个标记。
现在,我想在单击此标记时显示底部 sheet 并在 sheet 上显示特定数据。我从这里 Slide in view from bottom over Google Map on Marker click
获取了 bottomsheet 的一些参考资料
但是在执行这篇文章后我得到一个错误
错误是
E/AndroidRuntime: FATAL EXCEPTION: main
Process: part.time.job.v2, PID: 4236
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.google.android.material.bottomsheet.BottomSheetBehavior.setPeekHeight(int)' on a null object reference
at part.time.job.v2.LabourFragment.onCreateView(LabourFragment.java:115)
这是我的JAVA代码
package part.time.job.v2;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.android.libraries.places.api.Places;
import com.google.android.libraries.places.api.model.Place;
import com.google.android.libraries.places.api.model.PlaceLikelihood;
import com.google.android.libraries.places.api.net.FindCurrentPlaceRequest;
import com.google.android.libraries.places.api.net.FindCurrentPlaceResponse;
import com.google.android.libraries.places.api.net.PlacesClient;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.firebase.firestore.CollectionReference;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.QueryDocumentSnapshot;
import com.google.firebase.firestore.QuerySnapshot;
import com.google.maps.android.ui.IconGenerator;
import java.util.Arrays;
import java.util.List;
import model.Jobpost;
import static com.android.volley.VolleyLog.TAG;
/**
* A simple {@link Fragment} subclass.
*/
public class LabourFragment extends Fragment implements OnMapReadyCallback {
private GoogleMap mGoogleMap;
private MapView mMapview;
private int STORAGE_PERMISSION_CODE = 1;
private CameraPosition mCameraPosition;
private FusedLocationProviderClient mFusedLocationProviderClient;
private final LatLng mDefaultLocation = new LatLng(-33.8523341, 151.2106085);
private static final int DEFAULT_ZOOM = 15;
private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1;
private boolean mLocationPermissionGranted;
private Location mLastKnownLocation;
Dialog myDialog;
private BottomSheetBehavior bottomSheetBehavior;
private View bottomSheet;
// Keys for storing activity state.
private static final String KEY_CAMERA_POSITION = "camera_position";
private static final String KEY_LOCATION = "location";
// Used for selecting the current place.
private static final int M_MAX_ENTRIES = 5;
private String[] mLikelyPlaceNames;
private String[] mLikelyPlaceAddresses;
private List[] mLikelyPlaceAttributions;
private LatLng[] mLikelyPlaceLatLngs;
private PlacesClient mPlacesClient;
public LabourFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (savedInstanceState != null) {
mLastKnownLocation = savedInstanceState.getParcelable(KEY_LOCATION);
mCameraPosition = savedInstanceState.getParcelable(KEY_CAMERA_POSITION);
}
// Inflate the layout for this fragment
View view= inflater.inflate(R.layout.fragment_labour, container, false);
// myDialog = new Dialog(getActivity());
bottomSheet = view.findViewById(R.id.bottom_sheet);
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet); //error occur on this line
bottomSheetBehavior.setPeekHeight(200);
bottomSheetBehavior.setHideable(true);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
Places.initialize(getActivity(), getString(R.string.google_maps_key));
mPlacesClient = Places.createClient(getActivity());
mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(getActivity());
return view;
}
@Override
public void onSaveInstanceState(Bundle outState) {
if (mGoogleMap != null) {
outState.putParcelable(KEY_CAMERA_POSITION, mGoogleMap.getCameraPosition());
outState.putParcelable(KEY_LOCATION, mLastKnownLocation);
super.onSaveInstanceState(outState);
}
}
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mMapview = (MapView) view.findViewById(R.id.mapView);
if (mMapview!=null){
mMapview.onCreate(null);
mMapview.onResume();
mMapview.getMapAsync(this);
}
}
@Override
public void onMapReady(final GoogleMap googleMap) {
mGoogleMap=googleMap;
FirebaseFirestore mDatabase = FirebaseFirestore.getInstance();
CollectionReference mOrderRef = mDatabase.collection("Job Post1");
mOrderRef.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
@Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
for(QueryDocumentSnapshot documentSnapshot : queryDocumentSnapshots) {
if(documentSnapshot.contains("lat") && documentSnapshot.contains("lon")) {
String lat = (String) documentSnapshot.get("lat");
String lon = (String) documentSnapshot.get("lon");
final String title = (String) documentSnapshot.get("title");
final String jobdate = (String) documentSnapshot.get("jobdate");
final String time = (String) documentSnapshot.get("time");
if(lat != null && lon != null && !lat.isEmpty() && !lon.isEmpty()) {
double latitude = Double.parseDouble(lat.trim());
double longitude = Double.parseDouble(lon.trim());
IconGenerator iconGen = new IconGenerator(getActivity());
googleMap.addMarker(new MarkerOptions().position(new LatLng(latitude, longitude)).icon(BitmapDescriptorFactory.fromBitmap(iconGen.makeIcon(title))));
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(latitude, longitude), 18.0f));
googleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
updateBottomSheetContent(marker);
return false;
}
});
googleMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
@Override
public void onMapClick(LatLng latLng) {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
}
});
}
}
}
}
});
}
private void updateBottomSheetContent(Marker marker) {
TextView name = (TextView) bottomSheet.findViewById(R.id.detail_name);
name.setText(marker.getTitle());
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}
}
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"
tools:context="part.time.job.v2.LabourFragment">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:itemBackground="@color/purple"
app:itemIconTint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/nav_items"
tools:ignore="MissingConstraints" />
<com.google.android.gms.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="675dp"
tools:ignore="MissingConstraints" />
<include layout="@layout/bottom_sheet" />
</androidx.constraintlayout.widget.ConstraintLayout>
bottom_sheet xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
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:fitsSystemWindows="true">
<androidx.core.widget.NestedScrollView android:id="@+id/bottom_sheet"
android:layout_width="match_parent" android:layout_height="550dp"
android:background="@android:color/white" android:clipToPadding="true"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<TextView android:id="@+id/detail_name" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_margin="25dp"
android:layout_weight="3" android:gravity="center_vertical"
android:textAppearance="?android:textAppearanceLarge" />
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
您的 xml 中没有具有 "R.id.bottom_sheet" ID 的视图,但您正试图通过 findViewbyId() 获取视图,这就是为什么 bottomSheet 变量具有空值以及 bottomsheetbehaviour 值的原因null 并在尝试访问其上的任何方法时产生空指针异常。在您的情况下,以下行:
bottomSheetBehavior.setPeekHeight(200);
我检查了您的代码,它按预期显示底部 sheet。但是也有一些问题。
- 底部Sheet的大部分隐藏在
BottomNavigationView
后面。略微克服这种增加PeekHeight
。
bottomSheetBehavior.setPeekHeight(300);
- 您将底部 Sheet 容器的
BackgroundColor
设置为 android:color/white
。尝试将其更改为不同的颜色。
android:background="@android:color/holo_green_light"
- 您将
detail_name
的 layout_width
设置为 0dp
,这使视图不可见。努力做到match_parent
android:layout_width="match_parent"
这是底部的完整布局 Sheet 容器:
<androidx.core.widget.NestedScrollView
android:id="@+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="550dp"
android:background="@android:color/holo_green_light"
android:clipToPadding="true"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<TextView
android:id="@+id/detail_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="25dp"
android:layout_weight="3"
android:gravity="center_vertical"
android:textAppearance="?android:textAppearanceLarge" />
</androidx.core.widget.NestedScrollView>
输出:
我创建了一个 MapView,我在其中使用从数据库调用的经度和纬度绘制了多个标记。
现在,我想在单击此标记时显示底部 sheet 并在 sheet 上显示特定数据。我从这里 Slide in view from bottom over Google Map on Marker click
获取了 bottomsheet 的一些参考资料但是在执行这篇文章后我得到一个错误
错误是
E/AndroidRuntime: FATAL EXCEPTION: main
Process: part.time.job.v2, PID: 4236
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.google.android.material.bottomsheet.BottomSheetBehavior.setPeekHeight(int)' on a null object reference
at part.time.job.v2.LabourFragment.onCreateView(LabourFragment.java:115)
这是我的JAVA代码
package part.time.job.v2;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.android.libraries.places.api.Places;
import com.google.android.libraries.places.api.model.Place;
import com.google.android.libraries.places.api.model.PlaceLikelihood;
import com.google.android.libraries.places.api.net.FindCurrentPlaceRequest;
import com.google.android.libraries.places.api.net.FindCurrentPlaceResponse;
import com.google.android.libraries.places.api.net.PlacesClient;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.firebase.firestore.CollectionReference;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.QueryDocumentSnapshot;
import com.google.firebase.firestore.QuerySnapshot;
import com.google.maps.android.ui.IconGenerator;
import java.util.Arrays;
import java.util.List;
import model.Jobpost;
import static com.android.volley.VolleyLog.TAG;
/**
* A simple {@link Fragment} subclass.
*/
public class LabourFragment extends Fragment implements OnMapReadyCallback {
private GoogleMap mGoogleMap;
private MapView mMapview;
private int STORAGE_PERMISSION_CODE = 1;
private CameraPosition mCameraPosition;
private FusedLocationProviderClient mFusedLocationProviderClient;
private final LatLng mDefaultLocation = new LatLng(-33.8523341, 151.2106085);
private static final int DEFAULT_ZOOM = 15;
private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1;
private boolean mLocationPermissionGranted;
private Location mLastKnownLocation;
Dialog myDialog;
private BottomSheetBehavior bottomSheetBehavior;
private View bottomSheet;
// Keys for storing activity state.
private static final String KEY_CAMERA_POSITION = "camera_position";
private static final String KEY_LOCATION = "location";
// Used for selecting the current place.
private static final int M_MAX_ENTRIES = 5;
private String[] mLikelyPlaceNames;
private String[] mLikelyPlaceAddresses;
private List[] mLikelyPlaceAttributions;
private LatLng[] mLikelyPlaceLatLngs;
private PlacesClient mPlacesClient;
public LabourFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (savedInstanceState != null) {
mLastKnownLocation = savedInstanceState.getParcelable(KEY_LOCATION);
mCameraPosition = savedInstanceState.getParcelable(KEY_CAMERA_POSITION);
}
// Inflate the layout for this fragment
View view= inflater.inflate(R.layout.fragment_labour, container, false);
// myDialog = new Dialog(getActivity());
bottomSheet = view.findViewById(R.id.bottom_sheet);
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet); //error occur on this line
bottomSheetBehavior.setPeekHeight(200);
bottomSheetBehavior.setHideable(true);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
Places.initialize(getActivity(), getString(R.string.google_maps_key));
mPlacesClient = Places.createClient(getActivity());
mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(getActivity());
return view;
}
@Override
public void onSaveInstanceState(Bundle outState) {
if (mGoogleMap != null) {
outState.putParcelable(KEY_CAMERA_POSITION, mGoogleMap.getCameraPosition());
outState.putParcelable(KEY_LOCATION, mLastKnownLocation);
super.onSaveInstanceState(outState);
}
}
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mMapview = (MapView) view.findViewById(R.id.mapView);
if (mMapview!=null){
mMapview.onCreate(null);
mMapview.onResume();
mMapview.getMapAsync(this);
}
}
@Override
public void onMapReady(final GoogleMap googleMap) {
mGoogleMap=googleMap;
FirebaseFirestore mDatabase = FirebaseFirestore.getInstance();
CollectionReference mOrderRef = mDatabase.collection("Job Post1");
mOrderRef.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
@Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
for(QueryDocumentSnapshot documentSnapshot : queryDocumentSnapshots) {
if(documentSnapshot.contains("lat") && documentSnapshot.contains("lon")) {
String lat = (String) documentSnapshot.get("lat");
String lon = (String) documentSnapshot.get("lon");
final String title = (String) documentSnapshot.get("title");
final String jobdate = (String) documentSnapshot.get("jobdate");
final String time = (String) documentSnapshot.get("time");
if(lat != null && lon != null && !lat.isEmpty() && !lon.isEmpty()) {
double latitude = Double.parseDouble(lat.trim());
double longitude = Double.parseDouble(lon.trim());
IconGenerator iconGen = new IconGenerator(getActivity());
googleMap.addMarker(new MarkerOptions().position(new LatLng(latitude, longitude)).icon(BitmapDescriptorFactory.fromBitmap(iconGen.makeIcon(title))));
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(latitude, longitude), 18.0f));
googleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
updateBottomSheetContent(marker);
return false;
}
});
googleMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
@Override
public void onMapClick(LatLng latLng) {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
}
});
}
}
}
}
});
}
private void updateBottomSheetContent(Marker marker) {
TextView name = (TextView) bottomSheet.findViewById(R.id.detail_name);
name.setText(marker.getTitle());
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}
}
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"
tools:context="part.time.job.v2.LabourFragment">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:itemBackground="@color/purple"
app:itemIconTint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/nav_items"
tools:ignore="MissingConstraints" />
<com.google.android.gms.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="675dp"
tools:ignore="MissingConstraints" />
<include layout="@layout/bottom_sheet" />
</androidx.constraintlayout.widget.ConstraintLayout>
bottom_sheet xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
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:fitsSystemWindows="true">
<androidx.core.widget.NestedScrollView android:id="@+id/bottom_sheet"
android:layout_width="match_parent" android:layout_height="550dp"
android:background="@android:color/white" android:clipToPadding="true"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<TextView android:id="@+id/detail_name" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_margin="25dp"
android:layout_weight="3" android:gravity="center_vertical"
android:textAppearance="?android:textAppearanceLarge" />
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
您的 xml 中没有具有 "R.id.bottom_sheet" ID 的视图,但您正试图通过 findViewbyId() 获取视图,这就是为什么 bottomSheet 变量具有空值以及 bottomsheetbehaviour 值的原因null 并在尝试访问其上的任何方法时产生空指针异常。在您的情况下,以下行:
bottomSheetBehavior.setPeekHeight(200);
我检查了您的代码,它按预期显示底部 sheet。但是也有一些问题。
- 底部Sheet的大部分隐藏在
BottomNavigationView
后面。略微克服这种增加PeekHeight
。
bottomSheetBehavior.setPeekHeight(300);
- 您将底部 Sheet 容器的
BackgroundColor
设置为android:color/white
。尝试将其更改为不同的颜色。
android:background="@android:color/holo_green_light"
- 您将
detail_name
的layout_width
设置为0dp
,这使视图不可见。努力做到match_parent
android:layout_width="match_parent"
这是底部的完整布局 Sheet 容器:
<androidx.core.widget.NestedScrollView
android:id="@+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="550dp"
android:background="@android:color/holo_green_light"
android:clipToPadding="true"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<TextView
android:id="@+id/detail_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="25dp"
android:layout_weight="3"
android:gravity="center_vertical"
android:textAppearance="?android:textAppearanceLarge" />
</androidx.core.widget.NestedScrollView>
输出: