Google Maps v2 MapFragment从后退区返回时非常滞后 [英] Google Maps v2 MapFragment is extremely laggy on returning from the backstack

查看:152
本文介绍了Google Maps v2 MapFragment从后退区返回时非常滞后的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个使用v2 MapFragment的应用程序,并且遇到了非常奇怪的行为.我创建了MapFragment的子类来处理一些自定义行为(处理Marker s,调整菜单选项等),并且在首次加载时,它们都能很好地工作.然后,我将一个新的片段嵌入到我的活动中,将自定义MapFragment推入后台.但是,当我从堆栈返回地图时,事情变得很奇怪.平移地图变得极端缓慢(我们正在谈论〜1 FPS),这既适用于手动拖动/缩放,也适用于单击图钉导致的动画.然后 then ,如果我与溢出菜单的任何部分进行交互,即使只是将其打开并再次将其关闭,则滞后立即消失.似乎没有其他解决方法(关闭/重新打开该应用程序除外);与非溢出菜单项和导航抽屉进行交互无济于事.我从来没有见过这样的东西,也找不到以前描述过类似问题的人.任何想法,建议和/或修复都将受到欢迎.

I'm working on an app that makes use of a v2 MapFragment, and I'm running into very strange behavior. I've created a subclass of MapFragment to handle some custom behavior (handling Markers, tweaking the menu options, etc.), and on first load it all works beautifully. I then embed a new fragment into my activity, pushing the custom MapFragment onto the backstack. When I return the map from the backstack, though, things get weird; panning the map becomes extremely laggy (we're talking ~1 FPS), both for manual dragging/zooming and for animations caused by clicking on pins. And then, if I interact with any part of the overflow menu, even just opening it and dismissing it again, the lag immediately clears up. Nothing else seems to fix it (short of closing/reopening the app); interacting with the non-overflow menu items and the navigation drawer does nothing to help. I've never seen anything like this, nor can I find anyone who's described a similar problem before. Any ideas, suggestions, and/or fixes would be welcome.

要在回答之前回答一些问题:

To answer a few questions before they get asked:

  • 是的,我正在调用所有我覆盖的生命周期方法的super版本(onCreate()onCreateView() [我也将返回该超级对象返回的内容]和onDestroyView()).
  • 据我所知,我正在正确清理地图.每次刷新图钉时,我都会在每个图钉上调用remove(),然后在地图本身上调用clean(),我也将在onDestroyView()中执行所有这些操作.
  • Yes, I'm calling the super versions of all the lifecycle methods I override (onCreate(), onCreateView() [I'm also returning what the super returns for that one], and onDestroyView()).
  • As far as I can tell, I'm cleaning up the map properly. Every time I refresh the pins, I'm calling remove() on each of them and then clean() on the map itself, and I do all that in onDestroyView() as well.

最后,作为参考,这是添加新片段的代码:

And lastly, for reference, this is the code that adds the new fragment:

getFragmentManager().beginTransaction().replace(R.id.main_content_container, new JoinGroupFragment()).addToBackStack(null).commit();

当我完成后,我就打电话给:

And when I'm done with it, I just call:

getFragmentManager().popBackStack();

编辑:我不确定会提供多少帮助,但这是自定义的MapFragment:

I'm not sure how much help it'll be, but here's the custom MapFragment:

public class CustomMapFragment extends MapFragment {

    private static final String DIALOG_TAG = "CUSTOM_MAP_FRAGMENT_DIALOG";
    private static final int DEFAULT_ZOOM = 14;
    private static final int MARKER_ZOOM = 15;
    private static final int DEFAULT_PADDING = 80;
    private static final int ORANGE_THRESHOLD_MINUTES = 7;
    private static final int BLUE_THRESHOLD_MINUTES = 20;

    public static final String KEY_GROUP_NAME = "GROUP_NAME";
    public static final String KEY_GROUP_ID = "GROUP_ID";

    private TextView mGroupNameOverlay;
    private GoogleMap mMap;
    private ArrayList<Marker> mMarkers;
    private Marker mSelectedMarker;
    private ArrayList<Group> mAllGroups;
    private Group mCurrentGroup;
    private ArrayList<Location> mAllLocations;
    private boolean mMapReady;
    private String mUsername;
    private boolean mCenterOnUser;

