Domanda

In my Android app I created a View showing a field of 9x9 (see simplified screenshot):

enter image description here

Each field in the view is represented by an ImageView (showing pictures instead of rectangles). Some ImageViews will be deleted by setting them to null and setting visibility to View.GONE and new ImageViews will be created every time a new element is added to the field. This means I create lots of ImageViews in a short amount of time. The point is, the game runs great on my HTC One, but it's lagging after a while (I think when garbage collection is running) and then it stops lagging and works great again.

This brings me to the idea of using Pool's to manage my objects, e.g. by recycling "deleted ImageViews" and reuse them by setting the position and changing the image source. I'm developing for plain Android, but I think about using the LibGDX framework.

My question is: Would you have any suggestions on how to implement Pool's in plain Java/Android? I found this post interesting, thought I could go with the Apache Commons ObjectPool. Is there a better way on Android or LibGDX?

Note: The ImageView's are not at the same position each round. I move them around using the Tween Engine. However, the number of the image views is kind of constant. Means at some point of the game I do have 81 ImageView's (9*9) plus a few ImageView's for animating special events (maybe +10).

I would appreciate any advices/recommendations.

Best regards,

Jimmy

È stato utile?

Soluzione

According to Jimmy's suggestion, I am posting my comment as the answer

I suggest to make use of some kind of collection e.g List< ImageView >,

  • every image view that is no more neccesary should go there

  • if some new ImageView is neccessary then you should first check if your list doesn't contain one, and update its background and position.

  • Of course if you use it, you should remove it from the list.
  • Create new ImageView only if the list is empty.

I think it is one of the simplest Cache mechanism, but it works properly in e.g ListView (reuse of row Views)

Altri suggerimenti

I had a similar issue and never was really satisfied with the performance. The tip of it all was when I expanded my playfield to 30*30 so I got 900 ImageViews. I tried many optimizations, but the performance and memory was high and unpredictable across devices.

So what I did is to create a custom View. Just one. This view then paints squares in a canvas. I was amazed that now I can have literally tens of thousands of squares (100x100) and performance is ultra-smooth.

I post here the skeleton of my view with all the crap removed for an inspiration, I strongly recommend you to follow this approach.

    /**
     * ChequeredView is a view that displays a 2D square matrix, where each square can be individually selected.
     * For high performance, It only uses one view regardless of the matrix size (everything is drawn in a canvas)
     * @author rodo 13 march 2014 <rlp@nebular.tv>
     */

    public class ChequeredView extends View {

        private final String TAG="ChequeredView";

        private static final int 
            DEFAULT_MATRIX_SIZE_COLS=20, 
            DEFAULT_MATRIX_SIZE_COLS=20, 
            DEFAULT_SQUARE_SIZE=100;

        private int mCols=DEFAULT_MATRIX_SIZE_COLS, 
                    mRows=DEFAULT_MATRIX_SIZE_ROWS;

        /* Save touch press */
        private int mTouchX=0, mTouchY=0;

        ///////////////// VIEW CODE

        public ChequeredView(Context context, AttributeSet attrs) { super(context, attrs); }
        public ChequeredView(Context context) { super(context); }

        /**
         * Report a size of your view that is: SquareSize * NUM_COLS x SquareSize * NUM_ROWS. You will paint it later.
         */

        @Override
        protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);

            // calculate optimum square size
            mStyleSquareSize=MeasureSpec.getSize(widthMeasureSpec) / mSquaresPerCanvas;

            // report total size
            setMeasuredDimension(DEFAULT_MATRIX_SIZE_COLS * mStyleSquareSize, DEFAULT_MATRIX_SIZE_ROWS * mStyleSquareSize);
        }

        @Override
        public void onDraw(Canvas canvas)  {
            render(canvas);
        }


        @Override 
        public boolean onTouchEvent(android.view.MotionEvent event) {

            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // I execute the action in ACTION_UP so I can put this inside a scrollview and touch doesn't interferre the scroll.
                mTouchX=(int) event.getX();
                mTouchY=(int) event.getY();
                return true;

            case MotionEvent.ACTION_UP:

                // only process touch if finger has not moved very much (if it has, it's a fling on parent)

                if ( isApprox(event.getX(), mTouchX, 5) && (isApprox(event.getY(), mTouchY, 5)) ) 
                        processTouch((int)event.getX(), (int)event.getY());

                break;

            }
            return false;
        };

        /**
         * Check if a value is close to another one
         * @param value Value to check
         * @param ref Reference value
         * @param threshold Threshold
         * @return true if |val-ref|<threshold
         */

        private boolean isApprox(float value, int ref, int threshold) {
            float result=Math.abs(value-ref);
            return (result<threshold);
        }

        ///////////////// VIEW METHODS


        public void setMatrixSize(int numx, int numy) {
            mRows=numx;
            mCols=numy;
            invalidate();
        }

        ///////////////// VIEW INTERNALS


        /**
         * Renders the whole squaremap
         * @param canvas
         */

        private void render(Canvas canvas) {
            if (canvas==null) return;
            for (int x=0; x<mCols; x++) {
                for (int y=0; y<mRows; y++) {
                    render_square(canvas, x, y);
                }
            }
        }

        /**
         * Renders one of the squares
         * @param canvas Canvas where to draw
         * @param nCol The column
         * @param nRow The row
         */

        private void render_square(Canvas canvas, int nCol, int nRow) {


            String text=null, transition=null;
            int delay=0;
            Paint paint=null;

            int cx=nCol*mStyleSquareSize, cy=nRow*mStyleSquareSize;

            canvas.save();
            canvas.translate(cx, cy);
            canvas.drawRect(mStyleSquareMargin, mStyleSquareMargin, mStyleSquareSize-2*mStyleSquareMargin, mStyleSquareSize-2*mStyleSquareMargin, paint);

            // this draws an square (I use vectorial squares with text rather than images, but just change drawRect to drawBitmap)
            // just change it for drawBitmap() to draw one bitmap

            canvas.restore();
        }

        /**
         * Process a touch on the map area
         * @param x raw x coordinate
         * @param y raw y coordinate
         */ 

        private void processTouch(int x, int y) {
            int nx=x/mStyleSquareSize, ny=y/mStyleSquareSize;
            mSelectedX=nx;
            mSelectedY=ny;
            if (mSquareListener!=null) {
                mSquareListener.onSquareSelected(nx, ny, data);
            } 
            invalidate();
        }
    }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top