在Android中处理ImageViews的对象池?

时间:2014-03-26 21:35:42

标签: java android libgdx object-pooling

在我的Android应用中,我创建了一个显示9x9字段的视图(参见简化屏幕截图):

enter image description here

视图中的每个字段都由ImageView表示(显示图片而不是矩形)。通过将某些ImageView设置为null并将可见性设置为View.GONE,将删除某些ImageView,并且每次将新元素添加到字段时都会创建新的ImageView。这意味着我会在很短的时间内创建大量的ImageView。关键是,游戏在我的HTC One上运行得很好,但是它在一段时间后会滞后(我认为当垃圾收集正在运行时)然后它会停止滞后并且再次运行良好。

这让我想到了使用Pool来管理我的对象,例如:通过回收“已删除的ImageViews”并通过设置位置和更改图像源来重复使用它们。我正在为简单的Android开发,但我考虑使用LibGDX框架。

我的问题是:您对如何在普通Java / Android中实现Pool有任何建议吗?我发现this帖子很有意思,以为我可以使用Apache Commons ObjectPool。在Android或LibGDX上有更好的方法吗?

注意:每一轮ImageView都不在同一个位置。我使用Tween引擎移动它们。但是,图像视图的数量是不变的。在游戏的某些方面,我有81个ImageView(9 * 9)加上一些ImageView用于动画特殊事件(可能是+10)。

我很感激任何建议/建议。

致以最诚挚的问候,

2 个答案:

答案 0 :(得分:1)

我有类似的问题,从未对表现感到满意。最重要的是当我将我的playfield扩展到30 * 30时,我得到了900个ImageViews。我尝试了很多优化,但是各种设备的性能和内存都很高且无法预测。

所以我做的是创建一个自定义视图。只有一个。然后,此视图在画布中绘制正方形。令我惊讶的是,现在我可以拥有数万个正方形(100x100)并且性能非常流畅。

我在这里发布了我的视图的骨架,并删除了所有废话,我强烈建议您遵循这种方法。

    /**
     * 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();
        }
    }

答案 1 :(得分:1)

根据Jimmy的建议,我将发表评论作为答案

我建议使用某种类型的集合,例如List< ImageView >

  • 每个不再需要的图片视图都应该去那里

  • 如果需要一些新的ImageView,那么您应首先检查您的列表是否包含一个,并更新其背景和位置。

  • 当然如果你使用它,你应该从列表中删除它。
  • 仅当列表为空时才创建新的ImageView。

我认为它是最简单的Cache机制之一,但它在ListView(重用行视图)中正常工作