[Android]ImageViewの表示サイズの決まり方
ImageViewのビュー自体のサイズと、中身(Drawable)の表示のされ方はいろいろな要素に影響されて決まる。
ちょっとわかりにくいのでざっと整理してみたいと思う。
(この記事を読むには、Androidのレイアウト計算についてなどで、レイアウトの決まり方を知っておく必要あり)
サイズに影響を与える要素は以下の10個がある。<ol>
</ol>
このうち、ビューのサイズに影響を与えるのは1~7のみで、8~10はDrawableの表示のみに影響を与える。
ImageViewのソースから、Drawableをセットした場合の処理の流れを追いかけてみる。
setImageURI/setImageResource/setImageDrawableなどDrawableを変更するメソッドを呼ぶと、最後にrequestLayoutが呼ばれる。
requestLayoutを呼び出すと、フレームワークからmeasureが呼ばれ、measureの中からonMeasureが呼ばれる。ここでViewのWidth/Heightが決まる。
Width/Heightが決まるとフレームワークからlayoutが呼ばれ、layoutの中からsetLayoutが呼ばれる。setLayoutはさらにconfigureBounds(private関数)を呼び出す。ここで、Drawableの表示方法が決められる。
onMeasure
onMeasureの中では、以下のような方法で幅・高さが決められている(ただし、説明を簡単にするためにpaddingは0と仮定)
・adjustViewBoundsがtrueで、measureSpecの指定が幅・高さのどちらかが可変の場合
DrawableのIntrinsicWidth/Heightを、measureSpecとmaxで調整して幅・高さを仮ぎめする(ここで最大が決まる)
幅・高さをDrawableのアスペクト比と同じになるように調整する。
順序は、幅→高さの順。仮ぎめしたものより小さくなる。
調整が効かなかった場合は、仮ぎめしたものがそのまま使われる。
・上記以外の場合
DrawableのIntrinsicWidth/Heightを、
・measureSpec/EXACTLY
・min以上 && BackgroundDrawableのIntrinsicWidth/Height以上
・max以下 && measureSpec/AT_MOST 以下
の制約条件を満たすように調整する(優先度は上から順)
(注)adjustViewBounds=trueの場合、minは無視されることになる
configureBounds
configureBoundsでは、DrawableのIntrinsicWidth/HeightとViewのWidth/Heightが所与のものとして、ScaleTypeを元にDrawableを描画する時のMatrixを計算する。
(レイアウトが完了していない場合にも呼ばれることもあるが、その場合は何もしない)
・fitXY
Viewのwidth,heightになるようにMatrixを設定
・matrix
setImageMatrixで指定したMatrixをそのまま使う
・center
Drawableを中央に配置するようにMatrixを設定
・center_crop
Drawableを、アスペクト比を維持しつつ隙間がなくなるように拡大・縮小して中央に配置するようにMatrixを設定
・center_inside
Drawableを、アスペクト比を維持しつつ全てが収まるように拡大・縮小して配置するようにMatrixを設定
・fitStart, fitEnd, fitCenter
Matrix#ScaleToFitにおまかせ
onDraw
onDrawで計算したMatrixを使って描画を行う。
ただし、ViewよりDrawableのほうが大きい場合、CropToPaddingでpaddingの処理が変わる。
具体的には、CropToPaddingがfalse(デフォルト)の場合は、はみ出した箇所にはpaddingが表示されない(paddingの領域にもDrawableが描画される)が、trueだと常にpaddingが表示される。