一、Android:scaleType
android的scaleType属于View的视图层,在onDraw时通过给canvas添加不同的Maxtrix来达到不同的展现效果。
下面结合源码分析的每一种类型的含义:
private void configureBounds() {
if (mDrawable == null || !mHaveFrame) {
return;
}
int dwidth = mDrawableWidth;
int dheight = mDrawableHeight;
int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
int vheight = getHeight() - mPaddingTop - mPaddingBottom;
boolean fits = (dwidth < 0 || vwidth == dwidth) &&
(dheight < 0 || vheight == dheight);
if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
/* If the drawable has no intrinsic size, or we're told to
scaletofit, then we just fill our entire view.
*/
// 对于 fixXY模式,直接图片的宽度伸缩成View的宽度
mDrawable.setBounds(0, 0, vwidth, vheight);
mDrawMatrix = 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) {// 当图片的宽度与View的宽度一样时,优先处理
// The bitmap fits exactly, no transform needed.
mDrawMatrix = null;
} else if (ScaleType.CENTER == mScaleType) {
// 对于center模式,只是将图片平衡到View的中心位置
mDrawMatrix = mMatrix;
mDrawMatrix.setTranslate((int) ((vwidth - dwidth) * 0.5f + 0.5f),
(int) ((vheight - dheight) * 0.5f + 0.5f));
} else if (ScaleType.CENTER_CROP == mScaleType) {
mDrawMatrix = mMatrix;
// 对于centerCrop模式,其会将图片等比伸缩到一条边与View重合,另一条与View重合或超过
// 然后,平移至view的中心
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((int) (dx + 0.5f), (int) (dy + 0.5f));
} else if (ScaleType.CENTER_INSIDE == mScaleType) {
mDrawMatrix = mMatrix;
float scale;
float dx;
float dy;
// 对于centerInside模式,其是将图片的边界完全等比伸缩到View的里面,可能出现边界重合
// 然后平移到View的中心位置
if (dwidth <= vwidth && dheight <= vheight) {
scale = 1.0f;
} else {
scale = Math.min((float) vwidth / (float) dwidth,
(float) vheight / (float) dheight);
}
dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f);
dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f);
mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate(dx, dy);
} else {
// 下面是fitStart/fitCenter/fitEnd模式,它是将图片等比伸缩到View里面,类似于
//centerInside,不同的是其可以放置于开始、中间和结束三个不同的位置
mTempSrc.set(0, 0, dwidth, dheight);
mTempDst.set(0, 0, vwidth, vheight);
mDrawMatrix = mMatrix;
mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
}
}
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
.......
// 将先前调整过的Matrix添加到canvas里面,并与之前的Maxtric连接起来
if (mDrawMatrix != null) {
canvas.concat(mDrawMatrix);
}
mDrawable.draw(canvas);
canvas.restoreToCount(saveCount);
}
}
二、Android:adjustViewBounds
Android的adjustViewBounds属于View的属性层,在onMeasure时通过控制View的宽高比来影响View的大小。而宽度比是根据加载的图片的宽高算的。也就是说可以通过设置adjustViewBounds为true,来调整View的宽度比等于加载图片的宽度比。这往往会结合scaleType=centerCrop用于等比例显示图片。
调整View宽高比的源码如下:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
resolveUri();
int w;
int h;
float desiredAspect = 0.0f;
boolean resizeWidth = false;
boolean resizeHeight = false;
final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
if (mDrawable == null) {
// If no drawable, its intrinsic size is 0.
mDrawableWidth = -1;
mDrawableHeight = -1;
w = h = 0;
} else {
w = mDrawableWidth;
h = mDrawableHeight;
if (w <= 0) w = 1;
if (h <= 0) h = 1;
// We are supposed to adjust view bounds to match the aspect
// ratio of our drawable. See if that is possible.
if (mAdjustViewBounds) {
// 当你设置的宽(或高)值为wrap_content时,会触发相应的宽(或高)的长度的调整
resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;
// 调整时用的宽高比例就是图片资源的宽高比
desiredAspect = (float) w / (float) h;
}
}
if (resizeWidth || resizeHeight) {
// Get the max possible width given our constraints
widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec);
// Get the max possible height given our constraints
heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec);
if (desiredAspect != 0.0f) {
// See what our actual aspect ratio is
float actualAspect = (float)(widthSize - pleft - pright) /
(heightSize - ptop - pbottom);
if (Math.abs(actualAspect - desiredAspect) > 0.0000001) {
boolean done = false;
// 等比例调整宽度
if (resizeWidth) {
int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright;
if (newWidth <= widthSize) {
widthSize = newWidth;
done = true;
}
}
// 等比例调整高度
if (!done && resizeHeight) {
int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) +
ptop + pbottom;
if (newHeight <= heightSize) {
heightSize = newHeight;
}
}
}
}
} else {
}
setMeasuredDimension(widthSize, heightSize);
}