6

Short version: Disabling hardware acceleration with android:hardwareAccelerated="false" in xml changes the background color of my Theme.Sherlock.Light.DarkActionBar theme to a whiter "white". EDIT: this used to be the main question. I changed the title to emphasize the second problem.

Disabling hardware acceleration for the mapView only, causes constant redraws.

Long version:

AFAIK hardware acceleration is enabled by default on API level 14 and up. (reference)

Since I'm building and testing for API level 16, my hardware acceleration was usually on so that's what I was used to seeing. The theme is light but not quite pure white, it's a light grey (default).

I had some circle overlays being drawn on a map and when I zoomed in close, the mapview was becoming very laggy and I was getting a shape "too large to be rendered into a texture" error in logcat. I discovered that turning hardware acceleration off fixes the problem.

When I turn hardware acceleration off for the application (or an individual activity) in the android manifest, the background color of my layouts changes. It goes from a light grey to a very very light grey, almost pure white. Is this normal behavior?

I tried turning off hardware acceleration just for the mapview with:

if(android.os.Build.VERSION.SDK_INT>=11) {
mapView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); }

This works great to get rid of the texture too large errors because the mapview is not hardware accelerated AND it also keeps the rest of my app hardware accelerated. This would be the ideal solution. However, this causes another problem. It makes the onDraw method of the overlays where I use this code get called constantly. i.e, onDraw is called over and over and over by itself without calling invalidate() on the mapview. Any idea why this would be the case?

UPDATE:

Below is some simple code that will recreate the issue with the constant redraws when hardware acceleration is disabled only for the mapView (what I want):

MapActivity:

public class SettingsActivity extends MapActivity {

private MapView mapView;
private static List<Overlay> overlayList;
private static AccuracyCircleOverlay accuracyCircleOverlay;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_manage_maps);

    mapView = (MapView) findViewById(R.id.map);

    overlayList = mapView.getOverlays();
    accuracyCircleOverlay = new AccuracyCircleOverlay(this);
    overlayList.add(accuracyCircleOverlay);
}

@Override
protected boolean isRouteDisplayed() {
    return false;
}
}

Overlay:

public class AccuracyCircleOverlay extends Overlay {

private Context context;

public AccuracyCircleOverlay(Context context) {
    this.context = context;
}

@TargetApi(11)
@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
    super.draw(canvas, mapView, shadow);

    if (android.os.Build.VERSION.SDK_INT >= 11) {
        mapView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    }                 
            Log.d("accuracy OVERLAY", "being redrawn");
}
}
Community
  • 1
  • 1
Flyview
  • 1,899
  • 1
  • 28
  • 46
  • Experiencing this as well, thought I was the only one.. – miki Dec 03 '12 at 20:12
  • Looks like a bug in Android, you should submit it. – Henry Dec 10 '12 at 11:01
  • There is no relation between Hardware acceleration and your MapView overlay calling, can you post source codes for your MapView and its overlay ? – Anis BEN NSIR Dec 10 '12 at 14:45
  • 1
    Yes, Anis, that's what is expected, but not what is happening. Updated with simplified code to show the redraw problem when hardware acceleration is disabled on the mapView. – Flyview Dec 10 '12 at 19:13

3 Answers3

4

I finally realized what the problem was. Setting the layer type of the mapView to software should not be done from inside the draw method of the overlay! Doing that seems to trigger another draw, hence causing a loop. I put the following code in the onCreate of my Activity that holds the mapView and it worked. It tells the mapView to render in software mode without causing constant redraws!

In activity:

if (android.os.Build.VERSION.SDK_INT >= 11) {
    mapView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}  

Or alternatively in XML:

<com.google.android.maps.MapView
        ...
        android:layerType="software" />
Flyview
  • 1,899
  • 1
  • 28
  • 46
2

I appreciate that this answer doesn't address the focus of your question (which I understand to be questioning why the MapView and associated overlays constantly redraw when hardware acceleration is turned off - which, I admit, I really don't know why). However, I think you should leave the acceleration turned on and instead focus on restricting the size of what you're actually drawing in the overlay, as ndw suggests.

This is exactly what I have had to do in the application I'm working on right now. I am drawing a Path over the top of my MapView to illustrate a route from a log of GPS data. If I zoom in too far, the Path suddenly disappears, and I get a warning in LOGCAT saying "Shape path too large to be rendered into a texture", just as you have. My solution to fix it is to regenerate the Path on each zoom of the map (which, thinking about it, needs to be done anyway because on each zoom level, the actual pixel positions you get from Projection are obviously different). When the Path is generated by whatever data you're basing it on (e.g. set of GeoPoints) use the Projection class to determine if any path segments, etc. are way, way off the map and should be left off (which is what I am doing with my GPS route). If you have single vector objects which by themselves can get too large (such as a circle), then I suggest you design the code so that it will include the circle if any of its area falls within the map, but also include some logic to just draw a small portion of that circle's surface (e.g. by drawing a square) in the visible region if at a high zoom level.

Trevor
  • 10,903
  • 5
  • 61
  • 84
  • 1
    Yes, that is the exact problem I have. I am indeed using a projection so that the size of the [filled] circle represents a real distance/area. However, the circle(s) can dynamically change size. It may be tricky checking if all edges of the circle are out of view so that I can just fill the view with a separate overlay. How did you do it? – Flyview Dec 10 '12 at 23:37
1

I don't think the change is color is specific to your code, I've experienced the same thing.

Run the emulator without GPU emulation and holo_light theme and you get a white background, run with GPU emulation and you get the light gray background.

That said, I don't think disabling hardware acceleration is the right solution to your mapview problem. I would try to find out why the shape you're trying to render is too large and if there's anything you can do to make it smaller.

ndw
  • 513
  • 6
  • 14
  • Yes, I've realized I would like to keep HW acceleration on for the app, but would not mind turning it off for the mapview (which leads to the second problem, which should be the real question). The shape being too big is a common problem, especially with paths. In my case, when you zoom in, the circle becomes too large to render. If there was a way to only draw what is within view on the map, that would work. – Flyview Dec 10 '12 at 21:15
  • I too immediately thought that you should retain acceleration and instead look at how you're drawing the circles. When you say "if there was a way to only draw what is within view on the map..." - can't you easily do that simply using `Projection` (which I would have thought you're already using anyway) to determine what would be outside of the viewing area and hence don't add it to the `Path`? I've had a similar issue in my application where a `Path` I'm drawing would vanish at a high zoom level. I simply avoided drawing the parts of the `Path` that were way, way out of bounds to fix that. – Trevor Dec 10 '12 at 21:57
  • A circle is in your view if the max circle coordinate is greater than the min coordinate of your view, and the min circle coordinate is less than the max coordinate of your view. (for both X & Y) This alone may solve the problem if you're drawing a lot of textures off screen. – ndw Dec 13 '12 at 15:27