Как сделать приложение-головоломку в Droid без нехватки памяти?
Я заметил, что другие приложения-головоломки из магазина Google Play могут содержать до 400 отдельных подвижных частей головоломки
Я пытался научиться, по крайней мере, взять изображение, которое будет представлять мою головоломку, обрезать определенные участки и замаскировать пространство изображения, оставленное с помощью дизайна кусочка головоломки, чтобы создать мои отдельные кусочки головоломки.
Я хочу максимально использовать 20 штук для своего приложения, но пока, согласно журналу памяти андроид-студии, когда фабрика растровых изображений готова, создавая одну часть головоломки, я использую около 18 Мб памяти, после того, как 20 штук созданы вместе со всеми остальными Функции приложения Я использую 400+ МБ памяти, которую я должен использовать "largeHeap = true", чтобы не допустить исчерпания памяти, но я настолько близок к превышению этих более высоких пределов, что приложение очень вялое и достаточно анимированная активность неизбежно приведет к сбою приложения
Я хотел бы знать, что делают другие приложения-головоломки, что я не
любой вклад очень ценится
К вашему сведению, я использую 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
5 ответов
Просто используйте векторы вместо изображений. Вы сэкономите много памяти для запуска приложения и хранилища. Его поддерживают очень хорошо. и вы можете использовать ресурсы SVG
Хорошо, давайте посмотрим.
Прежде всего, вам не нужны кусочки головоломки, чтобы быть такими большими, чтобы вы могли уменьшить их:
От http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Чтобы сказать декодеру, что нужно выполнить выборку изображения, загружая уменьшенную версию в память, установите для inSampleSize значение true в вашем объекте BitmapFactory.Options. Например, изображение с разрешением 2048x1536, которое декодируется с inSampleSize, равным 4, создает растровое изображение приблизительно 512x384. Загрузка этого в память использует 0,75 МБ, а не 12 МБ для полного изображения (при условии конфигурации растрового изображения 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);
}
Я предлагаю использовать библиотеку загрузки изображений, такую как Glide или Picasso, которая справится со всей тяжелой работой за вас.
Прежде всего, я должен поблагодарить @JeD за его отличный ответ, НО я должен упомянуть, что я думаю, что ответ отличается.
Начнем с основ...
Каждое устройство Android имеет определенный "размер кучи". это начинается с 16 мг до 64 мг и даже 128 мг в некоторых устройствах! (Я не уверен насчет 128 мг:D)
Поэтому всякий раз, когда вы используете drawable
по вашему мнению, требуется определенное количество оперативной памяти.
Вопрос: Как рассчитывается?!
Это по drawable
размерность, а не размер. Например, у нас есть два drawable
с размером 500 КБ. Один из них имеет размеры 500*500, другой - 700*700, второй занимает больше кучи, хотя оба размера были одинаковыми! Формула такова: WIDTH*HEIGHT*4 BYTE (4 из ARGB, для хранения каждого пикселя требуется четыре байта)
К настоящему времени вы должны понимать, что вы не можете использовать МНОГИЕ изображения вместе! Если вы хотите использовать LRUCACH, как упомянуто @JeD, вы потеряете некоторые из ваших изображений, поскольку ваша куча будет заполнена.
Вопрос: Так что же нам делать?!
Ответ использует сторонние движки и холст! Игровые движки, такие как COCOS2D,BOX2D и т. Д.... также являются хорошими примерами или также используют NDK, что делает ваше приложение HEAP БЕСПЛАТНЫМ!!!:D
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();
}
}