0

编辑:增加了赏金。希望这会引起人们对手头问题的关注。

我目前正在尝试解决一个动画在应用程序的主屏幕上连续运行动画婴儿睡觉的问题。这是通过使用 imageView 并将动画中的当前图像替换为下一个图像来完成的。鉴于有超过 500 张图像,这变得非常占用 CPU,因为每次替换图像时都会重新计算整个视图层次结构。有没有办法在这个动画期间不重新计算整个视图层次?

运行方法如下所示:

        @Override
    public void run() {
        if (bitmap != null) {
            bitmap.recycle();
        }
        final String name = String.format(Locale.ENGLISH, pattern, current);
        final int id = getResources().getIdentifier(name, "drawable", getPackageName());
        if (id != 0) {
            bitmap = BitmapFactory.decodeResource(getResources(), id);
            view.setImageBitmap(bitmap);
        }
        current++;
        if (runAgain()) {
            frameCounter++;
            long nextAt = firstFrameAt + (frameCounter * interval);
            long delay = nextAt - SystemClock.elapsedRealtime() - 1;
            if (delay > 0) {
                handler.postDelayed(this, delay);
            } else {
                this.run();
            }
        }
    }

编辑:我一直在考虑创建自己的自定义 ImageView 并覆盖导致问题的任何方法。这会是一个可行的解决方案吗?

4

4 回答 4

0

是的,只是阻止布局请求。覆盖 onRequestLayout 并且不要调用超级。您可能必须在您的父级中执行一些手动 onLayout 以设置正确的大小(或者如果您是 ICS 则覆盖布局方法......不能在 2.x 中将其标记为最终版本)。说得通?

我的容器视图示例:

@Override
public void requestLayout() {

}

// i'm ignoring the widths/heights here an manually setting it to what i know it should be. The children DO need a layout initially or their widths will be 0 and not show. 
 // 

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);
    int count = getChildCount();
    for (int i=0; i<count; i++) {
        ImageView iv = (ImageView)getChildAt(i);
        iv.layout(0, 0, width, height);
    }
}

所以现在每次孩子发生变化时,我都不必重新验证整个视图层次结构,因为我知道尺寸没有改变。这在设备上节省了大量的 CPU。您可能还想考虑这样的事情:优化的图像视图

于 2013-01-31T02:45:08.097 回答
0

我通过实现我自己的自定义 ImageView 覆盖以下两种方法解决了这个问题:

@Override
public void setImageBitmap(final Bitmap bitmap) {
    if (VERSION.SDK_INT < VERSION_CODES.ICE_CREAM_SANDWICH) {

        // The currently set Drawable
        Bitmap oldBitmap = null;
        try
        {
            oldBitmap = ((BitmapDrawable)this.getDrawable()).getBitmap();   
        }catch(NullPointerException e)
        {
            Log.d("setImageBitmap", "oldBitmap is null");
        }

        if (null != oldBitmap && oldBitmap != bitmap) {
            final int oldWidth = oldBitmap.getWidth();
            final int oldHeight = oldBitmap.getHeight();

            /**
             * Ignore the next requestLayout call if the new Bitmap is the
             * same size as the currently displayed one.
             * */
            mIgnoreNextRequestLayout = oldHeight == bitmap.getHeight()
                    && oldWidth == bitmap.getWidth();
        }
    }

    super.setImageBitmap(bitmap);
}

@Override
public void requestLayout() {
    if (!mIgnoreNextRequestLayout) {
        super.requestLayout();
        Log.d("RequestLayout", "Requesting new layout");
    }
    else
        Log.d("RequestLayout", "Ignoring next requestLayout");

    // Reset Flag so that the requestLayout() will work again
    mIgnoreNextRequestLayout = false;
}
于 2013-02-03T12:11:33.267 回答
0

覆盖 ImageView 和(至少)它的 onDraw() 方法,使用 Canvas.drawBitmap 来绘制位图。

使用后台线程,您可以预取图像并创建一个小的 FIFO 位图队列,然后使用计时器机制触发队列中下一个位图的下一次绘制。

于 2013-01-28T13:26:52.783 回答
0

根据源代码ImageView

/**
 * Sets a drawable as the content of this ImageView.
 * 
 * @param drawable The drawable to set
 */
public void setImageDrawable(Drawable drawable) {
    if (mDrawable != drawable) {
        mResource = 0;
        mUri = null;

        int oldWidth = mDrawableWidth;
        int oldHeight = mDrawableHeight;

        updateDrawable(drawable);

        if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
            requestLayout();
        }
        invalidate();
    }
}

/**
 * Sets a Bitmap as the content of this ImageView.
 * 
 * @param bm The bitmap to set
 */
@android.view.RemotableViewMethod
public void setImageBitmap(Bitmap bm) {
    // if this is used frequently, may handle bitmaps explicitly
    // to reduce the intermediate drawable object
    setImageDrawable(new BitmapDrawable(mContext.getResources(), bm));
}

很明显,对您提供的新位图的边界进行了检查,以决定是否请求布局。

        if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
            requestLayout();
        }

根据您的描述,我相信您的图像应该具有完全相同的大小,因为您几乎要创建电影效果。所以,检查它们的尺寸。如果它们已经具有相同的尺寸,您应该检查是否真的the entire View Hierachy is re-calculate

旁注:

  • 您可能应该检查图像的大小:大图像的解码也是 CPU 密集型的。
  • 我建议您在单独的线程上解码下一个图像,然后在 50 毫秒过去时设置它,或者等到它们过去。换句话说,不要等待您的处理程序,解码下一个图像并在解码完成时通知。
  • 您正在调用recycle位图!请检查文档:This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap.我不确定回收是否非常密集,但在这种情况下请遵循文档并且不要调用它。
于 2013-01-31T15:31:13.217 回答