画像をカスタマイズする

画像のカスタマイズには、Image コンポーザブル(contentScalecolorFilter)のプロパティを使用します。既存の修飾子を利用して、Image にさまざまな効果を適用することもできます。修飾子は、Image コンポーザブルだけでなく任意のコンポーザブルにも使用できます。一方、contentScalecolorFilter は、Image コンポーザブルの明示的なパラメータです。

コンテンツの調整

contentScale オプションを指定することで、画像を切り抜いたり、境界内で画像の大きさを変更したりできます。contentScale オプションを指定しない場合は、デフォルトで ContentScale.Fit が使用されます。

以下の例では、Image コンポーザブルの枠を 150 dp に制限して Image コンポーザブルの背景を黄色に設定しています。ContentScale オプションの設定を変えた場合の画像を下の表に示します。

val imageModifier = Modifier
    .size(150.dp)
    .border(BorderStroke(1.dp, Color.Black))
    .background(Color.Yellow)
Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Fit,
    modifier = imageModifier
)

ContentScale オプションの設定を変えると、出力結果も異なります。次の表は、適切な ContentScale モードを選択する際に役立ちます。

元の画像 犬が写っているポートレートのソース。 風景のソース。別の犬が表示されています。
ContentScale 結果 - 縦長の画像: 結果 - 横長の画像:
ContentScale.Fit: アスペクト比を維持しながら、画像を均一に拡大・縮小します(デフォルト)。コンテンツが境界より小さい場合、画像はそれに合わせて拡大されます。 均一にスケーリングされた犬のポートレート。 均一にスケーリングされた犬の風景。
ContentScale.Crop: 空いているスペースがなくなるように画像を切り抜いて中央に配置します。 正方形のフレームに合わせて切り抜かれた縦向きの画像。画像の中央部分のみが表示されている。 正方形のフレームを埋めるように切り抜かれた横長の画像。画像の中央部分のみが表示されている。
ContentScale.FillHeight: 境界をデスティネーションの高さに合わせ、アスペクト比を維持しながら元の画像を拡大・縮小します。 正方形のフレームの高さに合わせて拡大された縦向きの画像。左右に黄色の背景が見える状態で、画像全体が表示されている。 横向きの画像が正方形のフレームの高さに合わせて拡大縮小され、左右が切り取られている。
ContentScale.FillWidth: 境界をデスティネーションの幅に合わせ、アスペクト比を維持しながら元の画像を拡大・縮小します。 正方形のフレームの幅いっぱいに拡大縮小された縦向きの画像。上下が切り取られている。 正方形のフレームの幅いっぱいに拡大された風景画像。画像全体が表示され、上下に黄色の背景が見える。
ContentScale.FillBounds: デスティネーションの境界に合わせ、垂直方向と水平方向に不均一にコンテンツを拡大・縮小します(注: 画像の比率に正確に合っていないコンテナに配置すると、画像が歪みます)。 正方形のフレームいっぱいに収まるように歪められた縦長の画像。画像が引き伸ばされています。 正方形のフレーム全体を埋めるように歪められた横長の画像。画像が引き伸ばされています。
ContentScale.Inside: デスティネーションの境界内で、アスペクト比を維持しながら元の画像を拡大・縮小します。元の画像の縦と横がデスティネーションと同じか小さい場合は、None に設定した場合と同じような結果になります。コンテンツは常に境界内に収まります。コンテンツが境界よりも小さい場合、スケーリングは適用されません。 元の画像が境界より大きい: 正方形の境界線よりも大きい縦長画像を、アスペクト比を維持したまま縮小して境界線内に収めた画像。左右に黄色の背景が表示されている。 元の画像が境界より小さい: 縦長の画像(元は正方形の境界よりも小さい)が、フレーム内に元のサイズで表示され、周囲に黄色の背景が表示されている。 元の画像が境界より大きい: 正方形の境界よりも大きい横向きの画像が、アスペクト比を維持したまま縮小され、上下に黄色の背景が表示されている。 元の画像が境界より小さい: 元のサイズが正方形の境界よりも小さい横長の画像が、フレーム内に元のサイズで表示され、周囲に黄色の背景が表示されている。
ContentScale.None: 元の画像にスケーリングを適用しません。コンテンツがデスティネーションの境界より小さくても、境界に合うように拡大はされません。 元の画像が境界より大きい: 正方形の境界線よりも大きい縦長画像が元のサイズで表示され、フレームの上部と下部からはみ出している。 元の画像が境界より小さい: 縦長の画像(元は正方形の境界よりも小さい)が、フレーム内に元のサイズで表示され、周囲に黄色の背景が表示されている。 元の画像が境界より大きい: 元のサイズで表示された横長の画像。元のサイズは正方形の境界よりも大きく、フレームの左右に一部がはみ出しています。 元の画像が境界より小さい: 元のサイズが正方形の境界よりも小さい横長の画像が、フレーム内に元のサイズで表示され、周囲に黄色の背景が表示されている。