    public CustomMapFragment() {

    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);

        mMarkers = new ArrayList<>();
        mAllLocations = new ArrayList<>();

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
        mUsername = prefs.getString(PreferenceUtils.KEY_USERNAME, null);
        mCenterOnUser = prefs.getBoolean(PreferenceUtils.KEY_CENTER_ON_ME, false);
        mSelectedMarker = null;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        ViewGroup view = (ViewGroup)super.onCreateView(inflater, container, savedInstanceState);

        if (view != null) {
            // View should never be null; MapFragments have a FrameLayout as their top level parent
            mGroupNameOverlay = (TextView)inflater.inflate(R.layout.group_name_overlay, view, false);
            view.addView(mGroupNameOverlay);
        }

        Bundle results = ((MainActivity)getActivity()).getFragmentResults();
        if (results != null) {
            String name = results.getString(KEY_GROUP_NAME);
            String id = results.getString(KEY_GROUP_ID);
            if (!StringUtils.isNullOrEmpty(name) && !StringUtils.isNullOrEmpty(id)) {
                mCurrentGroup = new Group(name, id);
                mAllGroups.add(mCurrentGroup);
            }
        }

        if (mCurrentGroup != null) {
            updateGroupNameOverlay(mCurrentGroup.getGroupName());
        }

        getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(GoogleMap googleMap) {
                mMap = googleMap;
                mMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
                    @Override
                    public boolean onMarkerClick(Marker marker) {
                        mSelectedMarker = marker;
                        getActivity().invalidateOptionsMenu();
                        return false;
                    }
                });
                mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
                    @Override
                    public void onMapClick(LatLng latLng) {
                        mSelectedMarker = null;
                        getActivity().invalidateOptionsMenu();
                    }
                });
                populateMap(true, false);
            }
        });

        GetGroupsRequest request = new GetGroupsRequest();
        request.setListener(new GetGroupsRequestListener());
        RequestProcessor.getInstance(getActivity()).queueRequest(request);

        return view;
    }

    @Override
    public void onDestroyView() {
        mSelectedMarker = null;
        for (Marker marker : mMarkers) {
            marker.remove();
        }
        mMarkers.clear();
        mMap.clear();
        mMap = null;
        mMapReady = false;
        super.onDestroyView();
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        if (mSelectedMarker == null) {
            inflater.inflate(R.menu.menu_map, menu);
        }
        else {
            inflater.inflate(R.menu.menu_marker, menu);
        }
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.map_menu_refresh_pins:
                performLocationsRequest(false);
                return true;
            case R.id.map_menu_recenter_zoom:
                populateMap(true, true);
                return true;
            case R.id.map_menu_select_group:
                DialogFragment selectDialog = new DialogFragment() {

                    @Override
                    public Dialog onCreateDialog(Bundle savedInstanceState) {
                        String[] groups = new String[mAllGroups.size()];
                        for (int i = 0; i < groups.length; i++) {
                            groups[i] = mAllGroups.get(i).getGroupName();
                        }
                        return new AlertDialog.Builder(getActivity())
                                .setItems(groups, new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {
                                        if (!mAllGroups.get(which).equals(mCurrentGroup)) {
                                            mCurrentGroup = mAllGroups.get(which);
                                            updateGroupNameOverlay(mCurrentGroup.getGroupName());
                                            performLocationsRequest(true);
                                        }
                                    }
                                })
                                .create();
                    }
                };
                selectDialog.show(getFragmentManager(), DIALOG_TAG);
                return true;
            case R.id.map_menu_join_group:
                getFragmentManager().beginTransaction().replace(R.id.main_content_container, new JoinGroupFragment()).addToBackStack(null).commit();
                return true;
            case R.id.map_menu_create_group:
                CreateDialogFragment createDialog = new CreateDialogFragment();
                createDialog.show(getFragmentManager(), DIALOG_TAG);
                return true;
            case R.id.map_marker_zoom:
                if (mSelectedMarker != null) {
                    mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(mSelectedMarker.getPosition(), MARKER_ZOOM));
                }
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    private void performLocationsRequest(boolean autoZoom) {
        GetLocationsRequest request = new GetLocationsRequest(mCurrentGroup.getGroupId());
        request.setListener(new GetLocationsRequestListener(autoZoom));
        RequestProcessor.getInstance(getActivity()).queueRequest(request);
    }

    private void updateGroupNameOverlay(final String groupName) {
        if (mGroupNameOverlay != null) {
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (groupName == null) {
                        mGroupNameOverlay.setText(R.string.map_group_overlay_no_group);
                    }
                    else {
                        mGroupNameOverlay.setText(getString(R.string.map_group_overlay_group, groupName));
                    }
                }
            });
        }
    }

    private void populateMap(boolean zoom, boolean animate) {
        if (!mMapReady) {
            mMapReady = true;
        }
        else {
            CameraUpdate update = null;
            mSelectedMarker = null;
            for (Marker marker : mMarkers) {
                marker.remove();
            }
            mMarkers.clear();
            mMap.clear();
            if (mAllLocations.size() == 1) {
                Location location = mAllLocations.get(0);
                mMarkers.add(addMarker(location));
                update = CameraUpdateFactory.newLatLngZoom(new LatLng(location.getLatitude(), location.getLongitude()), DEFAULT_ZOOM);
            }
            else if (mAllLocations.size() > 1) {
                LatLngBounds.Builder builder = new LatLngBounds.Builder();
                for (Location location : mAllLocations) {
                    mMarkers.add(addMarker(location));
                    if (mCenterOnUser) {
                        if (location.getUsername().equals(mUsername)) {
                            update = CameraUpdateFactory.newLatLngZoom(new LatLng(location.getLatitude(), location.getLongitude()), DEFAULT_ZOOM);
                        }
                    }
                    else {
                        builder.include(new LatLng(location.getLatitude(), location.getLongitude()));
                    }
                }
                if (!mCenterOnUser) {
                    update = CameraUpdateFactory.newLatLngBounds(builder.build(), DEFAULT_PADDING);
                }
            }

            if (update != null && zoom) {
                if (animate) {
                    mMap.animateCamera(update);
                }
                else {
                    mMap.moveCamera(update);
                }
            }
        }
    }

    private Marker addMarker(Location location) {
        String timestamp;
        long minutesOld = (new Date().getTime() - location.getLastReported()) / 60000;
        float hue = BitmapDescriptorFactory.HUE_RED;
        if (minutesOld < 1) {
            timestamp = getString(R.string.map_timestamp_just_now);
        }
        else if (minutesOld < 2) {
            timestamp = getString(R.string.map_timestamp_one_minute);
        }
        else {
            timestamp = getString(R.string.map_timestamp_n_minutes, minutesOld);
            if (minutesOld >= ORANGE_THRESHOLD_MINUTES) {
                hue = BitmapDescriptorFactory.HUE_ORANGE;
            }
            if (minutesOld >= BLUE_THRESHOLD_MINUTES) {
                hue = BitmapDescriptorFactory.HUE_BLUE;
            }
        }
        LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
        return mMap.addMarker(new MarkerOptions()
                .position(latLng)
                .icon(BitmapDescriptorFactory.defaultMarker(hue))
                .title(location.getUsername())
                .snippet(timestamp));
    }

    private class GetGroupsRequestListener extends RequestListener<GetGroupsResponse> {

        public GetGroupsRequestListener() {
            super(getActivity());
        }

        @Override
        protected void onRequestComplete(GetGroupsResponse response) {
            mAllGroups = response.getGroups();
            if (mAllGroups.size() > 0) {
                if (mCurrentGroup == null) {
                    mCurrentGroup = mAllGroups.get(0);
                    updateGroupNameOverlay(mCurrentGroup.getGroupName());
                }
                performLocationsRequest(true);
            }
        }
    }

    private class GetLocationsRequestListener extends RequestListener<GetLocationsResponse> {

        private boolean mmAutoZoom;

        public GetLocationsRequestListener(boolean autoZoom) {
            super(getActivity());
            mmAutoZoom = autoZoom;
        }

        @Override
        protected void onRequestComplete(GetLocationsResponse response) {
            mAllLocations = response.getLocations();
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    populateMap(mmAutoZoom, false);
                }
            });
        }
    }
}

