【发布时间】:2018-09-26 18:57:25
【问题描述】:
我知道周围有类似的问题,但我无法找到可靠的答案,这是我的问题:有什么方法可以在没有 Google Map 参考的情况下将标记存储在 ArrayList(或任何其他存储),然后只需将它们添加到我的地图中?
背景: 我有一个应用程序,目前有大约 3,500 个标记。每个标记还有一个与之关联的数据(布尔数组存储每个标记的数据,用于根据用户交互使它们可见/不可见)。目前,我使用扩展 AsyncTask 的类来获取这些标记的位置和数据。加载完成后,我在主线程上使用这些数据创建我的标记。但是,这需要一些时间,并且会在创建标记并将其添加到地图时冻结 UI。我想在后台以某种方式执行此操作。
到目前为止我所尝试的: 我创建了另一个扩展 AsyncTask 的类,传入我的 LocationData 和我的 Google Map 对象。但是当我尝试在我的 Async 类中制作标记时出现错误。我收到一个运行时错误,说我需要在 UI 线程上执行此操作。
java.lang.RuntimeException: 执行时出错 doInBackground()
原因:com.google.maps.api.android.lib6.common.apiexception.c:不是 在主线程上
我还考虑过在后台创建 MarkerOptions 对象,然后使用它在主线程中创建标记;但是,我无法将标签添加到 MarkerOption,它需要添加到标记中。在这种情况下,我需要在主线程中再次遍历所有这些标签以添加标签,我觉得这并没有为我节省太多时间/资源。
对于如何创建这些标记并在不阻塞 UI 的情况下附加其标签的任何建议/帮助,我们将不胜感激?
提前致谢。
这是我的一些代码:
LocationLoader 类 (BinLocation 是我的 Location 类,每个对象都有布尔变量(标记标签)和 LatLng) 公共类 LocationLoader 扩展 AsyncTaskLoader> {
private String TAG = LocationLoader.class.getName();
String[] fileNameArray;
//ArrayLists
private ArrayList<BinLocation> mBinLocationArrayList = new ArrayList<>();
public LocationLoader(Context context, String... fileNames){
super(context);
//get the file names that was passed in
fileNameArray = fileNames;
}//LocationLoader
@Override
protected void onStartLoading() {
Log.v(TAG, "onStartLoading called");
forceLoad();
}//onStartLoading
@Override
public ArrayList<BinLocation> loadInBackground() {
Log.v(TAG, "loadInBackground called");
String path = "/storage/emulated/0/";
File file;
String output = "";
//Read data from file
for (int i = 0; i < fileNameArray.length; i++) {
file = new File(path + fileNameArray[i]);
try (Scanner scanner = new Scanner(file)) {
//first line of the text, containing the location and version
output = scanner.nextLine();
String prefix = (output.split(":"))[0];
String line;
while (scanner.hasNextLine()) {
line = scanner.nextLine();
String inputArray[] = line.split(",");
BinLocation binLocation = new BinLocation(
prefix + "-" + inputArray[0],
Double.parseDouble(inputArray[1]),
Double.parseDouble(inputArray[2]),
Integer.parseInt(inputArray[3].trim()),
Integer.parseInt(inputArray[4].trim()),
Integer.parseInt(inputArray[5].trim()),
Integer.parseInt(inputArray[6].trim()));
mBinLocationArrayList.add(binLocation);
}//while
} catch (Exception e) {
Log.e(TAG, "File read error: ", e);
}
}//for
Log.v(TAG, "readLocation finished");
Log.v(TAG, "ArrayList size: " + mBinLocationArrayList.size());
return mBinLocationArrayList;
}//loadInBackground
}//LocationLoader class
这是我的 MarkerLoader 类(我已经尝试过这个并得到了 doInBackground() 错误)。此外,现在这里没有用于将数据添加到标记的代码,但它在添加到地图后立即进入循环。
public class MarkerLoader extends AsyncTaskLoader<ArrayList<Marker>> {
private GoogleMap map;
private ArrayList<Marker> mMarkerArrayList = new ArrayList<>();
private ArrayList<MyLocation> mBinLocationArrayList = new ArrayList<>();
public MarkerLoader (Context context, GoogleMap map, ArrayList<BinLocation> binLocationArrayList) {
super(context);
this.map = map;
this.mBinLocationArrayList = binLocationArrayList;
}//MarkerLoader
@Override
protected void onStartLoading() {
Log.v(TAG, "onStartLoading called");
forceLoad();
}//onStartLoading
@Override
public ArrayList<Marker> loadInBackground() {
Log.v(TAG, "loadInBackground called");
Marker marker;
for (BinLocation binLocation : mMyLocationArrayList) {
marker = map.addMarker(new MarkerOptions()
.position(binLocation.getPosition()));
mMarkerArrayList.add(marker);
}
Log.v(TAG, "loadInBackground finished, with: " + mMarkerArrayList.size());
return mMarkerArrayList;
}
}
这是主 Activity 中的辅助函数(populateMap()),用于制作标记并将它们保存在 ArrayList 中
private void populateMap() {
if (!checkMapReady() || !mMapIsEmpty) {
return;
}//if Map Not ready
//Initialize ArrayList
mMarkerArrayList = new ArrayList<>();
/**
* This part uses the loop to go through each MyLocation object in the ArrayList, extract
* all the data, and set the markers
*/
//Check to make sure the BinLocation ArrayList is not empty otherwise we will crash
if (mBinLocationArrayList.isEmpty()) {
Log.w(TAG, "populateMap() terminated, mBinLocationArrayList empty");
return;
}//if BinLocation empty
//Safety check to clear the map before populating it
mMap.clear();
//create a markerMyLocation object
Marker mMaker;
//This goes through the ArrayList for every MyLocation object and sets up the markerMyLocation
for (BinLocation binLocation : mBinLocationArrayList) {
//get boolean values
boolean[] booleanValues = {binLocation.getGarbage(), binLocation.getContainer(),
binLocation.getPaper(), binLocation.getCompost()};
//Set different icon
switch (markerIconPreference) {
case "customIcon":
//custom icon
//Decide what icon to use
if (booleanValues[0] && !booleanValues[1] && !booleanValues[2] && !booleanValues[3]) {
//Make a new MarkerOptions object to add the data
//garbage markers
mMaker = mMap.addMarker(new MarkerOptions()
.title(binLocation.getId())
.position(binLocation.getPosition())
.icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_garbage))
.visible(garbageVisible));
} else {
//Make a new MarkerOptions object to add the data
//recycling markers
mMaker = mMap.addMarker(new MarkerOptions()
.title(binLocation.getId())
.position(binLocation.getPosition())
.icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_recycling))
.visible(recyclingVisible));
}
//Add our boolean array as an object to our markerMyLocation
mMaker.setTag(booleanValues);
//Add the markerMyLocation to the ArrayList
mMarkerArrayList.add(mMaker);
break;
case "coloredTeardrop":
//teardrop icon
//Decide what icon to use
if (booleanValues[0] && !booleanValues[1] && !booleanValues[2] && !booleanValues[3]) {
//Make a new MarkerOptions object to add the data
//garbage markers
mMaker = mMap.addMarker(new MarkerOptions()
.title(binLocation.getId())
.position(binLocation.getPosition())
.visible(garbageVisible));
} else {
//Make a new MarkerOptions object to add the data
//recycling markers
mMaker = mMap.addMarker(new MarkerOptions()
.title(binLocation.getId())
.position(binLocation.getPosition())
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN))
.visible(recyclingVisible));
}
//Add our boolean array as an object to our markerMyLocation
mMaker.setTag(booleanValues);
//Add the markerMyLocation to the ArrayList
mMarkerArrayList.add(mMaker);
break;
}//switch
}//for
//disable the progress bar
mProgressBar.setVisibility(View.GONE);
//De-activate the CountDown timer since the map is ready
mCountDownTimer.cancel();
//set the boolean to false
mMapIsEmpty = false;
Log.v(TAG, "populateMap finished. Markers: " + mMarkerArrayList.size());
}//populateMap
这里是 onMapReady 函数
public void onMapReady(GoogleMap map) {
Log.v(TAG, "onMapReady called");
mMap = map;
//Setup on map loaded
mMap.setOnMapLoadedCallback(this);
//Check to see if the map is empty and the location array list is not empty and then call populateMap
if (mMapIsEmpty && !mBinLocationArrayList.isEmpty()) {
populateMap();
}//if map empty
//set bounds
mMap.setLatLngBoundsForCameraTarget(GREATER_VANCOUVER_BOUND);
//Set min zoom level to match the bound
mMap.setMinZoomPreference(10.0f);
//disable map toolbar
UiSettings mUiSettings = mMap.getUiSettings();
mUiSettings.setMapToolbarEnabled(false);
//Set listeners
mMap.setOnMarkerClickListener(this);
mMap.setOnInfoWindowCloseListener(this);
mMap.setOnInfoWindowClickListener(this);
// Setting our custom info window, passing out helper method
mMap.setInfoWindowAdapter(new CustomInfoWindowAdapter());
//Here we check for permission and setup the map accordingly
if (!checkLocationPermission()) {
//Permission is not granted, log, and use the default location
Log.v(TAG, "No location permission");
//setup default map
defaultMapSetup();
} else {
Log.v(TAG, "Location permission granted");
//Enable my location and initialize the map there
mMap.setMyLocationEnabled(true);
//Setup the map
locationMapSetup();
}//if -permission
}//onMapReady
【问题讨论】:
-
这没有帮助,显示一些你的代码。
-
@GhulamMoinulQuadir 我在问题中添加了一些代码。
-
只需添加有限数量的标记并启动一个合理的计时器并重复直到全部添加。
-
@Andy 我不认为这是一个好的解决方案。它可能会在未来产生更多问题。标记的数量是随时间不断增加的动态数字。 (数据库不断更新)。
标签: java android google-maps