ScaleTypeと表示画像の対応を整理するために作成したサンプルアプリで、ScaleType
をFIT_START
→FIT_XY
→MATRIX
と切り替えたときにFIT_START
とMATRIX
のときの画像表示が同じになる現象が発生したので、備忘録を兼ねてその際に調査した内容をまとめました。
ScaleTypeを変更したときの内部処理
ImageView.javaのソースコードはこちらにあります。
まず、ScaleType
を切り替えるためにsetScaleType()
を呼び出します。
引数に指定されたScaleType
がmScaleType
に代入されます。
public void setScaleType(ScaleType scaleType) {
if (scaleType == null) {
throw new NullPointerException();
}
if (mScaleType != scaleType) {
mScaleType = scaleType;
requestLayout();
invalidate();
}
}
このmScaleType
はconfigureBounds()
内で分岐条件として使われます。
以下、今回の調査に必要な条件のみを抜粋します。
① mScaleType == MATRIX
のとき、以下のような処理が行われます。
if (mMatrix.isIdentity()) {
mDrawMatrix = null;
} else {
mDrawMatrix = mMatrix;
}
mMatrix
はMatrix
の参照を持つ変数です。mDrawMatrix
はmMatrix
の参照を代入するための変数です。この2つの変数によってMatrix
による画像変換を行うか否かを制御しています。
mMatrix
が単位行列(変換前後で画像の状態を変化させないMatrix
)の場合、mDrawMatrix
にnullを代入しています。
② mScaleType == FIT_CENTER、FIT_END、FIT_START
のとき、以下のような処理が行われます。
mTempSrc.set(0, 0, dwidth, dheight);
mTempDst.set(0, 0, vwidth, vheight);
mDrawMatrix = mMatrix;
mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
ネイティブコードを呼び出していたので詳細は確認できなかったのですが、最後のsetRectToRect()
によってmScaleType
に対応したMatrix
を計算しているようです。
この処理によってmDrawMatrix
の参照先の内容が更新されることがEvaluate Expressionでトレースすることによって分かりました。
③ mScaleType == FIT_XY
のとき、以下のような処理が行われます。
vwidth
、vheight
はそれぞれImageViewの幅と高さです。
mDrawable.setBounds(0, 0, vwidth, vheight);
mDrawMatrix = null;
そして、mDrawMatrix
はonDraw()
内で分岐条件として使われています。
mDrawMatrix
がnullでないときのみMatrix
による画像変換が行われます。
@Override
protected void onDraw(Canvas canvas) {
...
if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) {
mDrawable.draw(canvas);
} else {
...
if (mDrawMatrix != null) {
canvas.concat(mDrawMatrix);
}
mDrawable.draw(canvas);
canvas.restoreToCount(saveCount);
}
}
以上を踏まえ、改めて状況を整理します。
ScaleType
をFIT_START
→FIT_XY
→MATRIX
と切り替えたとき、
-
FIT_START
- ②の処理が行われるので、
mMatrix
の参照先のMatrix
の内容はFIT_START
に対応した値になる -
mDrawMatrix
はnullでないので、Matrix
による画像変換が行われる
- ②の処理が行われるので、
-
FIT_XY
- ③の処理が行われるので、
mMatrix
の参照先のMatrix
の内容はFIT_START
に対応した値のままになる
-
mDrawMatrix
はnullなので、Matrix
による画像変換は行われない
- ③の処理が行われるので、
-
MATRIX
- ①の処理が行われるので、
mMatrix
の参照先のMatrix
の内容はFIT_START
に対応した値のままになる -
mDrawMatrix
はnullでないので、Matrix
による画像変換が行われる
- ①の処理が行われるので、
となるため、FIT_START
とMATRIX
のときの画像表示が同じになっていました。
最後に、この現象を回避するためにScaleType
を変更する前にsetImageMatrix()
を呼び出します。
public void setImageMatrix(Matrix matrix) {
// collapse null and identity to just null
if (matrix != null && matrix.isIdentity()) {
matrix = null;
}
// don't invalidate unless we're actually changing our matrix
if (matrix == null && !mMatrix.isIdentity() || matrix != null && !mMatrix.equals(matrix)) {
mMatrix.set(matrix);
configureBounds();
invalidate();
}
}
setImageMatrix()
の引数に新しい単位行列を代入することでmMatrix
が初期化されます。