如何在 运行 内存不足的情况下在 droid 中制作拼图应用程序?
How do I make a puzzle app in droid without running out of memory?
我注意到来自 google Play 商店的其他拼图应用程序最多可以有 400 个独立的可移动拼图块
我一直在尝试学习如何至少拍摄一张能代表我的谜题的图像,
裁剪某些部分并遮盖留下拼图设计的图像 space 以创建我的个人拼图
我想为我的应用程序最多使用 20 块,但根据我的 android 工作室内存日志,一旦位图工厂完成创建一个拼图块,我就用完了大约 18mb 的内存,之后与应用程序的所有其他功能一起创建了 20 个组件,我用完了 400+mb 的内存,我必须使用“largeHeap=true”来避免 运行 内存不足,但我已经很接近了超过应用程序超级缓慢和足够动画的更高限制 activity 将不可避免地使应用程序崩溃
我很想知道那些其他 Play 商店益智应用在做什么而我不会
非常感谢任何意见
仅供参考,我的图像使用 PNG24,测试图像的尺寸为 556x720
这里是一个例子,如果我只创建一个动画拼图图片
@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);
}
}
这是我加载 5 张图片时的样子
http://s15.postimg.org/ymqspk77v/Screenshot_2016_02_29_07_19_26.png
好吧,让我们看看。
首先你不需要这么大的拼图,所以你可以缩小它们:
来自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)
其次,您应该尝试在 AsyncTask 中加载位图以防止应用程序冻结。
第三,您可以使用 LruCache 缓存您的位图以加快速度
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);
}
这应该足够了,但如果还不够的话,这里有所有来源,还有一些额外的来源:
http://developer.android.com/training/displaying-bitmaps/index.html
您也可以尝试使用
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);
}
而不是
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);
}
首先我要感谢@JeD 的精彩回答,但我应该提到我认为答案是不同的。
让我们从基础开始...
每个 android 设备都有一个特定的 "heap size"。它从 16 毫克开始到 64 毫克,在某些设备中甚至是 128 毫克! (我不确定 128 毫克 :D)
因此,每当您在视图中使用 drawable
时,它都需要特定数量的内存。
Question: How is it calculated?!
它按 drawable
的维度,而不是大小。例如,我们有两个大小为 500 KB 的 drawable
。
其中一个尺寸为 500*500,另一个尺寸为 700*700
第二个占用更多的堆,尽管两个大小相同!
公式是这样的:WIDTH*HEIGHT*4 BYTE(4来自ARGB,每个像素需要4个字节来存储数据)
到现在,你应该明白你不能同时使用许多图像!如果你想像@JeD 提到的那样使用 LRUCACH,你会丢失一些图像,因为你的堆会变满。
Question : So what should we do?!
答案是使用第三方引擎和canvas!
COCOS2D、BOX2D 等游戏引擎也是很好的例子
或者也使用 NDK 让你的应用程序 HEAP FREE!!! :D
我建议使用图像加载库,例如 Glide 或 Picasso,它们可以为您处理所有繁重的工作。
只需使用矢量而不是图像。您将为 运行 应用程序和存储节省大量内存。它得到很好的支持。你可以使用 SVG 资源
Button b[]=new Button[9];
ArrayList list=new ArrayList();
int random;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_three);
final MediaPlayer mediaPlayer = MediaPlayer.create(this,R.raw.b2);
for(int i=0;i<b.length;i++)
{
int id=getResources().getIdentifier("b"+i,"id",getPackageName());
b[i]=findViewById(id);
b[i].setOnClickListener(this);
}
for(int i=0;i<b.length;i++)
{
while(true) {
random = new Random().nextInt(9);
Log.d("First=", "" + random);
if (!list.contains(random)) {
list.add(random);
Log.d("List=", "" + list.get(i).toString());
break;
}
}
}
for(int i=0;i<b.length;i++)
{
if(!list.get(i).toString().equals("0"))
{
b[i].setText(list.get(i).toString());
}
else
{
b[i].setText("");
}
}
}
public void back(View view) {
final MediaPlayer mediaPlayer = MediaPlayer.create(this,R.raw.b2);
mediaPlayer.start();
Intent intent = new Intent(three.this,newgame.class);
startActivity(intent);
finish();
}
@Override
public void onClick(View v) {
for(int i=0;i<b.length;i++)
{
if(v.getId()==b[i].getId())
{
final MediaPlayer mediaPlayer = MediaPlayer.create(this,R.raw.b2);
mediaPlayer.start();
}
}
if(v.getId()==b[0].getId())
{
if (b[1].getText().toString().equals("")) {
b[1].setText("" + b[0].getText().toString());
b[0].setText("");
}
if (b[3].getText().toString().equals("")) {
b[3].setText("" + b[0].getText().toString());
b[0].setText("");
}
}
if(v.getId()==b[1].getId())
{
if(b[0].getText().toString().equals(""))
{
b[0].setText(""+b[1].getText().toString());
b[1].setText("");
}
if(b[2].getText().toString().equals(""))
{
b[2].setText(""+b[1].getText().toString());
b[1].setText("");
}
if(b[4].getText().toString().equals(""))
{
b[4].setText(""+b[1].getText().toString());
b[1].setText("");
}
}
if(v.getId()==b[2].getId())
{
if (b[1].getText().toString().equals(""))
{
b[1].setText("" + b[2].getText().toString());
b[2].setText("");
}
if (b[5].getText().toString().equals(""))
{
b[5].setText("" + b[2].getText().toString());
b[2].setText("");
}
}
if(v.getId()==b[3].getId())
{
if(b[0].getText().toString().equals(""))
{
b[0].setText(""+b[3].getText().toString());
b[3].setText("");
}
if(b[4].getText().toString().equals(""))
{
b[4].setText(""+b[3].getText().toString());
b[3].setText("");
}
if(b[6].getText().toString().equals(""))
{
b[6].setText(""+b[3].getText().toString());
b[3].setText("");
}
}
if(v.getId()==b[4].getId())
{
if(b[1].getText().toString().equals(""))
{
b[1].setText(""+b[4].getText().toString());
b[4].setText("");
}
if(b[3].getText().toString().equals(""))
{
b[3].setText(""+b[4].getText().toString());
b[4].setText("");
}
if(b[5].getText().toString().equals(""))
{
b[5].setText(""+b[4].getText().toString());
b[4].setText("");
}
if(b[7].getText().toString().equals(""))
{
b[7].setText(""+b[4].getText().toString());
b[4].setText("");
}
}
if(v.getId()==b[5].getId())
{
if(b[2].getText().toString().equals(""))
{
b[2].setText(""+b[5].getText().toString());
b[5].setText("");
}
if(b[4].getText().toString().equals(""))
{
b[4].setText(""+b[5].getText().toString());
b[5].setText("");
}
if(b[8].getText().toString().equals(""))
{
b[8].setText(""+b[5].getText().toString());
b[5].setText("");
}
}
if(v.getId()==b[6].getId())
{
if (b[7].getText().toString().equals(""))
{
b[7].setText("" + b[6].getText().toString());
b[6].setText("");
}
if (b[3].getText().toString().equals(""))
{
b[3].setText("" + b[6].getText().toString());
b[6].setText("");
}
}
if(v.getId()==b[7].getId())
{
if(b[4].getText().toString().equals(""))
{
b[4].setText(""+b[7].getText().toString());
b[7].setText("");
}
if(b[6].getText().toString().equals(""))
{
b[6].setText(""+b[7].getText().toString());
b[7].setText("");
}
if(b[8].getText().toString().equals(""))
{
b[8].setText(""+b[7].getText().toString());
b[7].setText("");
}
}
if(v.getId()==b[8].getId())
{
if (b[7].getText().toString().equals(""))
{
b[7].setText("" + b[8].getText().toString());
b[8].setText("");
}
if (b[5].getText().toString().equals(""))
{
b[5].setText("" + b[8].getText().toString());
b[8].setText("");
}
}
}
public void reset(View view) {
final MediaPlayer mediaPlayer = MediaPlayer.create(this,R.raw.b2);
mediaPlayer.start();
Intent intent = new Intent(three.this,three.class);
startActivity(intent);
finish();
}
}
我注意到来自 google Play 商店的其他拼图应用程序最多可以有 400 个独立的可移动拼图块
我一直在尝试学习如何至少拍摄一张能代表我的谜题的图像, 裁剪某些部分并遮盖留下拼图设计的图像 space 以创建我的个人拼图
我想为我的应用程序最多使用 20 块,但根据我的 android 工作室内存日志,一旦位图工厂完成创建一个拼图块,我就用完了大约 18mb 的内存,之后与应用程序的所有其他功能一起创建了 20 个组件,我用完了 400+mb 的内存,我必须使用“largeHeap=true”来避免 运行 内存不足,但我已经很接近了超过应用程序超级缓慢和足够动画的更高限制 activity 将不可避免地使应用程序崩溃
我很想知道那些其他 Play 商店益智应用在做什么而我不会
非常感谢任何意见
仅供参考,我的图像使用 PNG24,测试图像的尺寸为 556x720
这里是一个例子,如果我只创建一个动画拼图图片
@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);
}
}
这是我加载 5 张图片时的样子
http://s15.postimg.org/ymqspk77v/Screenshot_2016_02_29_07_19_26.png
好吧,让我们看看。
首先你不需要这么大的拼图,所以你可以缩小它们:
来自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)
其次,您应该尝试在 AsyncTask 中加载位图以防止应用程序冻结。
第三,您可以使用 LruCache 缓存您的位图以加快速度
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);
}
这应该足够了,但如果还不够的话,这里有所有来源,还有一些额外的来源: http://developer.android.com/training/displaying-bitmaps/index.html
您也可以尝试使用
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);
}
而不是
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);
}
首先我要感谢@JeD 的精彩回答,但我应该提到我认为答案是不同的。
让我们从基础开始...
每个 android 设备都有一个特定的 "heap size"。它从 16 毫克开始到 64 毫克,在某些设备中甚至是 128 毫克! (我不确定 128 毫克 :D)
因此,每当您在视图中使用 drawable
时,它都需要特定数量的内存。
Question: How is it calculated?!
它按 drawable
的维度,而不是大小。例如,我们有两个大小为 500 KB 的 drawable
。
其中一个尺寸为 500*500,另一个尺寸为 700*700
第二个占用更多的堆,尽管两个大小相同!
公式是这样的:WIDTH*HEIGHT*4 BYTE(4来自ARGB,每个像素需要4个字节来存储数据)
到现在,你应该明白你不能同时使用许多图像!如果你想像@JeD 提到的那样使用 LRUCACH,你会丢失一些图像,因为你的堆会变满。
Question : So what should we do?!
答案是使用第三方引擎和canvas! COCOS2D、BOX2D 等游戏引擎也是很好的例子 或者也使用 NDK 让你的应用程序 HEAP FREE!!! :D
我建议使用图像加载库,例如 Glide 或 Picasso,它们可以为您处理所有繁重的工作。
只需使用矢量而不是图像。您将为 运行 应用程序和存储节省大量内存。它得到很好的支持。你可以使用 SVG 资源
Button b[]=new Button[9];
ArrayList list=new ArrayList();
int random;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_three);
final MediaPlayer mediaPlayer = MediaPlayer.create(this,R.raw.b2);
for(int i=0;i<b.length;i++)
{
int id=getResources().getIdentifier("b"+i,"id",getPackageName());
b[i]=findViewById(id);
b[i].setOnClickListener(this);
}
for(int i=0;i<b.length;i++)
{
while(true) {
random = new Random().nextInt(9);
Log.d("First=", "" + random);
if (!list.contains(random)) {
list.add(random);
Log.d("List=", "" + list.get(i).toString());
break;
}
}
}
for(int i=0;i<b.length;i++)
{
if(!list.get(i).toString().equals("0"))
{
b[i].setText(list.get(i).toString());
}
else
{
b[i].setText("");
}
}
}
public void back(View view) {
final MediaPlayer mediaPlayer = MediaPlayer.create(this,R.raw.b2);
mediaPlayer.start();
Intent intent = new Intent(three.this,newgame.class);
startActivity(intent);
finish();
}
@Override
public void onClick(View v) {
for(int i=0;i<b.length;i++)
{
if(v.getId()==b[i].getId())
{
final MediaPlayer mediaPlayer = MediaPlayer.create(this,R.raw.b2);
mediaPlayer.start();
}
}
if(v.getId()==b[0].getId())
{
if (b[1].getText().toString().equals("")) {
b[1].setText("" + b[0].getText().toString());
b[0].setText("");
}
if (b[3].getText().toString().equals("")) {
b[3].setText("" + b[0].getText().toString());
b[0].setText("");
}
}
if(v.getId()==b[1].getId())
{
if(b[0].getText().toString().equals(""))
{
b[0].setText(""+b[1].getText().toString());
b[1].setText("");
}
if(b[2].getText().toString().equals(""))
{
b[2].setText(""+b[1].getText().toString());
b[1].setText("");
}
if(b[4].getText().toString().equals(""))
{
b[4].setText(""+b[1].getText().toString());
b[1].setText("");
}
}
if(v.getId()==b[2].getId())
{
if (b[1].getText().toString().equals(""))
{
b[1].setText("" + b[2].getText().toString());
b[2].setText("");
}
if (b[5].getText().toString().equals(""))
{
b[5].setText("" + b[2].getText().toString());
b[2].setText("");
}
}
if(v.getId()==b[3].getId())
{
if(b[0].getText().toString().equals(""))
{
b[0].setText(""+b[3].getText().toString());
b[3].setText("");
}
if(b[4].getText().toString().equals(""))
{
b[4].setText(""+b[3].getText().toString());
b[3].setText("");
}
if(b[6].getText().toString().equals(""))
{
b[6].setText(""+b[3].getText().toString());
b[3].setText("");
}
}
if(v.getId()==b[4].getId())
{
if(b[1].getText().toString().equals(""))
{
b[1].setText(""+b[4].getText().toString());
b[4].setText("");
}
if(b[3].getText().toString().equals(""))
{
b[3].setText(""+b[4].getText().toString());
b[4].setText("");
}
if(b[5].getText().toString().equals(""))
{
b[5].setText(""+b[4].getText().toString());
b[4].setText("");
}
if(b[7].getText().toString().equals(""))
{
b[7].setText(""+b[4].getText().toString());
b[4].setText("");
}
}
if(v.getId()==b[5].getId())
{
if(b[2].getText().toString().equals(""))
{
b[2].setText(""+b[5].getText().toString());
b[5].setText("");
}
if(b[4].getText().toString().equals(""))
{
b[4].setText(""+b[5].getText().toString());
b[5].setText("");
}
if(b[8].getText().toString().equals(""))
{
b[8].setText(""+b[5].getText().toString());
b[5].setText("");
}
}
if(v.getId()==b[6].getId())
{
if (b[7].getText().toString().equals(""))
{
b[7].setText("" + b[6].getText().toString());
b[6].setText("");
}
if (b[3].getText().toString().equals(""))
{
b[3].setText("" + b[6].getText().toString());
b[6].setText("");
}
}
if(v.getId()==b[7].getId())
{
if(b[4].getText().toString().equals(""))
{
b[4].setText(""+b[7].getText().toString());
b[7].setText("");
}
if(b[6].getText().toString().equals(""))
{
b[6].setText(""+b[7].getText().toString());
b[7].setText("");
}
if(b[8].getText().toString().equals(""))
{
b[8].setText(""+b[7].getText().toString());
b[7].setText("");
}
}
if(v.getId()==b[8].getId())
{
if (b[7].getText().toString().equals(""))
{
b[7].setText("" + b[8].getText().toString());
b[8].setText("");
}
if (b[5].getText().toString().equals(""))
{
b[5].setText("" + b[8].getText().toString());
b[8].setText("");
}
}
}
public void reset(View view) {
final MediaPlayer mediaPlayer = MediaPlayer.create(this,R.raw.b2);
mediaPlayer.start();
Intent intent = new Intent(three.this,three.class);
startActivity(intent);
finish();
}
}