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が初期化されます。