Image コンポーザブルを特定の形にクリップする

画像を特定の形にするには、組み込みの clip 修飾子を使用します。 画像を円形に切り抜くには、Modifier.clip(CircleShape) を使用します。

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(200.dp)
        .clip(CircleShape)
)

犬の画像が完全な円形に切り抜かれています。
図 1. CircleShape で画像をクリップします。

角を丸くするには、Modifier.clip(RoundedCornerShape(16.dp) を使用して、丸くする角のサイズを指定します。

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(200.dp)
        .clip(RoundedCornerShape(16.dp))
)

角丸で切り抜かれた犬の正方形の画像。
図 2. RoundedCornerShape で画像をクリップします。

Shape を拡張して Path を指定し、画像を独自の形にクリップすることもできます。

class SquashedOval : Shape {
    override fun createOutline(
        size: Size,
        layoutDirection: LayoutDirection,
        density: Density
    ): Outline {
        val path = Path().apply {
            // We create an Oval that starts at ¼ of the width, and ends at ¾ of the width of the container.
            addOval(
                Rect(
                    left = size.width / 4f,
                    top = 0f,
                    right = size.width * 3 / 4f,
                    bottom = size.height
                )
            )
        }
        return Outline.Generic(path = path)
    }
}

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(200.dp)
        .clip(SquashedOval())
)

カスタムの楕円形に切り抜かれた犬の正方形の画像。
図 3. カスタム path を指定してクリップした画像。

Image コンポーザブルに枠線を追加する

通常、画像の周囲に枠線を付けるには、Modifier.border()Modifier.clip() を併用します。

val borderWidth = 4.dp
Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(150.dp)
        .border(
            BorderStroke(borderWidth, Color.Yellow),
            CircleShape
        )
        .padding(borderWidth)
        .clip(CircleShape)
)

犬の正方形の画像が円形に切り抜かれ、円形の周りに黄色の枠線が表示されている。
図 4. 枠線付きの切り抜き画像。

グラデーションの枠を作成する場合は、Brush API を使用すれば、画像の周囲に虹色にグラデーションする枠線を描画できます。

val rainbowColorsBrush = remember {
    Brush.sweepGradient(
        listOf(
            Color(0xFF9575CD),
            Color(0xFFBA68C8),
            Color(0xFFE57373),
            Color(0xFFFFB74D),
            Color(0xFFFFF176),
            Color(0xFFAED581),
            Color(0xFF4DD0E1),
            Color(0xFF9575CD)
        )
    )
}
val borderWidth = 4.dp
Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(150.dp)
        .border(
            BorderStroke(borderWidth, rainbowColorsBrush),
            CircleShape
        )
        .padding(borderWidth)
        .clip(CircleShape)
)

円形の犬の画像。円形の周囲に虹色のグラデーションの枠線が描かれている。
図 5. 虹色にグラデーションする円の枠線。

カスタム アスペクト比を設定する

画像をカスタム アスペクト比に変換するには、Modifier.aspectRatio(16f/9f) を使用して画像(または任意のコンポーザブル)にカスタム比率を指定します。

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    modifier = Modifier.aspectRatio(16f / 9f)
)

犬の正方形の画像が 16:9 のアスペクト比に変換され、幅が広くなり、高さが低くなっている。
図 6. ImageModifier.aspectRatio(16f/9f) を使用する。

カラーフィルタ: 画像のピクセルカラーを変換する

Image コンポーザブルには、画像の各ピクセルの出力を変更できる colorFilter パラメータが用意されています。

画像を色付けする

