Scaling and Displaying Bitmaps

With that, you are successfully taking pictures. Your image will be saved to a file on the filesystem for you to use.

Your next step will be to take this file, load it up, and show it to the user. To do this, you need to load it into a reasonably sized Bitmap object. To get a Bitmap from a file, all you need to do is use the BitmapFactory class:

    Bitmap bitmap = BitmapFactory.decodeFile(mPhotoFile.getPath());

There has to be a catch, though, right? Otherwise we would have put that in bold, you would have typed it in, and you would be done.

Here is the catch: When we say reasonably sized, we mean it. A Bitmap is a simple object that stores literal pixel data. That means that even if the original file was compressed, there is no compression in the Bitmap itself. So a 16-megapixel, 24-bit camera image – which might only be a 5 MB JPG – would blow up to 48 MB loaded into a Bitmap object (!).

You can get around this, but it does mean that you will need to scale the bitmap down by hand. You will first scan the file to see how big it is, next figure out how much you need to scale it by to fit it in a given area, and finally reread the file to create a scaled-down Bitmap object.

Create a new class called PictureUtils.java for your new method and add a static method to it called getScaledBitmap(String, int, int).

Listing 16.9  Creating getScaledBitmap(…) (PictureUtils.java)

public class PictureUtils {
    public static Bitmap getScaledBitmap(String path, int destWidth, int destHeight) {
        // Read in the dimensions of the image on disk
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        float srcWidth = options.outWidth;
        float srcHeight = options.outHeight;

        // Figure out how much to scale down by
        int inSampleSize = 1;
        if (srcHeight > destHeight || srcWidth > destWidth) {
            float heightScale = srcHeight / destHeight;
            float widthScale = srcWidth / destWidth;

            inSampleSize = Math.round(heightScale > widthScale ? heightScale :
                    widthScale);
        }

        options = new BitmapFactory.Options();
        options.inSampleSize = inSampleSize;

        // Read in and create final bitmap
        return BitmapFactory.decodeFile(path, options);
    }
}

The key parameter above is inSampleSize. This determines how big each sample should be for each pixel – a sample size of 1 has one final horizontal pixel for each horizontal pixel in the original file, and a sample size of 2 has one horizontal pixel for every two horizontal pixels in the original file. So when inSampleSize is 2, the image has a quarter of the number of pixels of the original.

One more bit of bad news: When your fragment initially starts up, you will not know how big PhotoView is. Until a layout pass happens, views do not have dimensions onscreen. The first layout pass happens after onCreate(…), onStart(), and onResume() initially run, which is why PhotoView does not know how big it is.

There are two solutions to this problem: Either you wait until a layout pass happens, or you use a conservative estimate. The conservative estimate approach is less efficient, but more straightforward. Write another static method called getScaledBitmap(String, Activity) to scale a Bitmap for a particular Activity’s size.

Listing 16.10  Writing conservative scale method (PictureUtils.java)

public class PictureUtils {
    public static Bitmap getScaledBitmap(String path, Activity activity) {
        Point size = new Point();
        activity.getWindowManager().getDefaultDisplay()
                .getSize(size);

        return getScaledBitmap(path, size.x, size.y);
    }

This method checks to see how big the screen is and then scales the image down to that size. The ImageView you load into will always be smaller than this size, so this is a very conservative estimate.

Next, to load this Bitmap into your ImageView, add a method to CrimeFragment to update mPhotoView.

Listing 16.11  Updating mPhotoView (CrimeFragment.java)

    private String getCrimeReport() {
        ...
    }

    private void updatePhotoView() {
        if (mPhotoFile == null || !mPhotoFile.exists()) {
            mPhotoView.setImageDrawable(null);
        } else {
            Bitmap bitmap = PictureUtils.getScaledBitmap(
                    mPhotoFile.getPath(), getActivity());
            mPhotoView.setImageBitmap(bitmap);
        }
    }
}

Then call that method from inside onCreateView(…) and onActivityResult(…).

Listing 16.12  Calling updatePhotoView() (CrimeFragment.java)

    mPhotoButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ...
            startActivityForResult(captureImage, REQUEST_PHOTO);
        }
    });

    mPhotoView = (ImageView) v.findViewById(R.id.crime_photo);
    updatePhotoView();

    return v;
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode != Activity.RESULT_OK) {
        return;
    }

    if (requestCode == REQUEST_DATE) {
        ...
    } else if (requestCode == REQUEST_CONTACT && data != null) {
        ...
    } else if (requestCode == REQUEST_PHOTO) {
        Uri uri = FileProvider.getUriForFile(getActivity(),
                "com.bignerdranch.android.criminalintent.fileprovider",
                mPhotoFile);

        getActivity().revokeUriPermission(uri,
                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

        updatePhotoView();
    }
}

Now that the camera is done writing to your file, you can revoke the permission, closing off access to your file again. Run CriminalIntent again, and you should see your image displayed in the thumbnail view.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.236.81.4