ScaleType, as an enumerated type, defines eight schemas in total
There are eight Scaletypes, which can be divided into three types:
-
The four that start with FIT_, what they have in common is that they all zoom in and out.
-
The three that begin with CENTER_ have in common the centerpoint of the image overlaps the centerpoint of the ImageView.
-
MATRIX, without changing the size of the original image, is drawn from the upper left corner of the ImageView, and the excess position is clipped. If the original image is smaller than ImageView, only the original size is drawn, leaving the rest blank.
In this paper, the analysis of the actual combat guess, according to the actual results guess a conclusion, finally look at the source code to verify the conclusion of the guess,So guess the conclusion is not necessarily correct, if you do not want to see the guess process, you can directly jump to the source analysis link, directly find the correct conclusion.
The four types beginning with FIT_
-
FIT_START(Keep the original image width to height ratio)
The original image is scaled up or down and displayed in the ImageView start (left/top), depending on the width and height of the image.
When the width of the image is greater than the height, the image width is enlarged/shrunk to the width of the ImageView, and the height is enlarged/shrunk to the same scale and displayed in the upper half of the ImageView.
When the image height is greater than the width, zoom in/out by height. The image height is zoomed in/out to the height of the ImageView, and the width is zoomed in/out by the same ratio to the left half of the ImageView.
-
FIT_END(keep the original image width to height ratio)
FIT_END is similar to FIT_START in that FIT_START is left/top and FIT_END is right/bottom.
-
FIT_CENTER(default mode, keep original image width to height ratio)
Zoom in and out of the original image in the center of the ImageView, depending on the width and height
When the image width is greater than the height, the image width is enlarged/shrunk to the width of the ImageView, and the height is enlarged/shrunk to the middle of the ImageView.
When the image height is greater than the width, zoom in/out by height. The image height is zoomed in/out to the height of the ImageView, and the width is zoomed in/out by the same scale to be displayed in the middle of the ImageView.
- FIT_XY(do not keep original image width to height ratio)
Zoom in/out of the image to the size of the ImageView.
summary
For FIT_START, FIT_END, and FIT_CENTER, which image width or height is closer to the corresponding width and height of the ImageView will be scaled to the size of the ImageView, and then the other one will be scaled to the previous scale, keeping the original width and height ratio of the image. And will show the complete picture, that is, all parts of the picture, will not cause zoom in or zoom out only part of the picture content; FIX_XT simply scales to the size of the ImageView, does not maintain the image’s original width to height ratio, but displays the entire image’s contents.
So the FIT_ method will display all the contents of the image.
PS: All the content of the image meaning, put everything on the picture displayed, some larger/smaller only larger/smaller part of such as zoom in the picture above the girl playing computer, and then zoom in to the ImageView, but we can’t see LeetCode this text, so all the content will not be able to display images.
CENTER_ Three types at the beginning (zoom center is the center of the image)
-
CENTER_CROP
Originally thought that the scale will also be related to the width and height, but see the following figure can not guess what is the specific relationship, can only be in the back of the source code analysis to see how to scale.
-
CENTER_INSIDE(keep original image width to height ratio)
If the size of the original image is larger than the ImageView size, scale down the width and height of the original image until the longer end is displayed in the ImageView and the center is displayed in the ImageView. If the size of the original image is smaller than the size of the ImageView, the image is not centered. As shown below:
-
CENTER
Keep the size of the original image in the center of the ImageView. When the size of the original image is greater than the size of the ImageView, the excess is truncated.
MATRIX
Without changing the size of the original image, draw from the upper left corner of the ImageView, and cut out any excess.
Source code analysis
Matrix (0), //Scale the image using {@link Matrix.ScaleToFit#FILL}.
FIT_XY (1), //Scale the image using {@link Matrix.ScaleToFit#START}.
FIT_START (2), //Scale the image using {@link Matrix.ScaleToFit#START}.
FIT_CENTER (3), //Scale the image using {@link Matrix.ScaleToFit#CENTER}.
FIT_END (4), //Scale the image using {@link Matrix.ScaleToFit#END}.
CENTER (5), //Center the image in the view, but perform no scaling.
CENTER_CROP (6), /**
* Scale the image uniformly (maintain the image's aspect ratio) so * that both dimensions (width and height) of the image will be equal * to or larger than the corresponding dimension of the view * (minus padding). The image is then centered in the view. */ CENTER_INSIDE (7) /** * Scale the image uniformly (maintain the image's aspect ratio) so
* that both dimensions (width and height) of the image will be equal
* to or less than the corresponding dimension of the view
* (minus padding). The image is then centered in the view.
*/
Copy the code
After setting the ScaleType, the implementation details are in the ImageView configureBounds() method (Android-29)
private void configureBounds() {
if(mDrawable == null || ! mHaveFrame) {return;
}
final int dwidth = mDrawableWidth; //图片宽度
final int dheight = mDrawableHeight;//图片高度
final int vwidth = getWidth() - mPaddingLeft - mPaddingRight; //ImageView宽度
final int vheight = getHeight() - mPaddingTop - mPaddingBottom;//ImageView高度
final boolean fits = (dwidth < 0 || vwidth == dwidth)
&& (dheight < 0 || vheight == dheight);
if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) { //FIX_XY
/* If the drawable has no intrinsic size, or we're told to scaletofit, then we just fill our entire view. */ mDrawable.setBounds(0, 0, vwidth, vheight); // matrix = null; } else { // We need to do the scaling ourself, so have the drawable // use its native size. mDrawable.setBounds(0, 0, dwidth, dheight); if (ScaleType.MATRIX == mScaleType) { // Use the specified matrix as-is. if (mMatrix.isIdentity()) { mDrawMatrix = null; } else { mDrawMatrix = mMatrix; }} else if (fits) {// The bitmap fits exactly, no transform needed. MDrawMatrix = null; } else if (ScaleType.CENTER == mScaleType) { // Center bitmap in view, no scaling. mDrawMatrix = mMatrix; Mdrawmatrix. setTranslate(math. round((vwidth-dwidth) * 0.f), math. round((vheight-dheight) * 0.f)); mdrawmatrix. setTranslate(math. round((vwidth-dwidth) * 0.f)); } else if (ScaleType.CENTER_CROP == mScaleType) { mDrawMatrix = mMatrix; float scale; float dx = 0, dy = 0; if (dwidth * vheight > vwidth * dheight) { scale = (float) vheight / (float) dheight; Dx = (vwidth - dwidth * scale) * 0.5f; } else { scale = (float) vwidth / (float) dwidth; Dy = (vheight - dheight * scale) * 0.5f; } mDrawMatrix.setScale(scale, scale); mDrawMatrix.postTranslate(Math.round(dx), Math.round(dy)); } else if (ScaleType.CENTER_INSIDE == mScaleType) { mDrawMatrix = mMatrix; float scale; float dx; float dy; If (dwidth <= vwidth && dheight <= vheight) {scale = 1.0f; } else { scale = Math.min((float) vwidth / (float) dwidth, (float) vheight / (float) dheight); } dx = math. round((vwidth - dwidth * scale) * 0.5f); Dy = Math. Round ((vheight-dheight * scale) * 0.5f); mDrawMatrix.setScale(scale, scale); mDrawMatrix.postTranslate(dx, dy); } else { //FIT_START,FIT_END,FIT_CENTER // Generate the required transform. mTempSrc.set(0, 0, dwidth, dheight); mTempDst.set(0, 0, vwidth, vheight); mDrawMatrix = mMatrix; mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType)); }}}Copy the code
- CENTER
// Center bitmap inview, no scaling. mDrawMatrix = mMatrix; Mdrawmatrix. setTranslate(math. round((vwidth-dwidth) * 0.f), math. round((vheight-dheight) * 0.f)); mdrawmatrix. setTranslate(math. round((vwidth-dwidth) * 0.f));Copy the code
MDrawMatrix matrix translation is just moving the image to the middle of the ImageView, showing the middle region of the image. I’ll just shift it.
- CENTER_CROP
mDrawMatrix = mMatrix;
float scale;
float dx = 0, dy = 0;
if (dwidth * vheight > vwidth * dheight) {
scale = (float) vheight / (float) dheight;
dx = (vwidth - dwidth * scale) * 0.5f;
} else {
scale = (float) vwidth / (float) dwidth;
dy = (vheight - dheight * scale) * 0.5f;
}
mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));
Copy the code
Dwidth /dheight>vwidth/vheight is equivalent to dwidth * vheight >vwidth * dheight, In other words, the ratio of ImageView to image height is less than the ratio of ImageView to image width. In this case, vheight/deight is used to scale the image to ensure that the image width is greater than or equal to the width of the ImageView. Because (vheight/dheight)* dwidth>vwidth. Comments are also explained. First scale, then translate. So this mode has to do with the aspect ratio of the image and the aspect ratio of the ImageView.
- CENTER_INSIDE
mDrawMatrix = mMatrix;
float dx;
float dy;
if(dwidth <= vwidth && dheight <= vheight) {scale = 1.0f; }else {
scale = Math.min((float) vwidth / (float) dwidth,
(float) vheight / (float) dheight); } dx = math. round((vwidth - dwidth * scale) * 0.5f); Dy = Math. Round ((vheight-dheight * scale) * 0.5f); mDrawMatrix.setScale(scale, scale); mDrawMatrix.postTranslate(dx, dy);Copy the code
From scale = math.min ((float) vwidth/(float) dwidth, (float) vheight/(float) dheight); You can see that this mode is related to (ImageView to image height ratio) and (ImageView to image width ratio), whichever small scale is equal to it. And then finally, zoom and pan.
- When ImageView is set to four different ScaleTypes, ImageView adopts a Matrit.ScaleToFit to display images.
} else { //FIT_START,FIT_END,FIT_CENTER
// Generate the required transform.
mTempSrc.set(0, 0, dwidth, dheight);
mTempDst.set(0, 0, vwidth, vheight);
mDrawMatrix = mMatrix;
mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
@UnsupportedAppUsage
private static Matrix.ScaleToFit scaleTypeToScaleToFit(ScaleType st) {
// ScaleToFit enum to their corresponding Matrix.ScaleToFit values
return sS2FArray[st.nativeInt - 1];
}
private static final Matrix.ScaleToFit[] sS2FArray = {
Matrix.ScaleToFit.FILL,
Matrix.ScaleToFit.START,
Matrix.ScaleToFit.CENTER,
Matrix.ScaleToFit.END
};
/**
* Controlls how the src rect should align into the dst rect for setRectToRect().
*/
public enum ScaleToFit {
/**
* Scale in X and Y independently, so that src matches dst exactly. This may change the
* aspect ratio of the src.
*/
FILL(0),
/**
* Compute a scale that will maintain the original src aspect ratio, but will also ensure
* that src fits entirely inside dst. At least one axis (X or Y) will fit exactly. START
* aligns the result to the left and top edges of dst.
*/
START(1),
/**
* Compute a scale that will maintain the original src aspect ratio, but will also ensure
* that src fits entirely inside dst. At least one axis (X or Y) will fit exactly. The
* result is centered inside dst.
*/
CENTER(2),
/**
* Compute a scale that will maintain the original src aspect ratio, but will also ensure
* that src fits entirely inside dst. At least one axis (X or Y) will fit exactly. END
* aligns the result to the right and bottom edges of dst.
*/
END(3);
// the native values must match those inSkmatrix.h ScaleToFit(int nativeInt) {this.nativeInt = nativeInt; } final int nativeInt; }Copy the code
FILL: equivalent to scaletype. FIX_XY, the height and width of the image are scaled independently. The width of the image is scaled vwidth/dwidth and the height is scaled vheight/dheight.
The following is the conclusion of the three types of FIT_START,FIT_CENTER and FIT_END in this online article, but the source code analysis is not given. I have tested several use cases according to its conclusion and found it is not correct. So I’m going to analyze it from the source point of view.
In addition, this article is still very good to write, the previous several models combined with source code graphic analysis is very good.
Wrong conclusion:
FIT_START,FIT_CENTER, and FIT_END use the same scaling strategy. If dheight> dwidth, vwidth/dwidth is used as the scaling ratio, and vice versa.
www.jianshu.com/p/fe5d2e3fe…
Correct analysis:
FIT_START,FIT_CENTER,FIT_END all call setRectToRect().
public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) {
if (dst == null || src == null) {
throw new NullPointerException();
}
returnnSetRectToRect(native_instance, src, dst, stf.nativeInt); } @fastNative private static native Boolean nSetRectToRect(long nObject, RectF SRC, RectF dst, int stf);Copy the code
Finally we find the actual implementation in the framework layer
/**
33 * Delegate implementing the native methods of android.graphics.Matrix
34 *
35 * Through the layoutlib_create tool, the original native methods of Matrix have been replaced
36 * by calls to methods of the same name in this delegate class.
37 *
38 * This class behaves like the original native implementation, but inJava, keeping previously 39 * native data into its own objects and mapping them to int that are sent back and forth between 40 * it and the original Matrix class. 41 * 42 * @see DelegateManager 43 * 44 */ public final class Matrix_Delegate { ... . . 532 @LayoutlibDelegate 533 /*package*/ static boolean nSetRectToRect(long native_object, RectF src, 534 RectF dst, int stf) { 535 Matrix_Delegate d = sManager.getDelegate(native_object); 536if (d == null) {
537 return false;
538 }
539
540 if (src.isEmpty()) {
541 reset(d.mValues);
542 return false;
543 }
544
545 if(dst.isEmpty()) { 546 d.mValues[0] = d.mValues[1] = d.mValues[2] = d.mValues[3] = d.mValues[4] = d.mValues[5] 547 = d.mValues[6] = d.mValues[7] = 0; 548 d.mValues[8] = 1; 549}else{550float tx, sx = dst.width() / src.width();
551 float ty, sy = dst.height() / src.height();
552 boolean xLarger = false; 553, 554,if(stf ! = ScaleToFit.FILL.nativeInt) { //FIT_START,FITA_END,FIT_CENTER 555if (sx > sy) {
556 xLarger = true; 557 sx = sy; 558}else {
559 sy = sx;
560 }
561 }
562
563 tx = dst.left - src.left * sx;
564 ty = dst.top - src.top * sy;
565 if (stf == ScaleToFit.CENTER.nativeInt || stf == ScaleToFit.END.nativeInt) {
566 floatdiff; 567, 568,if(xLarger) { 569 diff = dst.width() - src.width() * sy; 570}else {
571 diff = dst.height() - src.height() * sy;
572 }
573
574 if (stf == ScaleToFit.CENTER.nativeInt) {
575 diff = diff / 2;
576 }
577
578 if(xLarger) { 579 tx += diff; 580}else {
581 ty += diff;
582 }
583 }
584
585 d.mValues[0] = sx;
586 d.mValues[4] = sy;
587 d.mValues[2] = tx;
588 d.mValues[5] = ty;
589 d.mValues[1] = d.mValues[3] = d.mValues[6] = d.mValues[7] = 0;
590
591 }
592 // shared cleanup
593 d.mValues[8] = 1;
594 return true; 595}}Copy the code
If you want to understand how matrices work and change, check out the following blog post
www.jianshu.com/p/5e30db034…
Juejin. Cn/post / 684490…
Matrix is essentially a Matrix as shown below:
Matrix provides the following operations:
-
Scale corresponds to MSCALE_X and MSCALE_Y
-
Translate corresponds to MTRANS_X and MTRANS_Y
-
Skew corresponds to MSKEW_X and MSKEW_Y
-
Rotate There is no special value for rotation. The Matrix handles rotation by calculating scaling and miscutting.
FIT_START analysis:
float tx, sx = dst.width() / src.width();
551 float ty, sy = dst.height() / src.height();
552 boolean xLarger = false;
if(stf ! = ScaleToFit. The FILL. NativeInt) {/ / FIT_START FITA_END, FIT_CENTER, so this firm / / FIT_START is 555 on the interstateif (sx > sy) {
556 xLarger = true; 557 sx = sy; 558}else {
559 sy = sx;
560 }
561 }
562
563 tx = dst.left - src.left * sx;
564 ty = dst.top - src.top * sy;
if(STF = = ScaleToFit. CENTER. NativeInt | | STF = = ScaleToFit. END. NativeInt) {/ / FITA_END FIT_CENTER... d.m Values [0] = sx; 586 d.mValues[4] = sy; 587 d.mValues[2] = tx; 588 d.mValues[5] = ty; 589 d.mValues[1] = d.mValues[3] = d.mValues[6] = d.mValues[7] = 0;Copy the code
If (image/ImageView width ratio) is greater than (image/ImageView height ratio), the zoom ratio is equal to the image/ImageView width ratio, and vice versa. The X-axis translation distance is dst.left-src.left * sx. The Y-axis translation distance is dst.top-src.top * sy, because positions 0,4 of the matrix are scaled and positions 2,5 are translated.
FIT_END, FIT_CENTER analysis: As with FIT_START, the scaling ratio is related to the image and Imageview width ratio (height ratio), but the displacement has been adjusted a bit, so I won’t go into details here.
conclusion
Our guess conclusion is wrong, because our test example can not cover all scenarios, so the conclusion is biased, only applicable to a certain range, so, look at the source, look at the source!
The FIT_START, FIT_END, and FIT_CENTER scaling ratios are all related to the image’s width ratio (height ratio) to the ImageView, and the image retains its original scale as it moves.
FIT_XY, which scales directly to the width and height of the ImageView, destroys the original image scale.
CENTER, which just moves the image to the middle of the ImageView, shows the middle region of the image. Direct translation, no scaling, redundant for clipping.
CENTER_CROP, scale first, then pan. This mode is related to the image aspect ratio and the ImageView aspect ratio, not the image and ImageView width ratio (height ratio). Note the difference.
CENTER_INSIDE, which is related to (ImageView to image height ratio) and (ImageView to image width ratio), whichever small scale is equal to it. And then finally, zoom and pan.
MATRIX, without changing the size of the original image, is drawn from the upper left corner of the ImageView, and the excess is clipped.
Extension: Customize a picture zooming mode
We can design a mode that displays the image in the middle of the bottom. If the image is larger than the ImageView size, we can crop out the excess and keep the bottom.
No matter how high or wide the ImageView is, the image will always be displayed in the bottom center, with any excess cropped out.
The code is as follows:
// set the initial Matrix value Matrix Matrix = new Matrix();floatmatrixValues[] = { 1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f }; matrix.setValues(matrixValues); ImageView vWidth = img_test3.getwidth (); vHeight = img_test3.getHeight(); DWidth = img_test3.getDrawable().getintrinsicWidth (); dWidth = img_test3.getDrawable(). dHeight = img_test3.getDrawable().getIntrinsicHeight(); // BitmapFactory.Options options = new BitmapFactory.Options(); // BitmapFactory.decodeResource(getResources(),R.drawable.ic_test_3,options); // // to get the width and height of the picture, this way not, pay attention! // dHeight = options.outHeight; // dWidth = options.outWidth; // Pan the imagefloatMath.round((vWidth -dwidth) * 1 * 0.5);float ty3 = Math.round((vHeight - dHeight) * 1);
matrix.setTranslate(tx3,ty3);
img_test3.setImageMatrix(matrix);
Copy the code
One important thing to note is that ImageView needs to set scaleType to matrix for this to work. The source code is as follows:
if (ScaleType.MATRIX == mScaleType) {
// Use the specified matrix as-is.
if (mMatrix.isIdentity()) {
mDrawMatrix = null;
} else {
mDrawMatrix = mMatrix;
}
Copy the code
Refer to the article
Android Matrix a: www.jianshu.com/p/5e30db034…
ImageView ScaleType: juejin.cn/post/684490…
ImageView ScaleType principle and effect analysis: www.jianshu.com/p/fe5d2e3fe…