To elegant! Android in the big picture and long loads such pictures

We are always doing development time will inevitably encounter case load images when image is smaller than the size of the ImageView, we of course can be very happy to go directly loaded show.

But if we want to load the picture is much larger than the size of the ImageView directly with ImageView to show, it would bring bad visual effects, will take up too much memory and performance overhead.

Even this picture is large enough to cause the program to crash oom. This time we need for special handling of the picture:
To elegant!  Android in the big picture and long loads such pictures

# A picture compression

Image is too big, I wanted a way to compress it smaller pictures. Old iron, this idea is not entirely wrong.

BitmapFactory This class provides a plurality of analytical methods (decodeResource, decodeStream, decodeFile etc.) used to create the Bitmap.

We can choose analytical methods depending on the source of the picture.

  • For example, if the picture from the network, you can use decodeStream method;
  • If sd card inside the picture, you can choose decodeFile method;
  • If the resource file inside the picture, you can use decodeResource methods.
    These methods will allocate memory for the Bitmap created, if the image is too large, then it will lead to oom.

BitmapFactory these methods provide an optional parameter BitmapFactory.Options, to help us resolve the picture. This parameter has a property inSampleSize, this property can help us to compress the image.

In order to explain the effect of inSampleSize, we can give chestnuts.
For example, we have a 2048 1536 Pictures, set inSampleSize is 4, you can put this picture compression 512 384, the length of each reduced by 4 times, the share memory is reduced 16-fold.
This is a clear, inSampleSize's role is to reduce the length of the picture inSampleSize times, reduce the share of memory inSampleSize squared.
Value for official documents inSampleSize also made some of the requirements that must be greater than the value of inSampleSize equal to 1 if the given value is less than 1, it defaults to 1.
And the value inSampleSize needs to be a multiple of 2, and if not, it will automatically become the nearest multiples of 2 from this value down, such as a given value is 3, then the value will be the final inSampleSize 2.

Of course, this inSampleSize values ​​we can not just give the best so that we can get the original size of the photo, and then compressed as needed. Do not worry, Google will help us to think it over! BitmapFactory.Options have a property inJustDecodeBounds, this property is the time when true, indicating that our current border just to get the size of the current picture, then return BitmapFactory resolution pictures method is null, which is a very lightweight Methods. So that we can be very happy to get the picture size, the code is as follows:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // 当前只为获取图片的边界大小
BitmapFactory.decodeResource(getResources(), R.drawable.bigpic, options);
int outHeight = options.outHeight;
int outWidth = options.outWidth;
String outMimeType = options.outMimeType;

Get the size of the picture, we can calculate the required size of the required compression:

private int caculateSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        int sampleSize = 1;
        int picWidth = options.outWidth;
        int picHeight = options.outHeight;
        if (picWidth > reqWidth || picHeight > reqHeight) {
            int halfPicWidth = picWidth / 2;
            int halfPicHeight = picHeight / 2;
            while (halfPicWidth / sampleSize > reqWidth || halfPicHeight / sampleSize > reqHeight) {
                sampleSize *= 2;
            }
        }
        return sampleSize;
}

Here is the complete code:

        mIvBigPic = findViewById(R.id.iv_big_pic);
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true; // 当前只为获取图片的边界大小
        BitmapFactory.decodeResource(getResources(), R.drawable.bigpic, options);
        int outHeight = options.outHeight;
        int outWidth = options.outWidth;
        String outMimeType = options.outMimeType;
        System.out.println("outHeight = " + outHeight + " outWidth = " + outWidth + " outMimeType = " + outMimeType);
        options.inJustDecodeBounds = false;
        options.inSampleSize = caculateSampleSize(options, getScreenWidth(), getScreenHeight());
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bigpic, options);
        mIvBigPic.setImageBitmap(bitmap);

Such images into a here almost over.

# Second, the local show

Sometimes we can get good results by compression, but sometimes the effect is not so good, such as long image painting, drawing such as long, if we show direct compression, then this picture is completely blind , it is affecting experience. Then we can adopt a local show, then slide to show the way to see the picture.

Android inside BitmapRegionDecoder use locally show pictures, showing that a rectangular area. To accomplish this function you will need a way to set the picture, another way to set the display area.

Initialization
BitmapRegionDecoder provides a series of newInstance to initialize, support incoming file path, file descriptors and file stream InputStream, etc.

E.g:

mRegionDecoder = BitmapRegionDecoder.newInstance (inputStream, false) ;
above this approach solves the incoming picture, the next step is to set the display area.

Bitmap bitmap = mRegionDecoder.decodeRegion (mRect, sOptions );
Parameter First, a Rect, two parameters are BitmapFactory.Options, can be used to control inSampleSize, inPreferredConfig like. Here is a simple example, showing pictures of the front of a large portion of the screen:

