ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

android – 在通过ArrayList循环时在后台添加标记

2019-07-22 20:26:54  阅读:232  来源: 互联网

标签:ui-thread android arraylist google-maps-android-api-2


我从一个ArrayList of Objects中将标记添加到googleMap.大约有250个标记;我甚至必须将它们转换为位图来定制它们.这是一项资源密集型任务.但它严重阻止了我的UI线程.

我是这样做的:

    final HashMap<Marker, NearLocation> markerIdMap = new HashMap<Marker, NearLocation>();
    for (final NearLocation result : MainActivity.nearLocationList) {

            // Do all the hard work here

    }

在地图加载后,如何以某种方式动态地执行此操作,并在生成它们时填充它们?我不确定我是否可以通过在后台完成一些工作来做到这一点,然后当标记完成时将其移动到UI线程进行添加.

我知道如何使用AsyncTask单独执行此操作.虽然我在循环时不确定……

解决方法:

据我所知,添加标记不能在UI线程之外完成.

你可以做的是在后台执行所有准备工作(创建标记,转换为位图等).为了在添加标记时稍微省略UI线程,您可以放大并使用https://code.google.com/p/android-maps-extensions/仅显示可见标记或群集标记以降低数量,尽管250不是很多.

以下是关于该主题的SO答案:Add markers dynamically on Google Maps v2 for Android

我有一个应用程序,大约有4500个标记使用第一种方法运行得相当好(只要它没有快速缩小).应该注意的是,在这里我选择使用进度条制作闪屏,在用户有机会打开地图之前准备好所有标记.

如果您想要一种简单的机制来选择周围标记而不使用第三部分库,您可以执行以下操作:Android Maps v2 – animate camera to include most markers

刚才想到的一个想法,如果标记的创建真的那么昂贵,就是将EventBus https://github.com/greenrobot/EventBus添加到你的项目中.

使用EventBus,您可以在onEventAsync()方法中对标记进行长时间的准备.在该方法中,每当标记准备就绪时,将新标记发布到UI EventBus.getDefault().post(marker)并在onEventMainThread(Marker marker)中捕获它.在这里,您可以将标记保存在所有准备好的标记的列表中,如果地图当前处于打开状态,请添加它.

这是我用来在前面提到的应用程序中准备标记的一些代码.它用于在消防部门使用的应用程序中显示消防栓.首次启动时,所有消防栓都从一组CSV文件中读取,并且所有~4500消防栓的MarkerOptions都会被创建.毫无疑问,很多代码对你没用,只是留下它以防万一你可以从中受益:

private List<HydrantHolder> mHydrants;
private Map<HydrantType, List<MarkerOptions>> mMarkers;

private class ReadHydrantsFiles extends
        AsyncTask<Void, Integer, List<HydrantHolder>> {

    File[] hydrantsFiles = new File[0];

    // Before running code in separate thread
    @Override protected void onPreExecute() {

        loadStarted();

        String filepath = paths.PATH_LOCAL_HYDRANTS;

        File hydrantsPath = new File(filepath);
        hydrantsFiles = hydrantsPath.listFiles(new FilenameFilter() {

            @Override public boolean accept(File dir, String filename) {
                return filename.toLowerCase(Locale.ENGLISH).contains(
                        Constants.FILETYPE_CSV);
            }
        });

        int lineCount = 0;

        if (hydrantsFiles == null || hydrantsFiles.length == 0) {
            if (!preferences.isFirstStartUp()) {
                // TODO notify user
            }
            Log.e(TAG, "Missing hydrants");
            if (moduleCallback != null) {
                moduleCallback.doneLoadingModule(toString());
            }
            this.cancel(false);
        } else {

            for (File file : hydrantsFiles) {
                // store linecount for visual progress update
                lineCount += Files.lineCount(file);

            }

        }

    }

    // The code to be executed in a background thread.
    @Override protected List<HydrantHolder> doInBackground(Void... args) {
        List<HydrantHolder> all_hydrants = new ArrayList<HydrantHolder>();
        // Directory path here
        int totalLineProgress = 0;

        // // required
        int indexLatitude = modulePreferences.indexLatitude();
        int indexLongitude = modulePreferences.indexLongitude();
        // optional
        int indexAddress = modulePreferences.indexAddress();
        int indexAddressNumber = modulePreferences.indexAddressNumber();
        int indexAddressRemark = modulePreferences.indexAddressRemark();
        int indexRemark = modulePreferences.indexRemark();
        // decimals
        int latitude_decimal = modulePreferences.latitude_decimal();
        int longitude_decimal = modulePreferences.longitude_decimal();

        if (indexLatitude <= 0 || indexLongitude <= 0)
            return all_hydrants;

        for (File file : hydrantsFiles) {
            BufferedReader in = null;
            try {

                String hydrantspath = paths.PATH_LOCAL_HYDRANTS;
                File hydrantsPath = new File(hydrantspath);

                // Read File Line By Line
                in = new BufferedReader(new InputStreamReader(
                        new FileInputStream(file), "windows-1252"));
                String strLine;
                while ((strLine = in.readLine()) != null) {

                    totalLineProgress++;

                    String[] hydrantParts = strLine.split(";", -1);

                    String errors = "";
                    final String hydrantType = file.getName().replace(
                            Constants.FILETYPE_CSV, "");

                    File[] iconFiles = hydrantsPath
                            .listFiles(new FilenameFilter() {

                                @Override public boolean accept(File dir,
                                        String filename) {
                                    if (filename.contains(hydrantType)
                                            && filename
                                                    .contains(Constants.FILETYPE_PNG)) {
                                        return true;
                                    }
                                    return false;
                                }
                            });

                    HydrantHolder.Builder hb = new HydrantHolder.Builder();
                    if (hydrantParts.length >= 5) {

                        hb.setHydrantType(hydrantType);
                        if (iconFiles.length != 0) {
                            hb.setIconPath(hydrantspath
                                    + File.separatorChar
                                    + iconFiles[0].getName());
                        }
                        hb.setLatitude(hydrantParts[indexLatitude],
                                latitude_decimal);
                        hb.setLongitude(hydrantParts[indexLongitude],
                                longitude_decimal);

                        if (indexAddress > 0)
                            hb.setAddress(hydrantParts[indexAddress]);
                        if (indexAddressNumber > 0)
                            hb.setAddressNumber(hydrantParts[indexAddressNumber]);
                        if (indexAddressRemark > 0)
                            hb.setAddressRemark(hydrantParts[indexAddressRemark]);
                        if (indexRemark > 0)
                            hb.setRemark(hydrantParts[indexRemark]);

                        if (hb.getErrors().isEmpty()) {
                            HydrantHolder hydrant = hb.build();
                            all_hydrants.add(hydrant);
                        } else {
                            // TODO write error file to Dropbox if possible,
                            // otherwise locally
                            Log.d(TAG, errors);
                        }
                    } else {
                        errors = "Missing information";
                    }

                    publishProgress(totalLineProgress);
                }
            } catch (InvalidPathException e) {
                Log.e(TAG, e.getMessage(), e);
            } catch (IOException e) {
                Log.e(TAG, e.getMessage(), e);
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                    }
                }
            }
        }
        Log.d(TAG, "" + all_hydrants.size());
        return all_hydrants;
    }

    // Update the progress
    @Override protected void onProgressUpdate(Integer... values) {
        // set the current progress of the progress dialog
        // if (progressDialog != null && values != null && values.length >
        // 0) {
        // progressDialog.setProgress(values[0]);
        // }
    }

    @Override protected void onPostExecute(List<HydrantHolder> hydrants) {
        // Device.releaseOrientation((Activity) context);

        Log.d(TAG, "Saved " + hydrants.size() + " hydrants");
        mHydrants = hydrants;

        new PrepareMarkerOptionsTask(hydrants).execute();

        super.onPostExecute(hydrants);
    }

}