ColorFilter.tint(color, blendMode) を使用すると、指定された色のブレンドモードが Image コンポーザブルに適用されます。ColorFilter.tint(color, blendMode) は、BlendMode.SrcIn を使用してコンテンツの色合いを調整します。つまり、画像が表示される画面が、指定した色になります。これは、異なるテーマを設定する必要があるアイコンやベクターに役立ちます。

Image(
    painter = painterResource(id = R.drawable.baseline_directions_bus_24),
    contentDescription = stringResource(id = R.string.bus_content_description),
    colorFilter = ColorFilter.tint(Color.Yellow)
)

黄色の色調が適用されたバスの画像。
図 7. BlendMode.SrcInColorFilter.tint を適用しました。

BlendMode を追加すると、設定によって異なる効果が画像に適用されます。たとえば、BlendMode.Darken を指定して Color.Green を設定すると、次のような画像になります。

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    colorFilter = ColorFilter.tint(Color.Green, blendMode = BlendMode.Darken)
)

BlendMode.Darken を使用して緑の色合いを適用した犬。緑の色合いが濃くなっています。
図 8. BlendMode.DarkenColor.Green tint

利用可能な各種ブレンドモードの詳細については、BlendMode リファレンス ドキュメントをご覧ください。

カラー マトリックスを使用して Image フィルタを適用する

カラー マトリックス ColorFilter オプションを使用して画像を変換します。たとえば、画像にモノクロ フィルタを適用するには、ColorMatrix を使用して、彩度を 0f に設定します。

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    colorFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) })
)

黒と白のフィルタが適用され、すべての彩度が削除された犬。
図 9. 彩度 0 のカラー マトリックス(モノクロ画像)。

Image コンポーザブルのコントラストや明るさを調整する

画像のコントラストと明るさを変更するには、ColorMatrix を使用して値を変更します。

val contrast = 2f // 0f..10f (1 should be default)
val brightness = -180f // -255f..255f (0 should be default)
val colorMatrix = floatArrayOf(
    contrast, 0f, 0f, 0f, brightness,
    0f, contrast, 0f, 0f, brightness,
    0f, 0f, contrast, 0f, brightness,
    0f, 0f, 0f, 1f, 0f
)
Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    colorFilter = ColorFilter.colorMatrix(ColorMatrix(colorMatrix))
)

明るさとコントラストが調整され、より鮮やかに見える犬。
図 10. ColorMatrix を使用して画像の明るさとコントラストを調整

Image コンポーザブルの色を反転する

画像の色を反転するには、ColorMatrix を設定します。

val colorMatrix = floatArrayOf(
    -1f, 0f, 0f, 0f, 255f,
    0f, -1f, 0f, 0f, 255f,
    0f, 0f, -1f, 0f, 255f,
    0f, 0f, 0f, 1f, 0f
)
Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    colorFilter = ColorFilter.colorMatrix(ColorMatrix(colorMatrix))
)

色が反転した犬。ネガのような効果が表れている。
図 11. 色を反転した画像。

Image コンポーザブルをぼかす

画像をぼかすには、Modifier.blur() を使用します。radiusXradiusY を指定して、ぼかす範囲の半径を水平方向と垂直方向のそれぞれで指定します。

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(150.dp)
        .blur(
            radiusX = 10.dp,
            radiusY = 10.dp,
            edgeTreatment = BlurredEdgeTreatment(RoundedCornerShape(8.dp))
        )
)

強いぼかし効果が適用され、焦点が合っていないように見える犬。
図 12. BlurEffect が適用された画像。

Images をぼかす場合は、BlurredEdgeTreatment.Unbounded ではなく BlurredEdgeTreatment(Shape) を使用することをおすすめします。後者は、元のコンテンツの境界を超えてレンダリングする画像にぼかしを入れる場合に使用します。画像の場合、コンテンツの境界外にレンダリングすることは少ないかもしれませんが、角丸の長方形をぼかす際には、両者を区別して使用することが必要になる場合があります。

たとえば、前の画像で BlurredEdgeTreatmentUnbounded に設定すると、画像の境界がはっきりせずにぼやけて表示されます。

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(150.dp)
        .blur(
            radiusX = 10.dp,
            radiusY = 10.dp,
            edgeTreatment = BlurredEdgeTreatment.Unbounded
        )
        .clip(RoundedCornerShape(8.dp))
)

犬のぼかし画像。ぼかしが元の画像の境界を超えて広がり、輪郭がぼやけています。
図 13. BlurEdgeTreatment.Unbounded.