我正在开发一个具有应用功能的应用,该功能可在Google地图上将团队显示为标记。
我可以将自己显示为在移动时会更新的标记,而其他人则可以显示为片段上的标记。
问题是我进入MapFragment的标记仅在第一次显示。 我导航到另一个片段并返回地图时,我看到了一个没有标记和缩放按钮的EMPTY地图。
尝试#3,请查看以前的实现的历史记录,这些记录稍有不同:
我的片段布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/incident_map"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.google.android.gms.maps.SupportMapFragment"
/>
<ProgressBar
android:id="@+id/incident_map_progress_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"/>
<!--<com.google.android.gms.maps.MapView
android:id="@+id/incident_map"
android:layout_width="match_parent"
android:layout_height="match_parent" />-->
</RelativeLayout>
我的IncidentMapFragment代码,现在根据用户反馈进行更新。更新是最小的。而不是使用onActivityCreated()在onResume()即时消息上,而不是使用onSaveInstanceState()在onPause()即时消息上,请参见下面的更新代码
package com.xyz.fragments;
//i did not include imports
//Based on https://developers.google.com/maps/documentation/android-sdk/map-with-marker
public class IncidentMapFragment extends Fragment implements OnMapReadyCallback {
public static final String TAG = "IncidentMapFragment";
private static IncidentMapFragment incidentMapFragment = null;
public static IncidentMapFragment instance() {
if (incidentMapFragment==null)
{
incidentMapFragment = new IncidentMapFragment();
}
return incidentMapFragment;
}
private MapView mapView;
private static GoogleMap map;
private ProgressBar progressBar;
private SupportMapFragment mapFragment;
public static final int UPDATE_MY_CURRENT_LOCATION = 0;
public static final int UPDATE_MY_TEAMS_CURRENT_LOCATIONS = 1;
public static final int UPDATE_ALL_TEAMS_CURRENT_LOCATIONS = 2;
public static final int UPDATE_ALL_LOCATION_BASED_EVENTS = 3;
private static Context context;
private int MY_DEFINED_ACCESS_COARSE_LOCATION_CALLBACK_RESULT = 1;
private int MY_DEFINED_ACCESS_FINE_LOCATION_CALLBACK_RESULT = 2;
boolean flagCoarseLocation = false;
boolean flagFineLocation = false;
private WamsUnitVehicleUnitRelationshipDao vehicleUnitRelationshipDao = new WamsUnitVehicleUnitRelationshipDaoImpl();
private static WamsUnitVehicleUnitRelationship newVehicleUnitRelationship = null;
private static CameraPosition cp;
private static Bundle markersBundle = new Bundle();
private static Bundle saveStateBundle = new Bundle();
private boolean createdStateInDestroyView = false;
private static final String SAVED_BUNDLE_TAG = "IncidentMapFragment_SAVE_STATE";
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.incident_map_fragment, container, false);
context = rootView.getContext();
progressBar = rootView.findViewById(R.id.incident_map_progress_bar);
try {
FragmentManager fm = getActivity().getSupportFragmentManager();
if (savedInstanceState == null) {
mapFragment = SupportMapFragment.newInstance();
fm.beginTransaction().replace(R.id.incident_map, mapFragment,TAG).commit();
}
else {
mapFragment = (SupportMapFragment) fm
.findFragmentByTag(TAG);
}
if (savedInstanceState!=null) {
saveStateBundle = savedInstanceState.getBundle(SAVED_BUNDLE_TAG);
//restore camera
cp = saveStateBundle.getParcelable("cp");
//restore bundle of markers
markersBundle = saveStateBundle.getParcelable("markersBundle");
if (cp!=null && markersBundle!=null)
{
reDrawMarkers(markersBundle);
}
}
} catch (Exception exc) {
Log.e(TAG, exc.getMessage());
exc.printStackTrace();
}
return rootView;
}
@Override
public void onActivityCreated(Bundle bundle) {
super.onActivityCreated(bundle);
Log.i(TAG,"onActivityCreated()");
newVehicleUnitRelationship = vehicleUnitRelationshipDao.getWamsUnitVehicleUnitRelationship(NgfrApp.getInstance().getUniqueDeviceId());
if (mapFragment!=null) {
mapFragment.getMapAsync(this);
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
Log.i(TAG,"onSaveInstanceState()");
outState.putBundle(SAVED_BUNDLE_TAG, saveState());
createdStateInDestroyView = false;
super.onSaveInstanceState(outState);
}
@Override
public void onDestroyView() {
super.onDestroyView();
saveStateBundle = saveState();
createdStateInDestroyView = true;
cp = null;
markersBundle = null;
}
private Bundle saveState() {
Bundle state = new Bundle();
state.putParcelable("cp", cp);
state.putParcelable("markersBundle", markersBundle);
return state;
}
/*Handler used by outside class such as MqttBroker.
1) UPDATE_MY_CURRENT_LOCATION. When my location service send a update location(lat,long) call updateMarkersOnMap() which creates a new AsyncTask to update teh display
2) UPDATE_MY_TEAMS_CURRENT_LOCATIONS. When the MQTT dservice gets new location(lat,long) call updateMarkersOnMap() which creates a new AsyncTask to update teh display
3) UPDATE_ALL_TEAMS_CURRENT_LOCATIONS, not implemented.
*/
public static final Handler updateIncidentMap = new Handler(Looper.getMainLooper()) {
public void handleMessage(Message msg) {
//if the context is null then the MapFragment & GoogleMap objects are NOT instantiated and updating the maps non-existant UI will cause exceptions, NPE, and crashes!
if (context != null) {
Location myCurrentLocation = null;
final int what = msg.what;
switch (what) {
case UPDATE_MY_CURRENT_LOCATION:
Log.i(TAG,"UPDATE_MY_CURRENT_LOCATION");
myCurrentLocation = (Location) msg.obj;
if (map != null) {
updateMarkersOnMap(map,markersBundle, myCurrentLocation.getLatitude(),myCurrentLocation.getLongitude(),newVehicleUnitRelationship.getWamsId(), newVehicleUnitRelationship.getVehicleUnitId());
}
break;
case UPDATE_MY_TEAMS_CURRENT_LOCATIONS:
Log.i(TAG,"UPDATE_MY_TEAMS_CURRENT_LOCATIONS");
if (map != null) {
WamsLocationMarker wamsLocationMarker = (WamsLocationMarker) msg.obj;
updateMarkersOnMap(map, markersBundle,wamsLocationMarker.getLat(),wamsLocationMarker.getLon(), wamsLocationMarker.getClientId(), wamsLocationMarker.getVehicleId());
//mock team
mockTeam(map,markersBundle, wamsLocationMarker.getLat(),wamsLocationMarker.getLon());
}
break;
case UPDATE_ALL_TEAMS_CURRENT_LOCATIONS:
break;
default:
break;
}
} //end if context is NOt null
} //end handleMessage
};
/*I added the @SuppressLint("MissingPermission") because we are handling this in permissionHelper(getActivity()),
and the IDE is too naive to know*/
@SuppressLint("MissingPermission")
@Override
public void onMapReady(GoogleMap googleMap) {
Log.i(TAG, "onMapReady()");
try {
//remove progress bar
progressBar.setVisibility(GONE);
//initially zoom in my location
map = googleMap;
if (permissionHelper(getActivity()))
{
map.setMyLocationEnabled(true);
map.getUiSettings().setZoomControlsEnabled(true);
map.getUiSettings().setCompassEnabled(true);
}
if (cp != null) {
map.moveCamera(CameraUpdateFactory.newCameraPosition(cp));
cp = null;
}
reDrawMarkers(markersBundle);
}
catch (NullPointerException exc)
{
exc.printStackTrace();
}
}
private static void updateMarkersOnMap(GoogleMap map,Bundle bundle, double lat,double log, String id, String vehicleId) {
final GoogleMap _map = map;
final double _lat = lat;
final double _log = log;
final String _id = id;
final String _vehicleId = vehicleId;
new AsyncTask < Void, Void, WamsLocationMarker > () {
boolean update = false;
@Override
protected WamsLocationMarker doInBackground(Void...voids) {
Marker marker = null;
WamsLocationMarker wamsLocationMarker=null;
try {
Log.i(TAG,"async map: "+map);
if (_map != null && bundle!=null)
{
Log.i(TAG,_map.toString());
//if the wamsLocationMarker exists, then UPDATE
if (bundle.containsKey(_id)) {
Log.i(TAG,"markersBundle.containsKey(_id): "+ bundle.containsKey(_id));
Log.i(TAG,"update true");
//get from hashmap
wamsLocationMarker = (WamsLocationMarker)bundle.get(_id);
update = true;
} else {
//add to map
//if the ids are equal then this is YOU
wamsLocationMarker = new WamsLocationMarkerFactory().createWamsLocationMarker(_id, _vehicleId, _lat, _log);
Log.i(TAG,"WamsLocationMarker");
Log.i(TAG,"update false");
}
} else {
Log.e(TAG, " updateMarkersOnMap() map is " + _map);
}
}
catch (NullPointerException exc)
{
exc.printStackTrace();
}
return wamsLocationMarker;
}
@Override
protected void onPostExecute(WamsLocationMarker wamsLocationMarker) {
super.onPostExecute(wamsLocationMarker);
try {
if (wamsLocationMarker != null) {
Log.i(TAG,"onPostExecute() update:"+update+",wamsLocationMarker:"+wamsLocationMarker);
if (update) {
Log.i(TAG,"onPostExecute() update:"+update);
//UPDATE wth new lat & long if the markers coordinates have change, else dont redraw, beacuse the draw causes a refresh affect & its also a waste computationally
if (wamsLocationMarker.getMarker().getPosition().latitude != _lat && wamsLocationMarker.getMarker().getPosition().longitude != _log) {
LatLng latLng = new LatLng(_lat, _log);
//UPDATE THE MARKER POSITION
wamsLocationMarker.getMarker().setPosition(latLng);
}
} else {
//ADD A NEW MARKER
Marker marker = _map.addMarker(wamsLocationMarker.getMakerOptions());
Log.i(TAG,"ASYNC MARKER:"+marker.getId());
wamsLocationMarker.setMarker(marker);
markersBundle.remove(wamsLocationMarker.getClientId());
markersBundle.putParcelable(wamsLocationMarker.getClientId(), wamsLocationMarker);
}
}
}
catch (NullPointerException exc)
{
exc.printStackTrace();
}
}
}.execute();
}
public void reDrawMarkers(Bundle bundle) {
Log.i(TAG,"reDrawMarkers()");
if (bundle!=null) {
Set<String> keys = bundle.keySet();
WamsLocationMarker wamsLocationMarker = null;
for (String k : keys) {
Log.i(TAG, "key:" + k);
wamsLocationMarker = (WamsLocationMarker) bundle.getParcelable(k);
updateMarkersOnMap(map, bundle, wamsLocationMarker.getLat(), wamsLocationMarker.getLon(), wamsLocationMarker.getClientId(), wamsLocationMarker.getVehicleId());
}
}
}
//Just for test - Im mocking some there points to represent other teams mates on the map, this is just for testing
private static void mockTeam(GoogleMap map, Bundle bundle, double _lat, double _log) {
updateMarkersOnMap(map,bundle, _lat+0.001, _log , "mock111111111", newVehicleUnitRelationship.getVehicleUnitId());
updateMarkersOnMap(map,bundle,_lat, _log+0.001 ,"mock222222222", newVehicleUnitRelationship.getVehicleUnitId());
updateMarkersOnMap(map,bundle, _lat-0.001, _log,"mock33333333", newVehicleUnitRelationship.getVehicleUnitId());
updateMarkersOnMap(map,bundle, _lat, _log-0.001,"mock444444444", newVehicleUnitRelationship.getVehicleUnitId());
}
//Ask the user if required & attempt to grant required location services
private boolean permissionHelper(Activity activity) {
String permission = Manifest.permission.ACCESS_COARSE_LOCATION;
int res = getContext().checkCallingPermission(permission);
//if the required coarse & fine permissions are granted then return true else proceed to get the permissions
if (res == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "ACCESS_COARSE_LOCATION GRANTED");
permission = Manifest.permission.ACCESS_FINE_LOCATION;
res = getContext().checkCallingPermission(permission);
if (res == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "ACCESS_FINE_LOCATION GRANTED");
flagFineLocation = true;
} else {
Log.i(TAG, "ACCESS_FINE_LOCATION NOT GRANTED");
}
flagCoarseLocation = true;
}
//prompt user for COARSE location permission. If the user cancel then display toast & navigate back
if (!flagCoarseLocation) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
Manifest.permission.ACCESS_COARSE_LOCATION)) {
dialogForCoarsePermission();
} else {
ActivityCompat.requestPermissions(activity,
new String[] {
Manifest.permission.ACCESS_COARSE_LOCATION
},
MY_DEFINED_ACCESS_COARSE_LOCATION_CALLBACK_RESULT);
flagCoarseLocation = true;
}
}
//prompt user for FINE location permission. If the user cancel then display toast & navigate back
if (!flagFineLocation) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
Manifest.permission.ACCESS_FINE_LOCATION)) {
dialogForFinePermission();
} else {
ActivityCompat.requestPermissions(activity,
new String[] {
Manifest.permission.ACCESS_COARSE_LOCATION
},
MY_DEFINED_ACCESS_FINE_LOCATION_CALLBACK_RESULT);
flagFineLocation = true;
}
}
if (!flagCoarseLocation)
{
Log.i(TAG, "ACCESS_COARSE_LOCATION NOT GRANTED");
}
else
{
Log.i(TAG, "ACCESS_COARSE_LOCATION GRANTED");
}
if (!flagFineLocation)
{
Log.i(TAG, "ACCESS_FINE_LOCATION NOT GRANTED");
}
else
{
Log.i(TAG, "ACCESS_FINE_LOCATION GRANTED");
}
return (flagCoarseLocation && flagFineLocation);
}
private void dialogForCoarsePermission() {
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
//Yes button clicked
ActivityCompat.requestPermissions(getActivity(),
new String[] {
Manifest.permission.ACCESS_COARSE_LOCATION
},
MY_DEFINED_ACCESS_COARSE_LOCATION_CALLBACK_RESULT);
break;
case DialogInterface.BUTTON_NEGATIVE:
//No button clicked
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage("The map requires coarse location permission.Please it by pressing Yes. ").setPositiveButton("Yes", dialogClickListener)
.setNegativeButton("No", dialogClickListener).show();
}
private void dialogForFinePermission() {
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
//Yes button clicked
ActivityCompat.requestPermissions(getActivity(),
new String[] {
Manifest.permission.ACCESS_FINE_LOCATION
},
MY_DEFINED_ACCESS_FINE_LOCATION_CALLBACK_RESULT);
break;
case DialogInterface.BUTTON_NEGATIVE:
//No button clicked
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage("The map ALSO requires fine location permission.Please it by pressing Yes. ").setPositiveButton("Yes", dialogClickListener)
.setNegativeButton("No", dialogClickListener).show();
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
Log.i(TAG, "onRequestPermissionsResult() request code:" + requestCode); // Log printed
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == MY_DEFINED_ACCESS_COARSE_LOCATION_CALLBACK_RESULT) {
flagCoarseLocation = true;
Toast.makeText(context, "Coarse location permission granted.", Toast.LENGTH_SHORT).show();
}
if (requestCode == MY_DEFINED_ACCESS_FINE_LOCATION_CALLBACK_RESULT) {
flagFineLocation = true;
Toast.makeText(context, "Fine location permission granted.", Toast.LENGTH_SHORT).show();
}
}
}
我仅为此片段提供了GUI和相应的日志。
我第一次导航到片段。一切正常,您会看到转到我的位置按钮和缩放按钮:
现在,当我导航到名为“服务”的第一个片段或将应用程序在后台放置一会儿时,就会出现问题。我再也看不到这些标记,并且添加了通过网络发送的新位置更新,但是没有绘制出来。地图缩放按钮也丢失了!
08-16 08:06:03.358 1314-1314/com.xyz I/IncidentMapFragment: onSaveInstanceState()
08-16 08:06:03.836 1314-1314/com.xyz I/IncidentMapFragment: reDrawMarkers()
key:015140000100161
key:mock33333333
key:mock444444444
key:mock111111111
key:mock222222222
08-16 08:06:03.836 1314-1340/com.xyz I/IncidentMapFragment: async map: com.google.android.gms.maps.GoogleMap@1e72e3b
com.google.android.gms.maps.GoogleMap@1e72e3b
markersBundle.containsKey(_id): true
update true
async map: com.google.android.gms.maps.GoogleMap@1e72e3b
com.google.android.gms.maps.GoogleMap@1e72e3b
markersBundle.containsKey(_id): true
update true
async map: com.google.android.gms.maps.GoogleMap@1e72e3b
com.google.android.gms.maps.GoogleMap@1e72e3b
markersBundle.containsKey(_id): true
update true
async map: com.google.android.gms.maps.GoogleMap@1e72e3b
com.google.android.gms.maps.GoogleMap@1e72e3b
markersBundle.containsKey(_id): true
update true
async map: com.google.android.gms.maps.GoogleMap@1e72e3b
com.google.android.gms.maps.GoogleMap@1e72e3b
markersBundle.containsKey(_id): true
update true
08-16 08:06:03.837 1314-1314/com.xyz I/IncidentMapFragment: onActivityCreated()
08-16 08:06:03.851 1314-1314/com.xyz I/IncidentMapFragment: onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@19b04df
onPostExecute() update:true
onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@47c13e2
onPostExecute() update:true
onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@6b22ea9
onPostExecute() update:true
onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@e387818
08-16 08:06:03.852 1314-1314/com.xyz I/IncidentMapFragment: onPostExecute() update:true
onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@dab0d7
onPostExecute() update:true
onMapReady()
ACCESS_COARSE_LOCATION GRANTED
ACCESS_FINE_LOCATION GRANTED
08-16 08:06:03.853 1314-1314/com.xyz I/IncidentMapFragment: reDrawMarkers()
08-16 08:06:03.854 1314-1314/com.xyz I/IncidentMapFragment: key:015140000100161
key:mock33333333
key:mock444444444
key:mock111111111
key:mock222222222
08-16 08:06:03.854 1314-1338/com.xyz I/IncidentMapFragment: async map: com.google.android.gms.maps.GoogleMap@b7d1933
com.google.android.gms.maps.GoogleMap@b7d1933
markersBundle.containsKey(_id): true
update true
async map: com.google.android.gms.maps.GoogleMap@b7d1933
com.google.android.gms.maps.GoogleMap@b7d1933
markersBundle.containsKey(_id): true
update true
async map: com.google.android.gms.maps.GoogleMap@b7d1933
com.google.android.gms.maps.GoogleMap@b7d1933
markersBundle.containsKey(_id): true
update true
async map: com.google.android.gms.maps.GoogleMap@b7d1933
com.google.android.gms.maps.GoogleMap@b7d1933
markersBundle.containsKey(_id): true
update true
async map: com.google.android.gms.maps.GoogleMap@b7d1933
com.google.android.gms.maps.GoogleMap@b7d1933
markersBundle.containsKey(_id): true
update true
08-16 08:06:03.865 1314-1314/com.xyz I/IncidentMapFragment: onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@19b04df
onPostExecute() update:true
onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@47c13e2
onPostExecute() update:true
onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@6b22ea9
onPostExecute() update:true
onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@e387818
onPostExecute() update:true
onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@dab0d7
onPostExecute() update:true
查看调试窗口:状态已恢复但未绘制 我的日志显示标记被重新添加,没有错误或异常,但是没有显示?**为什么?我该如何解决呢?
谢谢
答案 0 :(得分:0)
很明显,第一个片段正在调用onResume(),onMapReady(),onPause(),这将重置地图
您可能错误地处理了它,因此我仍然认为问题仍然与活动/片段状态更改有关。
一个简单的调整方法就是查看是否是由于片段状态更改而导致的,这是每当用户放置标记时将所有值存储到SharedPreference,并在每次状态更改时将它们分配给地图(onPause,onResume等) )。
有关片段状态更改的说明
不同的事件(一些用户触发的事件和一些系统触发的事件)可以导致“活动/片段”从一种状态转换为另一种状态,以及重置活动/片段。因此,在android中,您需要处理活动的不同状态以及片段。
对于处理活动状态更改,您可以引用Android doc。
针对您的情况:
关于如何handle Fragment State Change,有一个非常非常好的示例和详细信息。
希望对您有帮助!
答案 1 :(得分:0)
您不必像其他建议那样存储状态更改。但是MapFragment
实现是错误的。您必须修复它。
将onActivityCreated()
中的代码更改为此并将其移至onCreate()
。您完全不必覆盖onActivityCreated
。
mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.incident_map);
mapFragment.getMapAsync(this);
答案 2 :(得分:0)
假设您提供的代码正确,则用此代码替换。试试吧,我做了一些修改
package com.xyz.fragments;
//I didnt not include imports in order to have a smaller post on Stackoverflow
//Based on https://developers.google.com/maps/documentation/android-sdk/map-with-marker
public class IncidentMapFragment extends Fragment implements OnMapReadyCallback {
public static final String TAG = "IncidentMapFragment";
private static IncidentMapFragment incidentMapFragment = null;
public static IncidentMapFragment instance() {
if (incidentMapFragment==null)
{
incidentMapFragment = new IncidentMapFragment();
}
return incidentMapFragment;
}
private MapView mapView;
private static GoogleMap map;
private ProgressBar progressBar;
private SupportMapFragment mapFragment;
public static final int UPDATE_MY_CURRENT_LOCATION = 0;
public static final int UPDATE_MY_TEAMS_CURRENT_LOCATIONS = 1;
public static final int UPDATE_ALL_TEAMS_CURRENT_LOCATIONS = 2;
public static final int UPDATE_ALL_LOCATION_BASED_EVENTS = 3;
private static Context context;
private int MY_DEFINED_ACCESS_COARSE_LOCATION_CALLBACK_RESULT = 1;
private int MY_DEFINED_ACCESS_FINE_LOCATION_CALLBACK_RESULT = 2;
boolean flagCoarseLocation = false;
boolean flagFineLocation = false;
private static CameraPosition cp;
private WamsUnitVehicleUnitRelationshipDao vehicleUnitRelationshipDao = new WamsUnitVehicleUnitRelationshipDaoImpl();
private static WamsUnitVehicleUnitRelationship newVehicleUnitRelationship = null;
private static Bundle bundleOfMarkers = new Bundle();
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.incident_map_fragment, container, false);
context = rootView.getContext();
Bundle bundleOfMarkers = new Bundle();
progressBar = rootView.findViewById(R.id.incident_map_progress_bar);
try {
FragmentManager fm = getActivity().getSupportFragmentManager();
mapFragment = (SupportMapFragment) fm.findFragmentById(R.id.incident_map);
if (mapFragment == null) {
mapFragment = SupportMapFragment.newInstance();
fm.beginTransaction().replace(R.id.incident_map, mapFragment).commit();
}
} catch (Exception exc) {
Log.e(TAG, exc.getMessage());
exc.printStackTrace();
}
return rootView;
}
@Override
public void onActivityCreated(Bundle bundle) {
super.onActivityCreated(bundle);
Log.i(TAG,"onActivityCreated()");
newVehicleUnitRelationship = vehicleUnitRelationshipDao.getWamsUnitVehicleUnitRelationship(NgfrApp.getInstance().getUniqueDeviceId());
if (bundle!=null) {
bundleOfMarkers = bundle.getBundle("bundleOfMarkers");
cp = bundle.getParcelable("cp");
}
if (mapFragment!=null) {
mapFragment.getMapAsync(this);
}
}
/*Handler used by outside class such as MqttBroker.
1) UPDATE_MY_CURRENT_LOCATION. When my location service send a update location(lat,long) call updateMarkersOnMap() which creates a new AsyncTask to update teh display
2) UPDATE_MY_TEAMS_CURRENT_LOCATIONS. When the MQTT dservice gets new location(lat,long) call updateMarkersOnMap() which creates a new AsyncTask to update teh display
3) UPDATE_ALL_TEAMS_CURRENT_LOCATIONS, not implemented.
*/
public static final Handler updateIncidentMap = new Handler(Looper.getMainLooper()) {
public void handleMessage(Message msg) {
//if the context is null then the MapFragment & GoogleMap objects are NOT instantiated and updating the maps non-existant UI will cause exceptions, NPE, and crashes!
if (context != null) {
Location myCurrentLocation = null;
final int what = msg.what;
switch (what) {
case UPDATE_MY_CURRENT_LOCATION:
Log.i(TAG,"UPDATE_MY_CURRENT_LOCATION");
myCurrentLocation = (Location) msg.obj;
if (map != null) {
updateMarkersOnMap(map, myCurrentLocation.getLatitude(),myCurrentLocation.getLongitude(),newVehicleUnitRelationship.getWamsId(), newVehicleUnitRelationship.getVehicleUnitId());
}
break;
case UPDATE_MY_TEAMS_CURRENT_LOCATIONS:
Log.i(TAG,"UPDATE_MY_TEAMS_CURRENT_LOCATIONS");
if (map != null) {
WamsLocationMarker wamsLocationMarker = (WamsLocationMarker) msg.obj;
updateMarkersOnMap(map, wamsLocationMarker.getLat(),wamsLocationMarker.getLon(), wamsLocationMarker.getClientId(), wamsLocationMarker.getVehicleId());
//mock team
mockTeam(map, wamsLocationMarker.getLat(),wamsLocationMarker.getLon());
}
break;
case UPDATE_ALL_TEAMS_CURRENT_LOCATIONS:
break;
default:
break;
}
} //end if context is NOt null
} //end handleMessage
};
/*I added the @SuppressLint("MissingPermission") because we are handling this in permissionHelper(getActivity()),
and the IDE is too naive to know*/
@SuppressLint("MissingPermission")
@Override
public void onMapReady(GoogleMap googleMap) {
Log.i(TAG, "onMapReady()");
try {
//remove progress bar
progressBar.setVisibility(GONE);
//initially zoom in my location
map = googleMap;
if (permissionHelper(getActivity()))
{
map.setMyLocationEnabled(true);
map.getUiSettings().setZoomControlsEnabled(true);
map.getUiSettings().setCompassEnabled(true);
}
if (cp != null) {
map.moveCamera(CameraUpdateFactory.newCameraPosition(cp));
cp = null;
}
myResume();
}
catch (NullPointerException exc)
{
exc.printStackTrace();
}
}
private static void updateMarkersOnMap(GoogleMap map,double lat,double log, String id, String vehicleId) {
final GoogleMap _map = map;
final double _lat = lat;
final double _log = log;
final String _id = id;
final String _vehicleId = vehicleId;
new AsyncTask < Void, Void, WamsLocationMarker > () {
boolean update = false;
@Override
protected WamsLocationMarker doInBackground(Void...voids) {
Marker marker = null;
WamsLocationMarker wamsLocationMarker=null;
try {
Log.i(TAG,"async map: "+map);
if (_map != null)
{
Log.i(TAG,_map.toString());
//if the wamsLocationMarker exists, then UPDATE
if (bundleOfMarkers.containsKey(_id)) {
Log.i(TAG,"bundleOfMarkers.containsKey(_id): "+bundleOfMarkers.containsKey(_id));
Log.i(TAG,"update true");
//get from hashmap
wamsLocationMarker = (WamsLocationMarker)bundleOfMarkers.get(_id);
update = true;
} else {
//add to map
//if the ids are equal then this is YOU
wamsLocationMarker = new WamsLocationMarkerFactory().createWamsLocationMarker(_id, _vehicleId, _lat, _log);
Log.i(TAG,"WamsLocationMarker");
Log.i(TAG,"update false");
}
} else {
Log.e(TAG, " updateMarkersOnMap() map is " + _map);
}
}
catch (NullPointerException exc)
{
exc.printStackTrace();
}
return wamsLocationMarker;
}
@Override
protected void onPostExecute(WamsLocationMarker wamsLocationMarker) {
super.onPostExecute(wamsLocationMarker);
try {
if (wamsLocationMarker != null) {
Log.i(TAG,"onPostExecute() update:"+update+",wamsLocationMarker:"+wamsLocationMarker);
if (update) {
Log.i(TAG,"onPostExecute() update:"+update);
//UPDATE wth new lat & long if the markers coordinates have change, else dont redraw, beacuse the draw causes a refresh affect & its also a waste computationally
if (wamsLocationMarker.getMarker().getPosition().latitude != _lat && wamsLocationMarker.getMarker().getPosition().longitude != _log) {
LatLng latLng = new LatLng(_lat, _log);
//UPDATE THE MARKER POSITION
wamsLocationMarker.getMarker().setPosition(latLng);
}
} else {
//ADD A NEW MARKER
Marker marker = _map.addMarker(wamsLocationMarker.getMakerOptions());
Log.i(TAG,"ASYNC MARKER:"+marker.getId());
wamsLocationMarker.setMarker(marker);
bundleOfMarkers.remove(wamsLocationMarker.getClientId());
bundleOfMarkers.putParcelable(wamsLocationMarker.getClientId(), wamsLocationMarker);
}
}
}
catch (NullPointerException exc)
{
exc.printStackTrace();
}
}
}.execute();
}
/*I also tried using onSaveInstanceState & onViewStateRestored. I saved the markers on map in Bundle & put them in parceable.
However I was unable to redraw the map*/
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
Log.i(TAG,"onSaveInstanceState()");
myPause();
outState.putBundle("bundleOfMarkers", bundleOfMarkers);
outState.putParcelable("cp",cp);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
Log.i(TAG,"onRestoreInstanceState()");
super.onRestoreInstanceState(savedInstanceState);
bundleOfMarkers = savedInstanceState.getBundle("bundleOfMarkers");
myResume();
}
/*
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
Log.i(TAG,"onViewStateRestored(),savedInstanceState:"+savedInstanceState);
super.onViewStateRestored(savedInstanceState);
}*/
/* @Override
public void onResume() {
super.onResume();
Log.i(TAG,"onResume()");
if (mapFragment!=null) {
mapFragment.getMapAsync(this);
}
newVehicleUnitRelationship = vehicleUnitRelationshipDao.getWamsUnitVehicleUnitRelationship(NgfrApp.getInstance().getUniqueDeviceId());
}*/
/*@Override
public void onPause() {
Log.i(TAG,"onPause()");
super.onPause();
myPause();
}*/
public void myResume() {
Log.i(TAG,"myResume()");
Set<String> keys = bundleOfMarkers.keySet();
WamsLocationMarker wamsLocationMarker = null;
for (String k : keys) {
Log.i(TAG,"key:"+k);
wamsLocationMarker = (WamsLocationMarker) bundleOfMarkers.getParcelable(k);
updateMarkersOnMap(map, wamsLocationMarker.getLat(), wamsLocationMarker.getLon(), wamsLocationMarker.getClientId(), wamsLocationMarker.getVehicleId());
}
}
public void myPause()
{
Log.i(TAG,"myPause()");
if (map!=null) {
//lets get the camera position & markers
cp = map.getCameraPosition();
}
//keys in hashmap
Set<String> keys = IncidentMapFragment.bundleOfMarkers.keySet();
WamsLocationMarker wamsLocationMarker = null;
for (String k : keys) {
wamsLocationMarker = (WamsLocationMarker) IncidentMapFragment.bundleOfMarkers.get(k);
bundleOfMarkers.putParcelable(k,wamsLocationMarker);
}
}
//Just for test - Im mocking some there points to represent other teams mates on the map, this is just for testing
private static void mockTeam(GoogleMap map, double _lat, double _log) {
updateMarkersOnMap(map, _lat+0.001, _log , "mock111111111", newVehicleUnitRelationship.getVehicleUnitId());
updateMarkersOnMap(map,_lat, _log+0.001 ,"mock222222222", newVehicleUnitRelationship.getVehicleUnitId());
updateMarkersOnMap(map, _lat-0.001, _log,"mock33333333", newVehicleUnitRelationship.getVehicleUnitId());
updateMarkersOnMap(map, _lat, _log-0.001,"mock444444444", newVehicleUnitRelationship.getVehicleUnitId());
}
//Ask the user if required & attempt to grant required location services
private boolean permissionHelper(Activity activity) {
String permission = Manifest.permission.ACCESS_COARSE_LOCATION;
int res = getContext().checkCallingPermission(permission);
//if the required coarse & fine permissions are granted then return true else proceed to get the permissions
if (res == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "ACCESS_COARSE_LOCATION GRANTED");
permission = Manifest.permission.ACCESS_FINE_LOCATION;
res = getContext().checkCallingPermission(permission);
if (res == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "ACCESS_FINE_LOCATION GRANTED");
flagFineLocation = true;
} else {
Log.i(TAG, "ACCESS_FINE_LOCATION NOT GRANTED");
}
flagCoarseLocation = true;
}
//prompt user for COARSE location permission. If the user cancel then display toast & navigate back
if (!flagCoarseLocation) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
Manifest.permission.ACCESS_COARSE_LOCATION)) {
dialogForCoarsePermission();
} else {
ActivityCompat.requestPermissions(activity,
new String[] {
Manifest.permission.ACCESS_COARSE_LOCATION
},
MY_DEFINED_ACCESS_COARSE_LOCATION_CALLBACK_RESULT);
flagCoarseLocation = true;
}
}
//prompt user for FINE location permission. If the user cancel then display toast & navigate back
if (!flagFineLocation) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
Manifest.permission.ACCESS_FINE_LOCATION)) {
dialogForFinePermission();
} else {
ActivityCompat.requestPermissions(activity,
new String[] {
Manifest.permission.ACCESS_COARSE_LOCATION
},
MY_DEFINED_ACCESS_FINE_LOCATION_CALLBACK_RESULT);
flagFineLocation = true;
}
}
if (!flagCoarseLocation)
{
Log.i(TAG, "ACCESS_COARSE_LOCATION NOT GRANTED");
}
else
{
Log.i(TAG, "ACCESS_COARSE_LOCATION GRANTED");
}
if (!flagFineLocation)
{
Log.i(TAG, "ACCESS_FINE_LOCATION NOT GRANTED");
}
else
{
Log.i(TAG, "ACCESS_FINE_LOCATION GRANTED");
}
return (flagCoarseLocation && flagFineLocation);
}
private void dialogForCoarsePermission() {
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
//Yes button clicked
ActivityCompat.requestPermissions(getActivity(),
new String[] {
Manifest.permission.ACCESS_COARSE_LOCATION
},
MY_DEFINED_ACCESS_COARSE_LOCATION_CALLBACK_RESULT);
break;
case DialogInterface.BUTTON_NEGATIVE:
//No button clicked
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage("The map requires coarse location permission.Please it by pressing Yes. ").setPositiveButton("Yes", dialogClickListener)
.setNegativeButton("No", dialogClickListener).show();
}
private void dialogForFinePermission() {
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
//Yes button clicked
ActivityCompat.requestPermissions(getActivity(),
new String[] {
Manifest.permission.ACCESS_FINE_LOCATION
},
MY_DEFINED_ACCESS_FINE_LOCATION_CALLBACK_RESULT);
break;
case DialogInterface.BUTTON_NEGATIVE:
//No button clicked
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage("The map ALSO requires fine location permission.Please it by pressing Yes. ").setPositiveButton("Yes", dialogClickListener)
.setNegativeButton("No", dialogClickListener).show();
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
Log.i(TAG, "onRequestPermissionsResult() request code:" + requestCode); // Log printed
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == MY_DEFINED_ACCESS_COARSE_LOCATION_CALLBACK_RESULT) {
flagCoarseLocation = true;
Toast.makeText(context, "Coarse location permission granted.", Toast.LENGTH_SHORT).show();
}
if (requestCode == MY_DEFINED_ACCESS_FINE_LOCATION_CALLBACK_RESULT) {
flagFineLocation = true;
Toast.makeText(context, "Fine location permission granted.", Toast.LENGTH_SHORT).show();
}
}
}
**Fragment Adapter class:**
package com.xyz.views;
//didnt include imports
public class MainActivityViewPagerAdapter extends FragmentStatePagerAdapter {
public MainActivityViewPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
Fragment returnFragment=null;
switch(position) {
case 0:
returnFragment = ServicesFragment.newInstance();
break;
case 1:
returnFragment = BiometricsFragment.newInstance();
break;
case 2:
returnFragment =IncidentMapFragment.newInstance();
break;
}
return returnFragment;
}
@Override
public int getCount() {
return 3;
}
public CharSequence getPageTitle(int position) {
CharSequence title=null;
switch (position) {
case 0:
title = "Services";
break;
case 1:
title = "Biometrics";
break;
case 2:
title = "Incident Map";
break;
}
return title;
}
}