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】rememberCoroutineScope() vs LaunchedEffect

Jetpack Compose で非同期処理を行う場合、どっちを使うべきか。

👉 android - Using rememberCoroutineScope() vs LaunchedEffect - Stack Overflow hatena-bookmark

悩ましい問題です。

2度押しボタン対策3秒待ちで考えてみます。

 

rememberCoroutineScope()


val scope = rememberCoroutineScope()
var enabled1 by remember { mutableStateOf(true) }

Button(
  onClick = {
    scope.launch {
      enabled1 = false
      delay(3000)
      enabled1 = true
    }
  },
  enabled = enabled1
) {
  Text("Button1")
}


起動

 ↓

ボタンを表示

 ↓

ボタンをクリック

 ↓

CoroutineScope.launch が実行される

   ↓

  enabled1 が false に更新されて3秒開始

   ↓

  ボタンが更新され無効化される

   ↓

  3秒経ったら enabled1 が true に更新される

 ↓

ボタンが更新され有効化される

@Composable のスコープでは書けない。


Calls to launch should happen inside a LaunchedEffect and not composition
→ @SuppressLint("CoroutineCreationDuringComposition")

実行のタイミングをユーザー操作に直接的に紐付けれる。

 

LaunchedEffect


var enabled2 by remember { mutableStateOf(true) }

Button(
  onClick = {
    enabled2 = false
  },
  enabled = enabled2
) {
  Text("Button2")
}

LaunchedEffect(key1 = enabled2) {
  if (!enabled2) {
    delay(3000)
    enabled2 = true
  }
}


起動

 ↓

ボタンを表示

 ↓

LaunchedEffect が実行される

   ↓

  enable2 が true なので何もしない

 ↓

ボタンをクリック

 ↓

enabled2 が false に更新される

 ↓

ボタンが更新され無効化される

 ↓

LaunchedEffect が key1 の変更により実行される

   ↓

  enabled2 が false なので3秒開始

   ↓

  3秒経ったら enabled2 が true に更新される

   ↓

ボタンが更新され有効化される

 ↓

LaunchedEffect が key1 の変更により実行される

   ↓

  enable2 が true なので何もしない

@Composable スコープで書く。

re/compose か キーの変化 で実行される。

 

まとめ

SideEffect系 は、今後、苦労することになると思う。

ViewModel を使ったほうが直感的でないか?

ライフサイクルはも合ってるし、

どっちみち、実際は、ViewModel → Repository の経路をたどるし。


// Screen-level Composable

val enabled3 by viewModel.enabled

Button(
  onClick = {
    viewModel.click()
  },
  enabled = enabled3
) {
  Text("Button3")
}


// ViewModel

private val _enabled = mutableStateOf(true)
val enabled: State<Boolean> = _enabled

fun click() {
  viewModelScope.launch {
    _enabled.value = false
    delay(3000) // request to repository suspend function
    _enabled.value = true
  }
}

肥大化していますよね、その Composable。

StateHolder としての ViewModel も考慮に入れてみるのもいいかもしれません。

👉 【Jetpack Compose】NavBackStackEntry - Composable のライフサイクルと ViewModel の状態を確認する hatena-bookmark
👉 【Jetpack Compose】 「Layout Inspector Recomposition counts」で re-compose 回数を確認する hatena-bookmark
👉 【Jetpack Compose】よくあるボタンの有効化/無効化 hatena-bookmark


【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