So I'm new to Java and this kind of coding.
I'm trying to make a Parrallax scrolling Live Wallpaper. But I'm having memory issues.
Well I have made it, and it works on on the phone I have. But I think the way i have done it is not very efficient at all. Because when I try it on other phones it doesn't work. It breaks at my Out Of Memory catcher. I added another layer and now it does the same thing on my phone too. So I am able to debug it. Basically I guess I'm using up-to or over 16 meg of memory.
If someone could take a look at my code and help me load in the bitmaps more efficiently that would be greatly appreciated.
Here is how I'm currently doing it:
static class Layer {
public Bitmap bitmap;
private float scale = 1.0f;
private Matrix matrix = new Matrix();
public Layer(Bitmap b) {
this.bitmap = b;
}
public void setScale(float factor) {
scale = factor;
}
public Matrix getMatrix(float x, float y) {
if (scale == 1) {
matrix.reset();
} else {
matrix.setScale(scale, scale);
}
matrix.postTranslate(x, y);
return matrix;
}
}
public static List<Integer> findLayers(Integer path) {
List<Integer> files = new ArrayList<Integer>();
files.add(R.drawable.planet_layer4);
files.add(R.drawable.planet_layer3);
files.add(R.drawable.planet_layer2);
files.add(R.drawable.planet_layer1);
files.add(R.drawable.planet_layer0);
return files;
}
private void loadLayers() {
try {
clearLayers();
for (Integer file: layerFiles) {
addLayer(file);
}
recalibrateLayers();
} catch (IOException e) {
layers.clear();
Toast.makeText(LiveWallpaper.this, "There was a problem loading the wallpaper. Please contact the developer.", Toast.LENGTH_LONG).show();
} catch (OutOfMemoryError oom) {
layers.clear();
Toast.makeText(LiveWallpaper.this, "Whoops, we ran out of memory trying to load the images. ", Toast.LENGTH_LONG).show();
}
}
private void addLayer(int name) throws IOException {
Bitmap layer = BitmapFactory.decodeResource(getResources(), name);
if (layer == null) {
throw new IOException("BitmapFactory couldn't decode asset " + name);
}
synchronized(layers) {
layers.add(new Layer(layer));
}
}
private void clearLayers() {
synchronized(layers) {
layers.clear();
}
}
private void recalibrateLayers() {
for (Layer layer : layers) {
final int bitmapHeight = layer.bitmap.getHeight();
layer.setScale((float)mHeight / (float)bitmapHeight);
}
}
@Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mDrawParallax);
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
mHeight = height;
recalibrateLayers();
drawBackgrounds();
}
And here is where I draw them.
/*
* Draw one frame of the animation. This method gets called repeatedly
* by posting a delayed Runnable. You can do any drawing you want in
* here.
*/
void drawBackgrounds() {
final SurfaceHolder holder = getSurfaceHolder();
final Rect frame = holder.getSurfaceFrame();
mFrame = frame;
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
// draw something
drawParallax(c);
}
} finally {
if (c != null) holder.unlockCanvasAndPost(c);
}
}
void drawParallax(Canvas c) {
int frameWidth = mFrame.width();
for (int i=layers.size()-1; i>=0; i--) {
Layer layer = layers.get(i);
Bitmap bitmap = layer.bitmap;
float bitmapWidth = bitmap.getWidth() * layer.scale;
float max = frameWidth - bitmapWidth;
float offset = mOffset * max;
final Matrix m = layer.getMatrix(offset, 0);
c.drawBitmap(bitmap, m, null);
}
}