try {
        BitmapRegionDecoder regionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
        BitmapFactory.Options options1 = new BitmapFactory.Options();
        options1.inPreferredConfig = Bitmap.Config.ARGB_8888;
        Bitmap bitmap = regionDecoder.decodeRegion(new Rect(0, 0, getScreenWidth(), getScreenHeight()), options1);
        mIvBigPic.setImageBitmap(bitmap);
    } catch (IOException e) {
        e.printStackTrace();
    }

Of course, this is just the simplest usage, for we want to show the picture entirely inoffensive! Keguan, Shaoanwuzao, the future is already clear! Now that we can achieve regional show, then can we customize a View, you can with our fingers slide show pictures of different areas. yes! of course. So we will continue it!

According to the above analysis, we are very aware of the custom of thinking:

Providing a set intersection images;
rewriting onTouchEvent, according to a user gesture moving, modifying the display area of the picture;
after each update area parameters, call invalidate, onDraw regionDecoder.decodeRegion inside to get the bitmap, to draw
ado, directly on the code:

public class BigImageView extends View {
    private static final String TAG = "BigImageView";

    private BitmapRegionDecoder mRegionDecoder;
    private int mImageWidth, mImageHeight;
    private Rect mRect = new Rect();
    private static BitmapFactory.Options sOptions = new BitmapFactory.Options();
    {
        sOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
    }

    public BigImageView(Context context) {
        this(context, null);
    }

    public BigImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BigImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    public void setInputStream(InputStream inputStream) {
        try {
            mRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = false;
            BitmapFactory.decodeStream(inputStream, null, options);
            mImageHeight = options.outHeight;
            mImageWidth = options.outWidth;

            requestLayout();
            invalidate();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    int downX = 0;
    int downY = 0;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = (int) event.getX();
                downY = (int) event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                int curX = (int) event.getX();
                int curY = (int) event.getY();

                int moveX = curX - downX;
                int moveY = curY - downY;

                onMove(moveX, moveY);

                System.out.println(TAG + " moveX = " + moveX + " curX = " + curX + " downX = " + downX);

                downX = curX;
                downY = curY;
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return true;
    }

    private void onMove(int moveX, int moveY) {
        if (mImageWidth > getWidth()) {
            mRect.offset(-moveX, 0);
            checkWidth();
            invalidate();
        }

        if (mImageHeight > getHeight()) {
            mRect.offset(0, -moveY);
            checkHeight();
            invalidate();
        }

    }

    private void checkWidth() {
        Rect rect = mRect;
        if (rect.right > mImageWidth) {
            rect.right = mImageWidth;
            rect.left = mImageWidth - getWidth();
        }

        if (rect.left < 0) {
            rect.left = 0;
            rect.right = getWidth();
        }
    }

    private void checkHeight() {
        Rect rect = mRect;
        if (rect.bottom > mImageHeight) {
            rect.bottom = mImageHeight;
            rect.top = mImageHeight - getHeight();
        }

        if (rect.top < 0) {
            rect.top = 0;
            rect.bottom = getWidth();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int width = getMeasuredWidth();
        int height = getMeasuredHeight();

        mRect.left = 0;
        mRect.top = 0;
        mRect.right = width;
        mRect.bottom = height;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Bitmap bitmap = mRegionDecoder.decodeRegion(mRect, sOptions);
        canvas.drawBitmap(bitmap, 0, 0, null);
    }
}

According to the above source code:

Initial BitmapRegionDecoder inside setInputStream method, obtaining actual image width and height;

Rect onMeasure method which assign initial values ​​to control the display of the start image area;

onTouchEvent monitor user gesture Rect modify the parameters to modify the image display area, and boundary detection, and finally the invalidate;
Get The Bitmap Rect and draws in onDraw inside.

# Finally,
learning is not a simple matter, Ali p7 architects to share our learning path

To elegant!  Android in the big picture and long loads such pictures

As an Android programmer, there are many things to learn.

Released their own Android tidied learning content to help you learn advanced upgrade

  • Face questions Collection
  • Entry-level books PDF: Java, c, c ++
  • Featured Android Advanced PDF books
  • Ali specification document
  • Android development skills
  • Advanced PDF Daquan
  • Senior Advanced Video
  • Source
  • Video learning algorithm
  • To be continued

There are now learning trend flutter, kotin and other information , have been collated, save time searching to learn

If you have a need, you can point like + comment , concern me , then private letter I

To elegant!  Android in the big picture and long loads such pictures

To elegant!  Android in the big picture and long loads such pictures

To elegant!  Android in the big picture and long loads such pictures

Guess you like

Origin blog.51cto.com/14606040/2462580