Moving MapFragment (SurfaceView) causes black back

2019-01-12 20:17发布

I'm trying to implement new Android Google Maps API (v2). However it doesn't seem to go well with SlidingMenu. As you may know, MapFragment implementation is based on SurfaceView. The problem is that the SurfaceView doesn't like moving it around - I mean placing it in movable views:

When you move it, it leaves a black hole in the place where its pixels originally layed. It looks something like this.

This problem can be partially solved by specifying a transparent background on the SurfaceView or even placing a transparent View over it. I just can't figure if only for me the results are unacceptable, or if I have a different problem which makes it look how it looks.

To see how it looks, please watch THIS VIDEO.

(sorry for the quality, but the problem can be seen easily anyway)

When a normal listview (the orange screen) is pulled away while opening SlidingMenu, the animation is smooth and nice. But as soon as the MapFragment appears, there's some weird refreshing/flickering/synchronization issue on the map.

Tested on HTC One S and Samsung Galaxy ACE.


My super-duper-crazy idea of solving it: each time the opening animation starts, take a screenshot of the MapFragment (it should be possible with SurfaceView) and lay it over for the duration of the animation. But I really don't know how to do it... Taking screenshot of a map isn't possible, but maybe someone will get inspired by this.

Or maybe disable redrawing the map some other way, I don't know.


UPDATE:

Found this.

It appears that SurfaceView can be changed to TextureView to remove those limitations. But since MapFragment is based on SurfaceView, I don't know if it can be achieved.

Another update It appears that this issue has been resolved on devices 4.1+. They just used TextureView. but on lower versions we still have to use workarounds.

17条回答
欢心
2楼-- · 2019-01-12 20:54

add android:hardwareAccelerated="true" in your manifest file.

eg: <application android:name=".Sample" android:hardwareAccelerated="true" />

查看更多
对你真心纯属浪费
3楼-- · 2019-01-12 20:56

Of course the proper solution will be for Google to fix the problem (see Android Maps V2 issue 4639: http://code.google.com/p/gmaps-api-issues/issues/detail?id=4639).

However, one of my coworkers suggested simply extending the map beyond its container. If we extend the map fragment beyond the visible region of its container, like so:

android:layout_marginLeft="-40dp"
android:layout_marginRight="-40dp"

we can reduce/eliminate the flickering. Newer devices (e.g. Galaxy Nexus) show no flickering after this hack, and older devices (e.g. LG Optimus V) show reduced flickering. We have to extend margins on both sides so that info windows are centered when they're selected.


Update: This issue was fixed by Google on Aug. 28, but I'm guessing it still has to be rolled into a release.

查看更多
霸刀☆藐视天下
4楼-- · 2019-01-12 20:57

Here's a crazy idea, don't move the MapView ;-)

That is, create a MapView full screen width behind your app in a FrameLayout. Then lay your views on top, and punch a hole through to the MapView in the position you need to display it. When moving you'll need to update the map position to stay in sync so the user keeps seeing the same section of the map, but that should update better than moving a surface view around.

查看更多
我想做一个坏孩纸
5楼-- · 2019-01-12 20:59

To prevent the MapView(it is actually a SurfaceView) creating the black holes, I add a empty view with transparent background color to cover the whole MapView, such that it can prevent MapView from generating a black "hole". It may work for your situation.

查看更多
甜甜的少女心
6楼-- · 2019-01-12 20:59

Here's a very simple workaround I used to get rid of the flashing in a ViewPager. I would imagine the same will hold true for any Fragment or other dynamically added MapView.

The problem doesn't really seem to be the MapView itself, but the resources needed to run the MapView. These resources are only loaded the first time you fire up a MapView in your activity and re-used for each subsequent MapView, as you would expect. This loading of resources is what causes the screen to flash.

So to remove the flashing, I just included another MapView in the layout of my Activity (location in the activity is irrelevant). As soon as the Activity has loaded, I just set the Visibility of the extra MapView to GONE. This means all the resources needed for your MapView are ready for when you fire up any of your Fragments using MapViews with no lag, no flashing, all happiness.

This is of course a workaround and not a real 'solution' to the underlying problem but it will resolve the side effects.

So to cover off the code used for completeness:

Randomly (but cleanly) placed in my Activity Layout:

<com.google.android.gms.maps.MapView
        android:id="@+id/flash_map"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

Then in my Activity Class

private MapView flashMap;

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

            // Code omitted

    try {
        MapsInitializer.initialize(this);
    } catch (GooglePlayServicesNotAvailableException e) {
        e.printStackTrace();
    }
    flashMap = (MapView)findViewById(R.id.flash_map);
    flashMap.onCreate(savedInstanceState);
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
        flashMap.onSaveInstanceState(outState);
}

@Override
public void onResume() {
    super.onResume();
    flashMap.onResume();
    flashMap.setVisibility(View.GONE); // Just like it was never there...
}

@Override
public void onPause() {
    flashMap.onPause();
    super.onPause();
}

@Override
public void onDestroy() {
    flashMap.onDestroy();
    super.onDestroy();
}

@Override
public void onLowMemory() {
    super.onLowMemory();
    flashMap.onLowMemory();
}
查看更多
乱世女痞
7楼-- · 2019-01-12 21:00

Guys the best solution I found was to instantiate your support map through this. Does wonders

SupportMapFragment mapFragment = SupportMapFragment.newInstance(new GoogleMapOptions().zOrderOnTop(true));
查看更多
登录 后发表回答