ImageViewのビュー自体のサイズと、中身(Drawable)の表示のされ方はいろいろな要素に影響されて決まる。
ちょっとわかりにくいのでざっと整理してみたいと思う。

(この記事を読むには、Androidのレイアウト計算についてなどで、レイアウトの決まり方を知っておく必要あり)

サイズに影響を与える要素は以下の10個がある。<ol>

  • 親ViewGroupからのmeasureSpec
  • DrawableのIntrinsicWidth/Height(Bitmapなどの大きさ)
  • minWidth/Height
  • maxWidth/Height
  • BackgroundDrawableのIntrinsicWidth/Height
  • adjustViewBounds
  • padding
  • matrix
  • scaleType
  • cropToPadding
    </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が表示される。