3つの画像読み込みライブラリ Glide / Picasso / Coil - JetpackCompose 対応の状況

どれが、今、旬なのか。

まずはリンクを列挙しておきます。

全部使ってみようと思います。

giide vs picasso vs coil

👉 android glide, android picasso, android coil - 調べる - Google トレンド hatena-bookmark

 

Glide


32.8k stars
Watchers 1.1k watching
Forks 6k forks

👉 bumptech/glide: An image loading and caching library for Android focused on smooth scrolling hatena-bookmark

Glide

👉 Glide v4 : Fast and efficient image loading for Android hatena-bookmark

@sjudd Hey, I believe you're one of the maintainers of Glide. Is it a planned feature? Does Glide's team want help from the community on this? Jetpack Compose is going to stable soon, I believe later this month or next month

👉 Jetpack Compose Support · Issue #4459 · bumptech/glide hatena-bookmark

 

Picasso


18.3k stars
Watchers 867 watching
Forks 4k forks

👉 square/picasso: A powerful image downloading and caching library for Android hatena-bookmark

Picasso

👉 Picasso hatena-bookmark

Nobody works on Picasso. If you want something in the next N years definitely use Coil. Or Glide. Or whatever. They're all fine.

Image loading is a terrible, horrible business to be in. It's been really nice not being in that business for the last few years. I don't see a reason to resume. I certainly have no intent to support it anymore. Picasso accomplished its goal of moving the ecosystem out of the painful image loaders of 2011/2012 to the fluent and extensible ones we know today. But it's filled with technical debt and the legacy of poor design (at least, in hindsight) and is currently very, very stuck between a major refactor and redesign with no end in sight.

👉 Consider providing Jetpack Compose support · Issue #2203 · square/picasso hatena-bookmark

 

Coil


8.4k stars
Watchers 98 watching
Forks 512 forks

👉 coil-kt/coil: Image loading for Android backed by Kotlin Coroutines. hatena-bookmark

Coil

👉 Coil hatena-bookmark

To add support for Jetpack Compose, import the extension library:

implementation("io.coil-kt:coil-compose:2.1.0")

👉 Jetpack Compose - Coil hatena-bookmark

 

まとめ

JetpackCompose への対応ライブラリ群も見逃せません。

👉 skydoves/landscapist: 🍂 Jetpack Compose image loading library that fetches and displays network images with Glide, Coil, and Fresco hatena-bookmark

👉 wasabeef/composable-images: The Composable Images is a library providing Jetpack Compose wrapper for Glide, Picasso, and Coil. hatena-bookmark

アーキテクチャー、フレームワーク、ライブラリの選定はそのプロダクトの安定感に直結します。

機能確認を中心に検証しながら、将来の本筋を外してはなりません。

👉 画像読み込みライブラリ「COIL」 hatena-bookmark


【Jetpack Compose】Square 製 Radiography で View の構造を確認する

👉 square/radiography: Text-ray goggles for your Android UI. hatena-bookmark


dependencies {
  implementation 'androidx.compose.ui:ui-tooling:1.0.0-betaXY'
  implementation 'com.squareup.radiography:radiography:2.4.1'
}

scan() すると、画面のツリー構造がテキストで取得できるようでです。


DecorView { 1080×2160px }
├─LinearLayout { id:main, 1080×1962px }
│ ├─EditText { id:username, 580×124px, focused, text-length:0, ime-target }
│ ├─EditText { id:password, 580×124px, text-length:0 }
│ ╰─LinearLayout { 635×154px }
│   ├─Button { id:signin, 205×132px, text-length:7 }
│   ╰─Button { id:forgot_password, 430×132px, text-length:15 }
├─View { id:navigationBarBackground, 1080×132px }
╰─View { id:statusBarBackground, 1080×66px }


// Render the view hierarchy for all windows.
val prettyHierarchy = Radiography.scan()


// Extension function on View, renders starting from that view.
val prettyHierarchy = someView.scan()

 

Jetpack Compose でやってみる

この画面でやってみます。

square / radiography

Composable にコードを追加しておきます。


@Composable
fun HomeScreen(
  viewModel: HomeViewModel = hiltViewModel()
) {

  // ...

  val view: View = LocalView.current

  SideEffect {
    println(Radiography.scan())
    println(view.scan())
  }

}

👉 SideEffect - Compose における副作用  |  Jetpack Compose  |  Android Developers hatena-bookmark

 

