Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。
そのライブラリ中のImageについて、構成や使用方法などをまとめます。
※環境:Android Studio Hedgehog | 2023.1.1 Patch 2
Kotlin 1.9.0
Compose Compiler 1.5.1
androidx.compose.foundation 1.5.0
目次
UIの概要
Imageは画像データの表示を行うUI要素です。
@Preview_mdpi @Composable fun ImagePanelPreview() { Box( modifier = Modifier.size(320.dp, 240.dp), contentAlignment = Alignment.Center ) { Image( painter = painterResource(R.drawable.leaf1), contentDescription = "新緑の季節", ) } }
関数の引数
Imageの状態(コンテンツ、装飾、演出など)は、引数により指定できます。
fun Image( painter: Painter, contentDescription: String?, modifier: Modifier = Modifier, alignment: Alignment = Alignment.Center, contentScale: ContentScale = ContentScale.Fit, alpha: Float = DefaultAlpha, colorFilter: ColorFilter? = null ) { ... }
引数 | 概要 | |
---|---|---|
painter | Painter | 画像データ |
contentDescription | String? | 画像の説明 |
modifier | Modifier | UI全般のCompose修飾子 |
alignment | Aligment | 画像の配置(配置の基準点)(後述) |
contentScale | ContentScale | 画像のスケールモード(後述) |
alpha | Float | 画像のアルファ値 |
colorFilter | colorFilter | 画素に適用するカラーフィルター(後述) |
関数の構成
関数は内部にLayout関数(Composable関数)のみを持ちます。
Layoutは「自身のサイズを決定する機能」を定義する関数です。この機能は描画処理のLayoutフェーズで実行されます。
また、画像データ(Painter)は拡張関数Modifier.paint( )へ渡されて、描画処理のDrawingフェーズで描画されます。
※描画処理については「Jetpack Compose:Composeによるアプリ画面の描画」を参照
@Composable fun Image( painter: Painter, contentDescription: String?, modifier: Modifier = Modifier, alignment: Alignment = Alignment.Center, contentScale: ContentScale = ContentScale.Fit, alpha: Float = DefaultAlpha, colorFilter: ColorFilter? = null ) { val semantics = if (contentDescription != null) { Modifier.semantics { this.contentDescription = contentDescription this.role = Role.Image } } else { Modifier } // Explicitly use a simple Layout implementation here as Spacer squashes any non fixed // constraint with zero Layout( {}, modifier.then(semantics).clipToBounds().paint( painter, alignment = alignment, contentScale = contentScale, alpha = alpha, colorFilter = colorFilter ) ) { _, constraints -> layout(constraints.minWidth, constraints.minHeight) {} } }
画像のサイズ(Modifier.size)
Modifier.sizeで画像の表示サイズ(DispSize)が指定できます。
表示サイズに合うように、画像(OriginSize)のスケーリング(DispSizeへ拡大・縮小)が行われます。
@Preview_mdpi @Composable fun SizeXXX() { Box(modifier = Modifier.size(320.dp, 240.dp)) { // DrawArea:320x240 Image( painter = painterResource(R.drawable.bike), // OriginSize:280x210 contentDescription = null, modifier = Modifier.size(320.dp, 240.dp) // ZoomIn DispSize:320x240 // modifier = Modifier // 1on1 DispSize:280x210 // modifier = Modifier.size(240.dp, 180.dp) // ZoomOut DispSize:240x180 ) } }
サンプル中に登場する「DrawArea」は「描画が可能な領域」を表しています。
ImageはBox(コンテナタイプのUI)へ格納されます。ですので、Imageが描画可能な領域はBoxのサイズ内です。
「DrawArea < DispSize」の場合は、スケールモード(contentScale)に従った縮小が行われるので注意してください。
ちなみに、DrawAreaのことを「制約(Constraints)」と呼びます。制約はBoxからImageへ与えられます。
画像の配置(引数:alignment)
「引数:alignment」で画像の配置を指定できます。
パラメータ | 基準点 | ||
---|---|---|---|
Alignment | TopStart | 垂直方向:上 | 水平方向:左 |
TopCenter | 水平方向:中央 | ||
TopEnd | 水平方向:右 | ||
CenerStart | 垂直方向:中央 | ||
Cener | |||
CenterEnd | |||
BottomStart | 垂直方向:下 | ||
BottomCenter | |||
BottomEnd |
パラメータは画像の一点を表しています。画像はこの点を基準に配置されます。
@Preview_mdpi @Composable fun AlignmentXXX() { Box(modifier = Modifier.size(200.dp, 150.dp)) { // DrawArea:200x150 Image( painter = painterResource(R.drawable.bike), // DispSize:280x210 contentDescription = null, contentScale = ContentScale.None, // スケーリング無し alignment = Alignment.TopStart // alignment = Alignment.TopCenter // alignment = Alignment.TopEnd // alignment = Alignment.CenterStart // alignment = Alignment.Center // alignment = Alignment.CenterEnd // alignment = Alignment.BottomStart // alignment = Alignment.BottomCenter // alignment = Alignment.BottomEnd ) } }
画像のスケールモード(引数:contentScale)
「引数:contentScale」で画像のスケールモードを指定できます。
モード | スケーリング動作 | ||
---|---|---|---|
Crop | アスペクト比を維持 | 縦または横が DispSize≦DrawAreaになるまでスケーリング | 切り出し有り |
Fit | DispSize全体が DrawAreaに収まるまでスケーリング | 切り出し無し | |
FillHeight | 縦が DispSize≦DrawAreaになるまでスケーリング | 切り出し有り | |
FillWidth | 横が DispSize≦DrawAreaになるまでスケーリング |
||
Inside | Fitと同じ | ||
None | スケーリング無し | ||
FillBounds | アスペクト比を無視 | DispSize全体が DrawAreaに収まるまでスケーリング | 切り出し無し |
※デフォルト:「alignment = Alignment.Center」のため、画像の中心を基準に切り出し |
以下は、描画が可能な領域(DrawArea)と表示サイズ(DispSize)の組み合わせによる、スケーリング動作の違いを示した図です。
@Preview_mdpi @Composable fun ContentScaleTestPreview() { Box(modifier = Modifier.size(320.dp, 240.dp)) { // DrawArea:320x240 // Box(modifier = Modifier.size(240.dp, 120.dp)) { // DrawArea:240x120 // Box(modifier = Modifier.size(140.dp, 170.dp)) { // DrawArea:140x170 // Box(modifier = Modifier.size(160.dp, 240.dp)) { // DrawArea:160x240 // Box(modifier = Modifier.size(320.dp, 120.dp)) { // DrawArea:320x120 Image( painter = painterResource(R.drawable.bike), // DispSize:280x210 contentDescription = null, contentScale = ContentScale.Crop // contentScale = ContentScale.Fit // contentScale = ContentScale.FillHeight // contentScale = ContentScale.FillWidth // contentScale = ContentScale.Inside // contentScale = ContentScale.None // contentScale = ContentScale.FillBounds ) } }
画素のカラーフィルター(引数:colorFilter)
「引数:colorFilter」で画像の画素(ピクセルのARGB値)に適用するカラーフィルターを指定できます。
タイプは3つあります。
タイプ | フィルター動作 |
---|---|
tini | ソース画素とデスティネーション画素の色をBlendModeに従って合成 用途:アイコンの配色の置き換え |
colorMatrix | 4x5カラーマトリックスを使った演算で色を変換 用途:彩度の変更、YUV⇔RGB変換 |
lighting | 乗算値と加算値の2つを使った演算で色を変換 用途:照明効果のシミュレート |
※次の場所にドキュメント「画像のピクセルカラーを変換する」が用意されています。
※サンプルに登場するオリジナル画像
tin
ソース画素(第1引数)とデスティネーション画素(オリジナル画像の画素)の色をBlendMode(第2引数)に従って合成します。
@Immutable class ColorFilter internal constructor(internal val nativeColorFilter: NativeColorFilter) { companion object { ... @Stable fun tint(color: Color, blendMode: BlendMode = BlendMode.SrcIn): ColorFilter = actualTintColorFilter(color, blendMode) ... } }
BlendModeについては「BlendMode(Compose UI向け説明)」または「BlendMode(画像付き)」を参照してください。
BlendMode.Overlayデスティネーション側を優先するように、ソースとデスティネーション画素の色を乗算します。
@Preview_mdpi @Composable fun FilterTint_Overlay() { Box { Image( painter = painterResource(R.drawable.bike), contentDescription = null, // colorFilter = ColorFilter.tint(Color.Red, BlendMode.Overlay) // colorFilter = ColorFilter.tint(Color.Green, BlendMode.Overlay) colorFilter = ColorFilter.tint(Color.Blue, BlendMode.Overlay) ) } }
ソース画像を表示しますが、デスティネーション画像と重なる画素のみを表示します。
@Preview_mdpi @Composable fun FilterTint_SrcIn() { Box { Image( painter = painterResource(R.drawable.baseline_sentiment_satisfied), contentDescription = null, // colorFilter = ColorFilter.tint(Color.Cyan, BlendMode.SrcIn) colorFilter = ColorFilter.tint(Color.Magenta, BlendMode.SrcIn) ) } }
アイコンの色を状況により動的に変更したい場合に有効です。
colorMatrix
4×5カラーマトリックスを使った演算で色を変換します。
マトリックスは配列(FloatArray)で表現されます。
@Immutable class ColorFilter internal constructor(internal val nativeColorFilter: NativeColorFilter) { companion object { ... @Stable fun colorMatrix(colorMatrix: ColorMatrix): ColorFilter = actualColorMatrixColorFilter(colorMatrix) ... } }
★「4x5カラーマトリックス」の配列表現 [ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t ] ★「画素(R,G,B,A)×カラーマトリックス」の演算 R' = a*R + b*G + c*B + d*A + e; G' = f*R + g*G + h*B + i*A + j; B' = k*R + l*G + m*B + n*A + o; A' = p*R + q*G + r*B + s*A + t;ネガティブへ変換
ポジ⇒ネガ変換の例です。
R、G、B値を反転させています。
@Preview_mdpi @Composable fun FilterColorMatrix_Nega() { Box { val _matrix = ColorMatrix(floatArrayOf( -1.0f, 0.0f, 0.0f, 0.0f, 255.0f, 0.0f, -1.0f, 0.0f, 0.0f, 255.0f, 0.0f, 0.0f, -1.0f, 0.0f, 255.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f )) Image( painter = painterResource(R.drawable.bike), contentDescription = null, colorFilter = ColorFilter.colorMatrix(_matrix) ) } }
カラー⇒モノクロ変換の例です。
@Preview_mdpi @Composable fun FilterColorMatrix_Mono() { Box { val _matrix = ColorMatrix().apply { setToSaturation(0f) } Image( painter = painterResource(R.drawable.bike), contentDescription = null, colorFilter = ColorFilter.colorMatrix(_matrix) ) } }
定型のマトリックスが準備されています。その中のsetToSaturation( )を用いています。
lighting
乗算値(multiply)と加算値(add)の2つを使った演算で色を変換します。
乗算はmultiply/255(≦1)が係数になるので、暗くする効果があります。
また、加算はadd(≧0)が係数になるので、明るくする効果があります。
@Immutable class ColorFilter internal constructor(internal val nativeColorFilter: NativeColorFilter) { companion object { ... @Stable fun lighting(multiply: Color, add: Color): ColorFilter = actualLightingColorFilter(multiply, add) ... } }
★「multiplyとadd」の表現 multiply = Color(m_r, m_g, m_b, m_a) add = Color(a_r, a_g, a_b, a_a) ★「乗算と加算」の演算 R' = (m_r/255)*R + a_r; G' = (m_g/255)*G + a_g; B' = (m_b/255)*B + a_b; A' = A; ※アルファ―値(m_a、a_a)は無視Darkness(乗算)
輝度を1/2にする例です。
@Preview_mdpi @Composable fun FilterLighting_Mul() { Box(modifier = Modifier.background(Color.Cyan)) { val _multiply = Color(128, 128, 128, 255) val _add = Color(0, 0, 0, 255) Image( painter = painterResource(R.drawable.bike), contentDescription = null, colorFilter = ColorFilter.lighting(_multiply, _add) ) } }
輝度を×2にする例です。一部(右下)が白飛びになっています。
@Preview_mdpi @Composable fun FilterLighting_Add() { Box(modifier = Modifier.background(Color.Cyan)) { val _multiply = Color(255, 255, 255, 255) val _add = Color(128, 128, 128, 255) Image( painter = painterResource(R.drawable.bike), contentDescription = null, colorFilter = ColorFilter.lighting(_multiply, _add) ) } }
関連記事: