【问题标题】:How to get when an ImageView is completely loaded in Android如何在 Android 中完全加载 ImageView
【发布时间】:2013-07-10 11:28:42
【问题描述】:

我正在开发一个在一堆图像上画线的应用程序。为了选择这些图像,我有一个单选组,每当用户单击单选按钮时,图像都会加载所有自己的绘图。

在我的收音机监听器中,我有以下代码:

bitmap = BitmapUtils.decodeSampledBitmapFromResource(root + DefinesAndroid.CAMINHO_SHOPPINGS_SDCARD + nomeImagemAtual, size.x, size.y);
mImage.setImageBitmap(bitmap);

mImage.setDrawLines(true);
mImage.setImageBitmap(loadBitmapFromView(mImage));

decodeSampledBitmapFromResource 方法我从这个关于 android 开发者的链接获得(它更有效地加载位图)http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

这是我用来获取视图位图的方法

    public static Bitmap loadBitmapFromView(View v) {
        Bitmap b = Bitmap.createBitmap( v.getLayoutParams().width, v.getLayoutParams().height, Bitmap.Config.ARGB_8888);                
        Canvas c = new Canvas(b);
        v.layout(0, 0, v.getLayoutParams().width, v.getLayoutParams().height);
        v.draw(c);
        return b;
}

我正在设置mImage 的图像位图,因为我使用的是 ImageViewTouch 库(它可以在 ImageView 上进行缩放),如果我不这样做,所有的画布绘图都会被删除图像(如放大/缩小)。

错误日志如下

07-11 21:13:41.567: E/AndroidRuntime(20056): java.lang.IllegalArgumentException: width and height must be > 0
07-11 21:13:41.567: E/AndroidRuntime(20056):    at android.graphics.Bitmap.createBitmap(Bitmap.java:638)
07-11 21:13:41.567: E/AndroidRuntime(20056):    at android.graphics.Bitmap.createBitmap(Bitmap.java:620)

我几乎可以肯定这个错误正在发生,因为当我调用getBitmapFromView 方法时,图像位图没有完全加载。

我如何知道视图何时完全加载?

【问题讨论】:

  • 嗯,我不明白,你会在你的代码中再次为我解释一下吗?你第一次设置了ImageBitmap。为什么要第二次设置它并从视图中加载和高度?
  • 第一次是加载图片干净(没有任何图纸)。然后我通过调用 setDrawLines 在图像上绘制线条。一旦在 Canvas 上绘制图纸,我必须创建一个独特的图像 + 图纸图像。所以我第二次调用 imageBitmap,但这次,我必须从视图中获取位图(因为视图有图纸)..这样我把它们放在一起
  • 看来这个stackoverflow.com/questions/8831642/…会是你想要的更多。
  • 这个问题询问关于在图像上绘制线条的问题。我可以正常绘制它们,问题是更改位图并在之后绘制线条.. 位图似乎没有及时加载
  • 看来你已经覆盖了 ImageView 对吧?如何在 onDraw 添加触发以知道何时绘制。好问题,我也很想知道答案

标签: android bitmap imageview android-canvas


【解决方案1】:

实际上还有另一种更可靠的方法可以做到这一点,即使用ViewTreeObserver.OnPreDrawListener。这是一个例子:

mImage.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
    @Override
    public boolean onPreDraw() {
        try {
            loadBitmapFromView(mImage);            
             // Note that returning "true" is important,
             // since you don't want the drawing pass to be canceled
            return true;    
        } finally {
            // Remove listener as further notifications are not needed
            mImage.getViewTreeObserver().removeOnPreDrawListener(this);
        }
    }
});

使用OnPreDrawListener 保证 View 被测量和布局,而View#post(Runnable) 只是在所有视图已经最有可能测量和布局时执行你的Runnable布局。

【讨论】:

  • +1 用于使用 preDraw,提到返回 true 很重要,并在一次不错的尝试中清理侦听器......最终阻止!
【解决方案2】:

下面是一个有效的 NotifyImageView 类,它结合了上面 Dmitry 的方法 并添加代码,仅在 ImageView 真正可用后才通知。

希望对你有用。

`

public interface NotifyImageHolder {
    public void notifyImageChanged(final NotifyImageView thePosterImage, final int width, final int height);
}

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.ViewTreeObserver;
import android.widget.ImageView;

public class NotifyImageView extends ImageView {
    private boolean mImageChanged;
    private NotifyImageHolder mHolder;
    private boolean mImageFinished;

    public NotifyImageView(Context context) {
        super(context);
        init();
    }

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

    public NotifyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    protected void init() {
        mImageChanged = false;
        mImageFinished = false;
        mHolder = null;
        monitorPreDraw();
    }

    // so we can tell when the image finishes loading..
    protected void monitorPreDraw() {
        final NotifyImageView thePosterImage = this;
        getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {

            @Override
            public boolean onPreDraw() {
                try {
                    return true; //note, that "true" is important, since you don't want drawing pass to be canceled
                } finally {
                    getViewTreeObserver().removeOnPreDrawListener(this); // we don't need any further notifications
                    thePosterImage.buildDrawingCache();
                    mImageFinished = true;
                }
            }
        });
    }

    public void setNotifyImageHolder(NotifyImageHolder holder) {
        this.mHolder = holder;
    }

    public boolean isImageChanged() {
        return mImageChanged;
    }

    public boolean isImageFinished() {
        return mImageFinished;
    }

    public void notifyOff() {
        mHolder = null;
    }

    // the change notify happens here..
    @Override
    public void setImageDrawable(Drawable noPosterImage) {
        super.setImageDrawable(noPosterImage);
        if (mHolder != null && mImageFinished) {
            mImageFinished = false; // we send a single change-notification only
            final NotifyImageView theNotifyImageView = this;

            theNotifyImageView.post(new Runnable() {
                @Override
                public void run() {
                    if (mHolder != null) {
                        int width = getMeasuredWidth();
                        int height = getMeasuredHeight();
                        mImageChanged = true;
                        mHolder.notifyImageChanged(theNotifyImageView, width, height);
                    }
                }
            });

        }
    }

}

`

【讨论】:

  • 这里实际上不需要使用视图树观察器。由于您可以完全访问视图实现,因此您可以在 onDraw() 方法中获取您的大小。
【解决方案3】:

以这样的方式致电loadBitmapFromView

mImage.post(new Runnable() {
    @Override
    public void run() {
        loadBitmapFromView(mImage);
    }
});

Runnable 提供给post() 方法将在视图测量和布局后执行,因此getWidth()getHeight() 将返回实际的宽度和高度。

您还能做什么,通过调用measure 手动测量View,然后从getMeasuredWidth()getMeasuredHeight() 获取结果。但我不推荐这种方式。

【讨论】:

  • 终于成功了!!!我只需在 run() 方法中设置图像位图,一切都很好!我非常感激!如果可以的话,我会在你的回答中投票 10 次! :)
  • 同意 Felipe... 我花了很长时间寻找类似的方法。谢谢!
  • @RiddlerDev 检查另一个解决方案(由我发布)OnPreDrawListener
  • 这是一种救赎! Glide 监听器在 recyclerview 中效果不佳
猜你喜欢
  • 2011-08-01
  • 2014-01-29
  • 2020-07-09
  • 1970-01-01
  • 2023-02-17
  • 2011-01-29
  • 1970-01-01
  • 2016-06-18
相关资源
最近更新 更多