結果


I: window-focus:false
I:  DecorView { 1080×2160px }
I:  ├─LinearLayout { 1080×2116px }
I:  │ ├─ViewStub { id:action_mode_bar_stub, GONE, 0×0px }
I:  │ ╰─FrameLayout { id:content, 1080×2039px }
I:  │   ╰─ComposeView { 1080×2039px }
I:  │     ╰─AndroidComposeView { 1080×2039px }
I:  │       ╰─CompositionLocalProvider { 1080×2039px }
I:  │         ╰─ScaffoldLayout { 1080×2039px }
I:  │           ├─<subcomposition of ScaffoldLayout>
I:  │           ├─<subcomposition of ScaffoldLayout>
I:  │           │ ╰─SnackbarHost
I:  │           ├─<subcomposition of ScaffoldLayout>
I:  │           ├─<subcomposition of ScaffoldLayout>
I:  │           │ ╰─CompositionLocalProvider { 1080×154px }
I:  │           │   ├─NavBottomBar { 1080×154px }
I:  │           │   │ ╰─Row { 1080×154px, SELECTABLE-GROUP }
I:  │           │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:  │           │   │   │ ╰─BottomNavigationTransition { 162×154px }
I:  │           │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:  │           │   │   │   │ ├─Icon
I:  │           │   │   │   │ │ ╰─RenderVectorGroup
I:  │           │   │   │   │ ╰─Icon { 66×66px }
I:  │           │   │   │   ╰─Box { 96×44px, layout-id:"label" }
I:  │           │   │   │     ╰─ProvideTextStyle { 96×44px, text-length:4 }
I:  │           │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:  │           │   │   │ ╰─BottomNavigationTransition { 151×154px }
I:  │           │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:  │           │   │   │   │ ├─Icon
I:  │           │   │   │   │ │ ╰─RenderVectorGroup
I:  │           │   │   │   │ ╰─Icon { 66×66px }
I:  │           │   │   │   ╰─Box { 85×44px, layout-id:"label" }
I:  │           │   │   │     ╰─ProvideTextStyle { 85×44px, text-length:5 }
I:  │           │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:  │           │   │   │ ╰─BottomNavigationTransition { 156×154px }
I:  │           │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:  │           │   │   │   │ ├─Icon
I:  │           │   │   │   │ │ ├─RenderVectorGroup
I:  │           │   │   │   │ │ ├─RenderVectorGroup
I:  │           │   │   │   │ │ ├─RenderVectorGroup
I:  │           │   │   │   │ │ ╰─RenderVectorGroup
I:  │           │   │   │   │ ╰─Icon { 66×66px }
I:  │           │   │   │   ╰─Box { 90×44px, layout-id:"label" }
I:  │           │   │   │     ╰─ProvideTextStyle { 90×44px, text-length:5 }
I:  │           │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:  │           │   │   │ ╰─BottomNavigationTransition { 150×154px }
I:  │           │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:  │           │   │   │   │ ├─Icon
I:  │           │   │   │   │ │ ╰─RenderVectorGroup
I:  │           │   │   │   │ ╰─Icon { 66×66px }
I:  │           │   │   │   ╰─Box { 84×44px, layout-id:"label" }
I:  │           │   │   │     ╰─ProvideTextStyle { 84×44px, text-length:4 }
I:  │           │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:  │           │   │   │ ╰─BottomNavigationTransition { 146×154px }
I:  │           │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:  │           │   │   │   │ ├─Icon
I:  │           │   │   │   │ │ ╰─RenderVectorGroup
I:  │           │   │   │   │ ╰─Icon { 66×66px }
I:  │           │   │   │   ╰─Box { 80×44px, layout-id:"label" }
I:  │           │   │   │     ╰─ProvideTextStyle { 80×44px, text-length:4 }
I:  │           │   │   ╰─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:  │           │   │     ╰─BottomNavigationTransition { 180×154px }
I:  │           │   │       ├─Box { 66×66px, layout-id:"icon" }
I:  │           │   │       │ ├─Icon
I:  │           │   │       │ │ ╰─RenderVectorGroup
I:  │           │   │       │ ╰─Icon { 66×66px }
I:  │           │   │       ╰─Box { 114×44px, layout-id:"label" }
I:  │           │   │         ╰─ProvideTextStyle { 114×44px, text-length:8 }
I:  │           │   ╰─AdaptiveAd { 1080×0px }
I:  │           │     ╰─ViewFactoryHolder { 1080×0px }
I:  │           │       ╰─AdView { 1080×0px }
I:  │           │         ╰─FrameLayout { 0×0px }
I:  │           ╰─<subcomposition of ScaffoldLayout>
I:  │             ╰─NavHost
I:  │               ╰─Box
I:  │                 ╰─LocalOwnersProvider
I:  │                   ├─TopBar
I:  │                   │ ╰─CompositionLocalProvider
I:  │                   │   ├─Row
I:  │                   │   │ ╰─CompositionLocalProvider { roll:Button }
I:  │                   │   │   ├─CompositionLocalProvider
I:  │                   │   │   │ ╰─RenderVectorGroup
I:  │                   │   │   ╰─CompositionLocalProvider
I:  │                   │   ├─Row
I:  │                   │   │ ╰─ProvideTextStyle { text-length:4 }
I:  │                   │   ╰─CompositionLocalProvider
I:  │                   ╰─Column
I:  │                     ├─Button { roll:Button }
I:  │                     │ ╰─CompositionLocalProvider
I:  │                     │   ╰─Text { text-length:7 }
I:  │                     ├─Spacer
I:  │                     ├─Button { roll:Button }
I:  │                     │ ╰─CompositionLocalProvider
I:  │                     │   ╰─Text { text-length:7 }
I:  │                     ├─Spacer
I:  │                     ├─Button { roll:Button }
I:  │                     │ ╰─CompositionLocalProvider
I:  │                     │   ╰─Text { text-length:7 }
I:  │                     ├─Spacer
I:  │                     ╰─Button { roll:Button }
I:  │                       ╰─CompositionLocalProvider
I:  │                         ╰─Text { text-length:5 }
I:  ├─View { id:navigationBarBackground, 1080×44px }
I:  ╰─View { id:statusBarBackground, 1080×77px }

I: AndroidComposeView:
I: window-focus:false
I:  AndroidComposeView { 1080×2039px }
I:  ╰─CompositionLocalProvider { 1080×2039px }
I:    ╰─ScaffoldLayout { 1080×2039px }
I:      ├─<subcomposition of ScaffoldLayout>
I:      ├─<subcomposition of ScaffoldLayout>
I:      │ ╰─SnackbarHost
I:      ├─<subcomposition of ScaffoldLayout>
I:      ├─<subcomposition of ScaffoldLayout>
I:      │ ╰─CompositionLocalProvider { 1080×154px }
I:      │   ├─NavBottomBar { 1080×154px }
I:      │   │ ╰─Row { 1080×154px, SELECTABLE-GROUP }
I:      │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:      │   │   │ ╰─BottomNavigationTransition { 162×154px }
I:      │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:      │   │   │   │ ├─Icon
I:      │   │   │   │ │ ╰─RenderVectorGroup
I:      │   │   │   │ ╰─Icon { 66×66px }
I:      │   │   │   ╰─Box { 96×44px, layout-id:"label" }
I:      │   │   │     ╰─ProvideTextStyle { 96×44px, text-length:4 }
I:      │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:      │   │   │ ╰─BottomNavigationTransition { 151×154px }
I:      │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:      │   │   │   │ ├─Icon
I:      │   │   │   │ │ ╰─RenderVectorGroup
I:      │   │   │   │ ╰─Icon { 66×66px }
I:      │   │   │   ╰─Box { 85×44px, layout-id:"label" }
I:      │   │   │     ╰─ProvideTextStyle { 85×44px, text-length:5 }
I:      │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:      │   │   │ ╰─BottomNavigationTransition { 156×154px }
I:      │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:      │   │   │   │ ├─Icon
I:      │   │   │   │ │ ├─RenderVectorGroup
I:      │   │   │   │ │ ├─RenderVectorGroup
I:      │   │   │   │ │ ├─RenderVectorGroup
I:      │   │   │   │ │ ╰─RenderVectorGroup
I:      │   │   │   │ ╰─Icon { 66×66px }
I:      │   │   │   ╰─Box { 90×44px, layout-id:"label" }
I:      │   │   │     ╰─ProvideTextStyle { 90×44px, text-length:5 }
I:      │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:      │   │   │ ╰─BottomNavigationTransition { 150×154px }
I:      │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:      │   │   │   │ ├─Icon
I:      │   │   │   │ │ ╰─RenderVectorGroup
I:      │   │   │   │ ╰─Icon { 66×66px }
I:      │   │   │   ╰─Box { 84×44px, layout-id:"label" }
I:      │   │   │     ╰─ProvideTextStyle { 84×44px, text-length:4 }
I:      │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:      │   │   │ ╰─BottomNavigationTransition { 146×154px }
I:      │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:      │   │   │   │ ├─Icon
I:      │   │   │   │ │ ╰─RenderVectorGroup
I:      │   │   │   │ ╰─Icon { 66×66px }
I:      │   │   │   ╰─Box { 80×44px, layout-id:"label" }
I:      │   │   │     ╰─ProvideTextStyle { 80×44px, text-length:4 }
I:      │   │   ╰─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:      │   │     ╰─BottomNavigationTransition { 180×154px }
I:      │   │       ├─Box { 66×66px, layout-id:"icon" }
I:      │   │       │ ├─Icon
I:      │   │       │ │ ╰─RenderVectorGroup
I:      │   │       │ ╰─Icon { 66×66px }
I:      │   │       ╰─Box { 114×44px, layout-id:"label" }
I:      │   │         ╰─ProvideTextStyle { 114×44px, text-length:8 }
I:      │   ╰─AdaptiveAd { 1080×0px }
I:      │     ╰─ViewFactoryHolder { 1080×0px }
I:      │       ╰─AdView { 1080×0px }
I:      │         ╰─FrameLayout { 0×0px }
I:      ╰─<subcomposition of ScaffoldLayout>
I:        ╰─NavHost
I:          ╰─Box
I:            ╰─LocalOwnersProvider
I:              ├─TopBar
I:              │ ╰─CompositionLocalProvider
I:              │   ├─Row
I:              │   │ ╰─CompositionLocalProvider { roll:Button }
I:              │   │   ├─CompositionLocalProvider
I:              │   │   │ ╰─RenderVectorGroup
I:              │   │   ╰─CompositionLocalProvider
I:              │   ├─Row
I:              │   │ ╰─ProvideTextStyle { text-length:4 }
I:              │   ╰─CompositionLocalProvider
I:              ╰─Column
I:                ├─Button { roll:Button }
I:                │ ╰─CompositionLocalProvider
I:                │   ╰─Text { text-length:7 }
I:                ├─Spacer
I:                ├─Button { roll:Button }
I:                │ ╰─CompositionLocalProvider
I:                │   ╰─Text { text-length:7 }
I:                ├─Spacer
I:                ├─Button { roll:Button }
I:                │ ╰─CompositionLocalProvider
I:                │   ╰─Text { text-length:7 }
I:                ├─Spacer
I:                ╰─Button { roll:Button }
I:                  ╰─CompositionLocalProvider
I:                    ╰─Text { text-length:5 }