P.S.我意识到以这种方式创建劫持视图并以这种方式注入自己的叠加层可能不是最佳实践,但是出于其价值,我尝试将这一部分注释掉,但并不能解决问题,因此我对此表示怀疑是相关的.

P.S. I realize it probably isn't the best practice to sort of hijack view creation and inject my own overlay that way, but for what it's worth, I tried commenting that portion out and it didn't solve the problem, so I'm doubtful that it's related.

推荐答案

我遇到了类似的问题.假设我有带有子SupportMapFragment的FragmentA.通过导航抽屉菜单,我调用一个要显示的对话框.此时,FragmentA中的子贴图会进入某种后台模式"-大约每秒更新两次(我在此贴图上有动画标记,所以这种行为对我来说是众所周知的).通过显示任何带有叠加的元素(例如f)来打开背景"模式.对话框或AppBar菜单,并且在隐藏该元素时关闭.

I've experienced similar problem. Let's say I have FragmentA with child SupportMapFragment. Through navigation drawer menu I invoke a dialog to be shown. At this point child map in FragmentA goes to some "background mode" - it updates approximately twice a second (I have animated markers on this map, so this behavior is well known to me). This "background" mode is turned on by showing any element with overlay, f.e. dialog or AppBar menu, and is off when that element is hidden.

在对话框中,我有包含项目的列表,这些项目在单击时会启动片段替换事务.此时,FragmentA被FragmentB取代.这是OnClickListener的代码段:

In dialog i have list with items, which are starting fragment replace transaction, when clicked. At this point FragmentA is being replaced by FragmentB. Here's code snippet of OnClickListener:

final SmartFragment fragment = new SmartFragment();
FragmentManager supportFragmentManager = getActivity().getSupportFragmentManager();
supportFragmentManager
        .beginTransaction()
        .replace(R.id.fragment_container, fragment, SmartFragment.FRAGMENT_TAG)
        .addToBackStack(null)
        .commit();
dismiss();

但是,当我从堆栈返回FragmentA之后,保留的map仍然处于背景模式",即使这样该对话框也不再存在.在事务处理前移动dissmiss()语句无济于事,它的提示是,直到所有代码执行完毕,对话框才被关闭.因此,唯一合理的解决方案(除了直接在地图片段上打开背景模式"外,我找不到该方法)是将片段事务延迟到真正消除对话框之前.正如我之前假设的那样,它仅在执行OnClick(属于对话框的一部分)中的所有代码时才会发生,因此我们需要使事务不在声明它的实例上完成,而是稍后将其放置在Runnable中:

But after I return to FragmentA from backstack, map, which was retained, stays in that "background mode", even so the dialog doesn't exist anymore. Moving dissmiss() statement before transaction doesn't help, it seams like dialog isn't dismissed till all the code is executed. So the only reasonable solution (except turning of "background mode" directly on map fragment, which I can't find how to do) is to delay fragment transaction until dialog is really dismissed. As I assumed earlier, it happens only when all code in OnClick (which is a part of dialog) is executed, so we need to make transaction not being done at an instance it was declared, but a bit later, placing it in Runnable:

dismiss();
final SmartFragment fragment = new SmartFragment();
Handler handler = new Handler();
final FragmentManager supportFragmentManager = getActivity().getSupportFragmentManager();
handler.post(new Runnable() {
    @Override
    public void run() {
        supportFragmentManager
                .beginTransaction()
                .replace(R.id.fragment_container, fragment, StopsSmartFragment.FRAGMENT_TAG)
                .addToBackStack(null)
                .commit();
    }
});

为此,我们需要使片段和supportFragmentManager最终化.现在一切正常,地图的背景模式"已关闭,并且如果您从后退堆栈返回FragmentA,则地图会正常工作.

For this we need to make our fragment and supportFragmentManager final. Now everything works, map's "background mode" is turned off, and if you return to FragmentA from backstack, map works as it should.

如果有人知道如何直接在地图片段中关闭背景模式",请告诉我,我很高兴听到.

If anyone has an idea how to turn off "background mode" in map fragment directly, please tell, I'll be happy to hear.

这篇关于Google Maps v2 MapFragment从后退区返回时非常滞后的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