Как получить, когда ImageView полностью загружен в Android
Я разрабатываю приложение, которое рисует линии над связкой изображений. Чтобы выбрать эти изображения, у меня есть группа радиостанций, и всякий раз, когда пользователь нажимает на переключатель, изображение загружается со всеми его чертежами.
В моем радиослушателе есть следующий код:
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;
}
Я устанавливаю изображение Bitmap из 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
.
Как я могу узнать, когда изображение полностью загружено?
Ответы
Ответ 1
Вызвать loadBitmapFromView
таким образом:
mImage.post(new Runnable() {
@Override
public void run() {
loadBitmapFromView(mImage);
}
});
Runnable
, предоставляемый методу post()
, будет выполнен после измерения и компоновки просмотра, поэтому getWidth()
и getHeight()
вернут фактическую ширину и высоту.
Что еще вы можете сделать, измеряет View
вручную, вызывая measure
, а затем получая результат от getMeasuredWidth()
и getMeasuredHeight()
. Но я не рекомендую этот путь.
Ответ 2
На самом деле существует другой, более надежный способ сделать это, используя ViewTreeObserver.OnPreDrawListener
. И вот пример:
mImage.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
@Override
public boolean onPreDraw() {
try {
loadBitmapFromView(mImage);
return true; //note, that "true" is important, since you don't want drawing pass to be canceled
} finally {
mImage.getViewTreeObserver().removeOnPreDrawListener(this); //we don't need any further notifications
}
}
});
Использование OnPreDrawListener
гарантирует, что View
было измерено и составлено, а View#post(Runnable)
просто выполняет ваш Runnable
, когда все представления уже наиболее вероятно измерены и layouted.
Ответ 3
Ниже приведен рабочий класс 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);
}
}
});
}
}
}
`