どう使います?、これ。

scan() 時のオプションもいろいろあるようなので試してみますか。


【Jetpack Compose】テーマデフォルトの色 (baseline palette) を確認する【AndroidStudio】

ここ、なぜ出ないの?

【Jetpack Compose】テーマデフォルトの色を確認したい【AndroidStudio】

コードを追っていけば分かるが面倒。

特に、デフォルト色をひと目で分かるやつがほしい。

Light も Dark も、こういう感じの。

【Jetpack Compose】テーマデフォルトの色を確認したい【AndroidStudio】

👉 Dark theme - Material Design hatena-bookmark

いいのだが実際の色があれば分かりやすいのに、の Material3一覧 は以下。


👉 Color theming with MDC-Android – Material Design 3 hatena-bookmark

しかも、ブラウザをいちいち開けるのもなんかだるい。(開けてるけど。)

色の視覚的な記憶、って出来なくない?

いい方法ないか。

 

リンクをコメントで書いておく

⌘(command) キーとクリックでブラウザ開きますから。

Color theming with MDC-Android – Material Design 3

どうせ、ブラウザは開けてるはず。

 

デフォルト色の記述を変えて書いておく

AndroidStudio エディタの左にも表示されるような記述で、敢えて再度書く。

【Jetpack Compose】テーマデフォルトの色 (baseline palette) を確認したい【AndroidStudio】

Light と Dark の比較が欲しくなってきました。

 

まとめ

もう全部書いてやりました。

視覚的に分かりやすく、Light/Dark も比較できます。

