ImageView.setImageBitmap 在使用 Handler.postDelayed 方法调用时不会改变视图

ImageView.setImageBitmap doesn't change view when called using Handler.postDelayed method

我正在尝试制作简单的幻灯片,这是我正在使用的代码:

public class Pictures extends AppCompatActivity
    implements NavigationView.OnNavigationItemSelectedListener {

private ImageView picture;
private Integer resourceImage;
private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
    public void run() {
        showNewPicture(true);
        handler.postDelayed(this, 1000);
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    // initialization here
    final Button slideshowStart = (Button) findViewById(R.id.slideshow_start);
    // I'm starting slideshow using this:
    slideshowStart.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            handler.removeCallbacks(runnable);
            handler.post(runnable);
        }
    });

    final Button slideshowStop = (Button) findViewById(R.id.slideshow_stop);
    // I'm stopping slideshow using this:
    slideshowStop.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            handler.removeCallbacks(runnable);
        }
    });

}

private void showNewPicture(Boolean next){
    // defining resource ID for the new picture, i. e.:
    resourceImage = R.drawable.some_picture;
    setScaledImage(picture, resourceImage);
    picture.setTag(newTag);
}

// all the code below is to scale images to the actual size of the view
private void setScaledImage(ImageView imageView, final int resId) {
    final ImageView iv = imageView;
    ViewTreeObserver viewTreeObserver = iv.getViewTreeObserver();
    viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        public boolean onPreDraw() {
            iv.getViewTreeObserver().removeOnPreDrawListener(this);
            int imageViewHeight = iv.getMeasuredHeight();
            int imageViewWidth = iv.getMeasuredWidth();
            iv.setImageBitmap(
                    decodeSampledBitmapFromResource(getResources(),
                            resId, imageViewWidth, imageViewHeight));
            return true;
        }
    });
}

private static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
                                                      int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds = true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

private static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {

    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        if(height > width){
            while ((halfHeight / inSampleSize) >= reqHeight) {
                inSampleSize *= 2;
            }
        }else{
            while ((halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize *= 2;
            }
        }
    }

    return inSampleSize;
}
}

当我通过单击某些按钮调用 showNewPicture(true) 时,它工作正常并更新了 ImageView 的源。但是当我使用提供的代码 运行 幻灯片时,它只会在我单击 "start" 和 "stop" 按钮时发生变化。 runnable 中的代码会执行,所以如果我有 10 张图片,开始幻灯片放映,等待大约 5 秒并单击 "stop",ImageView 将显示第 6 张图片。另外,如果我替换 runnable

中的行
handler.postDelayed(this, 1000);

handler.post(this);

它工作得很好,我的 ImageView 正在更新,但过程是恒定的,我想设置一些延迟,这就是我使用 "postDelayed" 的原因。 我也试过这样声明处理程序:

private Handler handler = new Handler(Looper.getMainLooper());

得到了同样的结果。 此外,当我将带有 Pictures.this 上下文的 Toast 添加到 runnable 时 - 它会出现,所以我也尝试像这样声明 Runnable:

private Runnable runnable = new Runnable() {
    public void run() {
        Pictures.this.showNewPicture(true);
        Pictures.this.handler.postDelayed(this, 1000);
    }
};

但它导致相同的结果。 我在这里错过了什么?

您正在删除 slideShowStart onClick 中的处理程序回调,

handler.removeCallbacks(runnable);

因此无法接收消息。删除该行,它应该可以工作。

仍然没有弄清楚是什么原因导致了问题,但找到了解决方法,我已经像这样更改了我的 Runnable(添加了 picture.invalidate()):

private Runnable runnable = new Runnable() {
    public void run() {
        showNewPicture(true);
        picture.invalidate();
        handler.postDelayed(this, 5000);
    }
};

现在 ImageView 中的图像在每次 Runnable 执行时都会正确更新,即使我使用的是 postDelayed.