I notice that other puzzle apps from the google play store can go up to as many as 400 separate movable puzzle pieces
I have been attempting to learn how to at least take an image that will represent my puzzle, Crop certain sections and mask the image space that is left with a puzzle piece design in order to create my individual puzzle pieces
I want to max out with 20 pieces for my app but so far according to my android studio memory log once bitmap factory is done creating one puzzle piece I’m using up around 18mb of memory, after 20 pieces are created along with all the other functions of the app I’m using up 400+mb of memory which I must use “largeHeap=true” to keep from running out of memory, but I’m so close to exceeding those higher limits that the app is super sluggish and enough animated activity will inevitably crash the app
I would love to know what those other play store puzzle apps are doing that I’m not
any input is greatly appreciated
FYI I’m using PNG24 for my images and the dimension on my test image is 556x720
Here is an example if I were to just create one animated-able puzzle piece image
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);//Remove title bar
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);//Hides notification bar
this.setContentView(R.layout.activity_main);//set content view AFTER ABOVE sequence (to avoid crash)
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
mainDisplay = getWindowManager().getDefaultDisplay();
mainLayout = (ViewGroup) findViewById(R.id.id_layout);
DisplayMetrics m = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(m);
int windowHeight = m.heightPixels;
int windowWidth = m.widthPixels;
offsetX = (windowWidth / 1440.0f);
offsetY = (windowHeight / 2560.0f);
ourContext = this;
xpos = new float[2];
ypos = new float[2];
iv_PuzzleOne();
bkgdbm = BitmapFactory.decodeResource(getResources(), R.drawable.puzzleimage);
paintObject = new Paint();
paintObject.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
bm_PuzzleOne();
thisTimerTask = new ThisClass();
thisTimer = new Timer();
thisTimer.scheduleAtFixedRate(thisTimerTask, 16, 16);
touchpad = new ImageButton(this);
SetPos(0, 0, 1440, 2560);
touchpad.setLayoutParams(layoutPositioner);
touchpad.getBackground().setAlpha(1);
mainLayout.addView(touchpad);
touchpad.setOnTouchListener(new View.OnTouchListener() {
//@SuppressLint("NewApi")
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
xpos[0] = event.getX(); //storing my finger's position coordinates when I first touch the screen into the 1st element
ypos[0] = event.getY();
if ((event.getX() > imgx1) && (event.getX() < imgx1 + imagewidth1)
&& (event.getY() > imgy1) && (event.getY() < imgy1 + imageheight1)) {
touching1Puzzle = true;
img1.bringToFront();
}
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
xpos[1] = event.getX(); //add my finger's new current coordinates into the 2nd element
ypos[1] = event.getY();
if (touching1Puzzle) {
adjustImg();
}
}
if (event.getAction() == MotionEvent.ACTION_UP) {
if (touching1Puzzle) {
touching1Puzzle = false;
}
}
return false;
}
});
}
void bm_PuzzleOne()
{
//start of 1st puzzle
foregdimg1 = BitmapFactory.decodeResource(getResources(), R.drawable.puzzlepieceprac);//puzzle cut out (42.48MB) +6.15
mutableforegdimg1 = foregdimg1.copy(Bitmap.Config.ARGB_8888, true); //(48.32MB) +5.84
compositeImage1 = Bitmap.createBitmap(mutableforegdimg1);//cuts out foreground info into bkgdimage (54.43MB) +6.11
imgCanvas1 = new Canvas(compositeImage1); //canvas references puzzle cut out image (54.43MB) +0
imgCanvas1.drawBitmap(croppedBmp, null, new Rect(0, 0, 1500, 2000), paintObject);//places puzzle image on canvas (54.43MB) +0
img1.setImageBitmap(compositeImage1);
}
void iv_PuzzleOne()
{
img1 = new ImageView(ourContext);
SetPos(imgx1, imgy1, imagewidth1, imageheight1);
//bkgdimg.setImageResource(R.drawable.c);
//img1.setBackgroundColor(0xffF07305); //Orange
img1.setLayoutParams(layoutPositioner);
mainLayout.addView(img1);
}
void adjustImg()
{
if (touching1Puzzle)
{
if (xpos[1] > xpos[0]) //if the image had slid to the right
{
xPositionDifference = xpos[1] - xpos[0]; // find the difference in coordinate value between where my finger was and where it currently is
imgx1 += xPositionDifference; //add that difference to the current image position ...
xpos[0] += xPositionDifference; // ... store that difference for the next shift in finger postion
} else if (xpos[1] < xpos[0]) //if the image had slid to the left
{
xPositionDifference = xpos[0] - xpos[1]; // find the difference in coordinate value between where my finger was and where it currently is
imgx1 -= xPositionDifference; //subtract that difference to the current image position ...
xpos[0] -= xPositionDifference; // ... store that difference for the next shift in finger postion
}
if (ypos[1] > ypos[0]) //if the image had slid to the right
{
yPositionDifference = ypos[1] - ypos[0]; // find the difference in coordinate value between where my finger was and where it currently is
imgy1 += yPositionDifference; //add that difference to the current image position ...
ypos[0] += yPositionDifference; // ... store that difference for the next shift in finger postion
} else if (ypos[1] < ypos[0]) //if the image had slid to the left
{
yPositionDifference = ypos[0] - ypos[1]; // find the difference in coordinate value between where my finger was and where it currently is
imgy1 -= yPositionDifference; //subtract that difference to the current image position ...
ypos[0] -= yPositionDifference; // ... store that difference for the next shift in finger postion
}
}
}
class ThisClass extends TimerTask {
@Override
public void run() {
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
if(touching1Puzzle)
{SetPos(imgx1, imgy1, imagewidth1, imageheight1);
img1.setLayoutParams(layoutPositioner);}
}
});
}
}
public void SetPos(float x, float y, float width, float height) {
layoutPositioner = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutPositioner.topMargin = (int) (offsetY * y);
layoutPositioner.leftMargin = (int) (offsetX * x);
layoutPositioner.width = (int) (width * offsetX);
layoutPositioner.height = (int) (height * offsetY);
}
}
This is what it looks like when I load 5 images
http://s15.postimg.org/ymqspk77v/Screenshot_2016_02_29_07_19_26.png
Alright let's see.
First of all you don't need puzzle pieces to be this big, so you can scale them down:
From http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
To tell the decoder to subsample the image, loading a smaller version into memory, set inSampleSize to true in your BitmapFactory.Options object. For example, an image with resolution 2048x1536 that is decoded with an inSampleSize of 4 produces a bitmap of approximately 512x384. Loading this into memory uses 0.75MB rather than 12MB for the full image (assuming a bitmap configuration of ARGB_8888)
Secondly you should try loading Bitmaps in an AsyncTask to prevent the application from freezing.
Thirdly you can cache your Bitmaps using LruCache to speed things up
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
private LruCache<String, Bitmap> mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Get max available VM memory, exceeding this amount will throw an
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
// int in its constructor.
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount() / 1024;
}
};
...
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
That should be enough but in case it isn't here are all sources and then some extra: http://developer.android.com/training/displaying-bitmaps/index.html
You can also try using
void bm_PuzzleOne()
{
//start of 1st puzzle
BitmapFactory.Options opt = new BitmapFactory.Options();
//makes a loaded image mutable
opt.inMutable=true;
//reduces density to that of screen
opt.inTargetDensity = this.context.getResources().getDisplayMetrics().densityDpi;
opt.inScaled=true;
//makes bitmap half as big
opt.inSampleSize=2;
//foregdimg1 = BitmapFactory.decodeResource(getResources(), R.drawable.puzzlepieceprac);//puzzle cut out (42.48MB) +6.15
mutableforegdimg1 = BitmapFactory.decodeResource(getResources(), R.drawable.puzzlepieceprac,opt)
compositeImage1 = Bitmap.createBitmap(mutableforegdimg1);//cuts out foreground info into bkgdimage (54.43MB) +6.11
imgCanvas1 = new Canvas(compositeImage1); //canvas references puzzle cut out image (54.43MB) +0
imgCanvas1.drawBitmap(croppedBmp, null, new Rect(0, 0, 1500, 2000), paintObject);//places puzzle image on canvas (54.43MB) +0
img1.setImageBitmap(compositeImage1);
}
instead of
void bm_PuzzleOne()
{
//start of 1st puzzle
foregdimg1 = BitmapFactory.decodeResource(getResources(), R.drawable.puzzlepieceprac);//puzzle cut out (42.48MB) +6.15
mutableforegdimg1 = foregdimg1.copy(Bitmap.Config.ARGB_8888, true); //(48.32MB) +5.84
compositeImage1 = Bitmap.createBitmap(mutableforegdimg1);//cuts out foreground info into bkgdimage (54.43MB) +6.11
imgCanvas1 = new Canvas(compositeImage1); //canvas references puzzle cut out image (54.43MB) +0
imgCanvas1.drawBitmap(croppedBmp, null, new Rect(0, 0, 1500, 2000), paintObject);//places puzzle image on canvas (54.43MB) +0
img1.setImageBitmap(compositeImage1);
}
Simply just use vectors instead of images.. you will save much memory for running app and storage. Its supported very well. and you can use SVG resources
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With