【Jetpack Compose】テーマデフォルトの色 (baseline palette) を確認したい【AndroidStudio】

【Jetpack Compose】テーマデフォルトの色 (baseline palette) を確認したい【AndroidStudio】

以下、色味を確認するためだけのコード。


@Suppress("UNUSED_VARIABLE")
val default = listOf(

  // material3
  // light             dark
  Color(0xff6750a4), Color(0xffd0bcff), // colorPrimary
  Color(0xffffffff), Color(0xff381e72), // colorOnPrimary
  Color(0xffeaddff), Color(0xff4f378b), // colorPrimaryContainer
  Color(0xff21005d), Color(0xffeaddff), // colorOnPrimaryContainer
  Color(0xffd0bcff), Color(0xff6750a4), // colorPrimaryInverse
  Color(0xff625b71), Color(0xffccc2dc), // colorSecondary
  Color(0xffffffff), Color(0xff332d41), // colorOnSecondary
  Color(0xffe8def8), Color(0xff4a4458), // colorSecondaryContainer
  Color(0xff1d192b), Color(0xffe8def8), // colorOnSecondaryContainer
  Color(0xff7d5260), Color(0xffefb8c8), // colorTertiary
  Color(0xffffffff), Color(0xff492532), // colorOnTertiary
  Color(0xffffd8e4), Color(0xff633b48), // colorTertiaryContainer
  Color(0xff31111d), Color(0xffffd8e4), // colorOnTertiaryContainer
  Color(0xffb3261e), Color(0xfff2b8b5), // colorError
  Color(0xffffffff), Color(0xff601410), // colorOnError
  Color(0xfff9dedc), Color(0xff8c1d18), // colorErrorContainer
  Color(0xff410e0b), Color(0xfff2b8b5), // colorOnErrorContainer
  Color(0xff79747e), Color(0xff938f99), // colorOutline
  Color(0xfffffbfe), Color(0xff1c1b1f), // android:colorBackground
  Color(0xff1c1b1f), Color(0xffe6e1e5), // colorOnBackground
  Color(0xfffffbfe), Color(0xff1c1b1f), // colorSurface
  Color(0xff1c1b1f), Color(0xffe6e1e5), // colorOnSurface
  Color(0xffe7e0ec), Color(0xff49454f), // colorSurfaceVariant
  Color(0xff49454f), Color(0xffcac4d0), // colorOnSurfaceVariant
  Color(0xff313033), Color(0xffe6e1e5), // colorSurfaceInverse
  Color(0xfff4eff4), Color(0xff313033), // colorOnSurfaceInverse

  // material
  // light             dark
  Color(0xFF6200EE), Color(0xFFBB86FC), // primary
  Color(0xFF3700B3), Color(0xFF3700B3), // primaryVariant
  Color(0xFFFFFFFF), Color(0xFF000000), // onPrimary
  Color(0xFF03DAC6), Color(0xFF03DAC6), // secondary
  Color(0xFF018786), Color(0xFF03DAC6), // secondaryVariant
  Color(0xFF000000), Color(0xFF000000), // onSecondary
  Color(0xFFFFFFFF), Color(0xFF121212), // background
  Color(0xFF000000), Color(0xFFFFFFFF), // onBackground
  Color(0xFFFFFFFF), Color(0xFF121212), // surface
  Color(0xFF000000), Color(0xFFFFFFFF), // onSurface
  Color(0xFFB00020), Color(0xFFCF6679), // error
  Color(0xFFFFFFFF), Color(0xFF000000), // onError

  // Color.Any
  Color(0xFF000000), // Color.Black,
  Color(0xFF444444), // Color.DarkGray
  Color(0xFF888888), // Color.Gray
  Color(0xFFCCCCCC), // Color.LightGray
  Color(0xFFFFFFFF), // Color.White
  Color(0xFFFF0000), // Color.Red
  Color(0xFF00FF00), // Color.Green
  Color(0xFF0000FF), // Color.Blue
  Color(0xFFFFFF00), // Color.Yellow
  Color(0xFF00FFFF), // Color.Cyan
  Color(0xFFFF00FF), // Color.Magenta
  Color(0x00000000), // Color.Transparent

)

プラグインとかねえかな。

👉 IntelliJ IDEA プラグイン「Rainbow Brackets」を使う hatena-bookmark
👉 @SuppressWarnings て使っていますか。 hatena-bookmark