private class PrepareMarkerOptionsTask extends
        AsyncTask<Void, Integer, Map<HydrantType, List<MarkerOptions>>> {
    // Before running code in separate thread
    List<HydrantHolder> mHydrants;

    public PrepareMarkerOptionsTask(List<HydrantHolder> hydrants) {
        this.mHydrants = hydrants;
        mMarkers = new HashMap<HydrantsModule.HydrantType, List<MarkerOptions>>();
    }

    @Override protected void onPreExecute() {

    }

    // The code to be executed in a background thread.
    @Override protected Map<HydrantType, List<MarkerOptions>> doInBackground(
            Void... arg) {

        for (HydrantHolder hydrant : mHydrants) {

            final String hydrant_type = hydrant.getHydrantType();
            final String hydrant_icon_path = hydrant.getIconPath();
            double latitude = hydrant.getLatitude();
            double longitude = hydrant.getLongitude();

            final LatLng position = new LatLng(latitude, longitude);

            final String address = hydrant.getAddress();
            final String addressNumber = hydrant.getAddressNumber();
            final String addressremark = hydrant.getAddressRemark();
            final String remark = hydrant.getRemark();

            // Log.d(TAG, hydrant.toString());

            BitmapDescriptor icon = BitmapDescriptorFactory
                    .defaultMarker(BitmapDescriptorFactory.HUE_RED);

            if (!hydrant_icon_path.isEmpty()) {
                File iconfile = new File(hydrant_icon_path);
                if (iconfile.exists()) {
                    BitmapDescriptor loaded_icon = BitmapDescriptorFactory
                            .fromPath(hydrant_icon_path);
                    if (loaded_icon != null) {
                        icon = loaded_icon;
                    } else {
                        Log.e(TAG, "loaded_icon was null");
                    }
                } else {
                    Log.e(TAG, "iconfile did not exist: "
                            + hydrant_icon_path);
                }
            } else {
                Log.e(TAG, "iconpath was empty on hydrant type: "
                        + hydrant_type);
            }

            StringBuffer snippet = new StringBuffer();
            if (!address.isEmpty())
                snippet.append("\n" + address + " " + addressNumber);
            if (addressremark.isEmpty())
                snippet.append("\n" + addressremark);
            if (!remark.isEmpty())
                snippet.append("\n" + remark);

            addHydrantMarker(
                    hydrant_type,
                    new MarkerOptions().position(position)
                            .title(hydrant_type)
                            .snippet(snippet.toString()).icon(icon));

            // publishProgress(markers.size());
        }
        return mMarkers;
    }

    // Update the progress
    @Override protected void onProgressUpdate(Integer... values) {
    }

    @Override protected void onCancelled() {
    }

    // after executing the code in the thread
    @Override protected void onPostExecute(
            Map<HydrantType, List<MarkerOptions>> markers) {

        Log.d(TAG, "Prepared " + markers.size() + " hydrantMarkerOptions");
        mMarkers = markers;

        loadEnded();

        super.onPostExecute(markers);

    }

}

public Set<HydrantType> getHydrantTypes() {
    return new HashSet<HydrantType>(mMarkers.keySet());
}

private void addHydrantMarker(String typeName, MarkerOptions marker) {
    HydrantType type = new HydrantType(typeName, marker.getIcon());
    if (mMarkers.containsKey(type)) {
        List<MarkerOptions> markers = mMarkers.get(type);
        markers.add(marker);
    } else {
        List<MarkerOptions> markers = new ArrayList<MarkerOptions>();
        markers.add(marker);
        mMarkers.put(type, markers);
        enableHydrantType(type);
    }
}

public class HydrantType {

    private final String name;
    private final BitmapDescriptor icon;

    public HydrantType(String name, BitmapDescriptor icon) {
        this.name = name;
        this.icon = icon;
    }

    public String getName() {
        return name;
    }

    public BitmapDescriptor getIcon() {
        return icon;
    }

    @Override public int hashCode() {
        return name.hashCode();
    }

    @Override public boolean equals(Object o) {
        if (o instanceof HydrantType) {
            if (((HydrantType) o).name.equals(name)) {
                return true;
            }
        }
        return false;
    }

} 

根据评论我添加了更多的文字和代码.

是的我立刻添加了所有MarkerOptions.但是,因为我在添加所有标记之前放大了GoogleMaps-extensions(第一个链接),只花费CPU力量将可见的标记添加到地图中.如果用户平移地图或缩小,则会自动添加更多可见标记.

要使地图延迟加载标记:

@Override
public void onMapReady(GoogleMap map) {
    Log.i(TAG, "onMapReady");
    if (map != null) {
        map.setClustering(new ClusteringSettings().enabled(false)
                .addMarkersDynamically(true));
    }
}

我用来添加消防栓的代码(现在这在很多情况下都过于复杂,但如果仔细阅读,只需缩放到一个地址并在缩放完成后添加所有消防栓):

