Falling back on old SDK methods without reflection

As Android progresses as a platform, there will inevitably be methods added to the SDK. You’ll want to add some of these great new features while still supporting the old SDKs.

We can conditionally use these methods by getting the device’s SDK version and using the appropriate method, but this won’t actually work because although it may compile, the older device will throw an exception when it loads the class that uses that unknown method.

One way to fix this is via reflection, which works great. Reflection, however, can be resource intensive, and hard to maintain. You’ll have to declare the method as a string and pass in a method signature… gross.

Another way to do this builds on the first approach. Since a method isn’t loaded until the class it’s in is loaded, we can hide the method in another class. Here’s how it works:

public class MyActivity extends ListActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        // Check to see if this version of Android supports
        // ListView smooth scrolling
        if(Integer.parseInt(Build.VERSION.SDK) >= 8) {
            // Smooth scroll to position
            SmoothScrollMethodHolder.smoothScrollToPosition(getListView(), 5);
        }
        else {
            // Set postion
            getListView().setSelection(5);
        }
    }
   
    private class SmoothScrollMethodHolder {
        // Put method in a static method of a private class. This class
        // won't be loaded until you call this statically method.
        public static void smoothScrollToPosition(ListView listView, int position) {
            listView.smoothScrollToPosition(position);
        }
    }
}

This code is easy to read, easy to maintain, and comes with all the benefits that reflection strips away, like code completion. Of course, you don’t want to litter your code with the above, so you’ll probably want to put it into a utility class.

public static class CompatibilityUtils {
    private static final int SDK_VERSION = Integer.parseInt(Build.VERSION.SDK);

    public static void smoothScrollToPosition(ListView listView, int position) {
        if(SDK_VERSION >= 8) {
            API9.smoothScrollToPosition(listView, position);
        }
        else {
            listView.setPosition(position);
        }
    }
   
    private static class API6 {
        ...
    }

    private static class API8 {
        public static void smoothScrollToPosition(ListView listView, int position) {
            listView.smoothScrollToPosition(position);
        }
    }
   
    private static class API9 {
        ...
    }
   
    private static class API10 {
        ...
    }
   
    private static class API14 {
        ...
    }
}

You can add all your compatibility methods to this class. The original code is now just:

CompatibilityUtils.smoothScrollToPosition(getListView(), 5);
Posted in Android | Leave a comment

Inset TextView shadows

If you’re used to using Apple products, you’re probably familiar with the heavy use of inset shadows. It adds a bit of depth to the UI and can really make the screen look beautiful. Notice the white drop shadow in the title of the window.

Finder

I tend to do the same thing a lot for my Android apps. It’s incredibly simple to do. Here’s an example of a TextView with the same inset shadow.

<!-- Semi-opaque white inset shadow beneath the text -->
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    ...
    android:shadowColor="#88FFFFFF"
    android:shadowRadius="0.1"
    android:shadowDx="0"
    android:shadowDy="1" />
       
<!-- Semi-opaque black inset shadow above the text -->
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    ...
    android:shadowColor="#88000000"
    android:shadowRadius="0.1"
    android:shadowDx="0"
    android:shadowDy="-1" />

And here’s how it looks:

Inset shadows

It should look great on all screen sizes. Don’t use the above code strictly, however. Mess around with the color values and shadowRadius value to get the exact effect you’re looking for.

Posted in Android | 2 Comments

Repeating Bitmaps inside LayerLists

I’ve run into this particular issue several times now. It’s a bug in the Android SDK, which apparently has been fixed as of ICS (Ice Cream Sandwich). When you place a <bitmap> inside a <layer-list>, it tends to do whatever it feels like doing in regards to repeating the bitmap. Sometimes it will follow your instructions, and sometimes it won’t.

To fix this, just set the repeat mode in code. Here’s a snippet that will set all your Bitmaps repeating in a LayerDrawable.

private void setLayerDrawableBitmapsRepeating(LayerDrawable layerDrawable) {
    final int size = layerDrawable.getNumberOfLayers();
    for(int i = 0; i < size; i++) {
        Drawable drawable = layerDrawable.getDrawable(i);
        if(drawable instanceof BitmapDrawable) {
            ((BitmapDrawable) drawable).setTileModeXY(TileMode.REPEAT, TileMode.REPEAT);
        }
    }
}
Posted in Android | Leave a comment

Getting visible bounds from a MapView

MapView doesn’t have a getBounds() method, but using a couple of MapView’s other methods, it’s actually very easy to find the visible bounds. MapView has the methods getCenter(), getLongitudeSpan(), and getLatitudeSpan(). By combining these methods we can get the visible bounds.

private Rect getMapBounds(MapView mapView) {
    final GeoPoint mapCenter = mapView.getMapCenter();
    final int lngHalfSpan = mapView.getLongitudeSpan() / 2;
    final int latHalfSpan = mapView.getLatitudeSpan() / 2;

    return new Rect(mapCenter.getLongitudeE6() - lngHalfSpan, mapCenter.getLatitudeE6() - latHalfSpan,
            mapCenter.getLongitudeE6() + lngHalfSpan, mapCenter.getLatitudeE6() + latHalfSpan);
}
Posted in Android | Leave a comment