oort
oort

Reputation: 51

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

I'm trying to do simple slideshow, there is the code I'm using:

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;
}
}

When I'm calling showNewPicture(true) with some button click, it works fine and updates source of ImageView. But when I'm running the slideshow with the code provided, it only changes when I click "start" and "stop" button. The code inside the runnable executes, so if I'll have 10 images, start the slideshow, wait for about 5 seconds and click "stop", the ImageView will display the 6th image. Also if I replace the line insite the runnable

handler.postDelayed(this, 1000);

with

handler.post(this);

it works just fine, my ImageView is updating, but the process is constant, and I want to set some delay, that's why I'm using "postDelayed". I've also tried to declare Handler like this:

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

and got the same result. Also, when I'm adding Toast with Pictures.this context to runnable - it shows up, so I've also tried Declaring Runnable like this:

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

but it leads to the same result. What am I missing here?

Upvotes: 0

Views: 566

Answers (2)

oort
oort

Reputation: 51

Still hasn't figured out what causes the problem, but found a workaround, I've chaned my Runnable like this (added picture.invalidate()):

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

Now the image within the ImageView is updating correctly on each Runnable execution, even if I'm using postDelayed.

Upvotes: 0

barq
barq

Reputation: 3711

You are removing the handler callbacks in the onClick of the slideShowStart,

handler.removeCallbacks(runnable);

so there is no way the message will be received. Remove that line and it should work.

Upvotes: 0

Related Questions