9

我注意到来自 google play 商店的其他拼图应用程序可以多达 400 个单独的可移动拼图

我一直在尝试学习如何至少拍摄一张能代表我的拼图的图像,裁剪某些部分并用拼图设计掩盖留下的图像空间,以创建我的个人拼图

我想为我的应用程序最多使用 20 块,但到目前为止,根据我的 android studio 内存日志,一旦位图工厂完成创建一个拼图块,我正在使用大约 18mb 的内存,在与所有其他块一起创建 20 块之后应用程序的功能我使用了 400+mb 的内存,我必须使用“largeHeap=true”来避免内存不足,但我已经接近超过这些更高的限制,以至于应用程序超级缓慢且足够动画活动将不可避免地使应用程序崩溃

我很想知道其他 Play Store 益智应用在做什么,而我没有

非常感谢任何输入

仅供参考,我的图像使用 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

4

5 回答 5

3

好吧,让我们看看。

首先,你不需要这么大的拼图,所以你可以缩小它们:

来自 http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

要告诉解码器对图像进行二次采样,将较小的版本加载到内存中,请在 BitmapFactory.Options 对象中将 inSampleSize 设置为 true。例如,分辨率为 2048x1536 的图像使用 inSampleSize 为 4 进行解码会生成大约 512x384 的位图。将其加载到内存中使用 0.75MB 而不是 12MB 的完整图像(假设位图配置为 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);

}
于 2016-03-02T18:40:47.753 回答
2

只需使用矢量而不是图像。您将为运行应用程序和存储节省大量内存。它的支持非常好。你可以使用 SVG 资源

于 2016-03-09T15:04:26.880 回答
2

我建议使用诸如 Glide 或 Picasso 之类的图像加载库,它可以为您处理所有繁重的工作。

https://github.com/bumptech/glide

http://square.github.io/picasso/

于 2016-03-08T22:16:57.980 回答
1

First of all I should thank @JeD for his great answer, BUT I should mention that I think the answer is different.

Lets begin from the basics...


Each android device has an specific "heap size". it begin from 16mg to 64mg and even 128mg in some devices! (im not sure about 128 mg :D)

So whenever you use a drawable in your view, it takes a specific amount of ram.

Question: How is it calculated?!

Its by the drawables dimension, not the size. For example we have two drawables with 500 KBs of size. One of them has the dimensions 500*500, the other has 700*700 The second one takes more heap although both sizes were the same! The formula is this: WIDTH*HEIGHT*4 BYTE (4 comes from ARGB, it takes four bytes for each pixel to store data)

By now, you should understand that you cant use MANY images together! If you want to use LRUCACH as @JeD mentioned, you would lose some of your images as your heap would get full.

Question : So what should we do?!

The answer is using third party engines and canvas! Game engines like COCOS2D,BOX2D,etc... are also good examples or also using NDK which makes your app HEAP FREE!!! :D

于 2016-03-07T13:57:47.547 回答
0
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();

}

}

于 2021-05-28T08:24:11.403 回答