public void addHydrantsNearAddress(final AddressHolder addressHolder,
        final boolean zoomToAddress) {

    final GoogleMap map = googlemaps.getMap();


    final OnCameraChangeListener addHydrantsAfterZoom = new OnCameraChangeListener() {

        @Override public void onCameraChange(CameraPosition cameraPosition) {
            Log.d(TAG, cameraPosition.target.toString());
            Log.d(TAG, addressHolder.position.toString());

            final GoogleMap map = googlemaps.getMap();
            // if (Location.distanceBetween(cameraPosition.tar,
            // startLongitude, endLatitude, endLongitude, results)) {
            new Handler().postDelayed(new Runnable() {

                @Override public void run() {
                    addAllHydrants();
                }
            }, 500);

            map.setOnCameraChangeListener(null); // unregister
            // }
        }
    };

    if (map == null) {
        // wait for the map to be ready before adding hydrants
        googlemaps.setGoogleMapsCallback(new GoogleMapsCallback() {

            @Override public void mapReady(GoogleMap map) {

                if (zoomToAddress) {
                    map.setOnCameraChangeListener(addHydrantsAfterZoom);
                    googlemaps.zoomTo(addressHolder);
                } else {
                    addAllHydrants();
                }

                googlemaps.setGoogleMapsCallback(null); // unregister
            }
        });
    } else {

        if (zoomToAddress) {
            // only setOnCameraChangeListener if cammera needs to move
            LatLng cammeraPos = map.getCameraPosition().target;
            LatLng addressPos = addressHolder.position;

            float[] results = new float[1];
            Location.distanceBetween(cammeraPos.latitude,
                    cammeraPos.longitude, addressPos.latitude,
                    addressPos.longitude, results);

            // Log.d(TAG, "distance " + results[0]);

            if (results[0] > 1) {
                map.setOnCameraChangeListener(addHydrantsAfterZoom);
                googlemaps.zoomTo(addressHolder);
            } else {
                googlemaps.zoomTo(addressHolder);
                addAllHydrants();
            }

        }

    }

}

另外一个原因是,为了举例,我需要让用户在消防栓类型之间进行过滤.希望你能看到这个想法.

addAllHydrants()就像它听起来一样简单,迭代MarkerOptions并添加它们:

public void addAllHydrants() {

    enableAllHydrants();

    GoogleMap map = googlemaps.getMap();

    map.setTrafficEnabled(modulePreferences.showTraffic());
    map.setMapType(modulePreferences.getMapType());

    addHydrants(map);
}

private void addHydrants(GoogleMap map) {

    Log.d(TAG, "addHydrants");

    if (mHydrants == null || mHydrants.isEmpty()) {
        Toast.makeText(context,
                context.getString(R.string.missing_hydrants),
                Toast.LENGTH_LONG).show();
        return;
    } else {
        for (MarkerOptions marker : getEnabledHydrantMarkers()) {
            map.addMarker(marker);
        }
    }

}

我真的认为你自己会因为自己尝试计算,获取和添加可见标记而过于复杂化,而不仅仅是.

>准备MarkerOptions(第一代代码中的示例)
>使用onMapReady回调等待地图准备就绪(此示例来自Google的官方示例)
>在新创建的地图上设置addMarkersDynamically(true)
>放大感兴趣的地址或区域(例如,设备位置)
>添加所有MarkerOptions
>当您在地图上移动时,请参阅自动添加标记

回答有关onMapReady回调的问题

如果您使用XML添加地图,那么我怀疑您可能不需要添加回调.尝试直接在Activity的onCreate()中使用地图.只要对getExtendedMap()的调用不为null,你应该没问题.

我在代码中创建片段,以便能够将控制器代码与地图放在一起.因此我的SupportMapFragment看起来像这样:

public class GoogleMapFragment extends SupportMapFragment {

    private OnGoogleMapFragmentListener mCallback;

    public GoogleMapFragment() {
        mCallback = null;
    }

    public static interface OnGoogleMapFragmentListener {
        void onMapReady(GoogleMap map);
    }

    @Override public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mCallback = (OnGoogleMapFragmentListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(getActivity().getClass().getName()
                    + " must implement OnGoogleMapFragmentListener");
        }

    }

    @Override public View onCreateView(LayoutInflater inflater,
            ViewGroup container, Bundle savedInstanceState) {
        View view = super.onCreateView(inflater, container, savedInstanceState);
        if (mCallback != null) {
            mCallback.onMapReady(getExtendedMap());
        }

        return view;
    }

}

标签:ui-thread,android,arraylist,google-maps-android-api-2
来源: https://codeday.me/bug/20190722/1506245.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有