Landscapist

Jetpack Compose image loading library for requesting and displaying images using Glide, Fresco.

94184001-414d4800-fede-11ea-8801-cd0c997024df

Glide

Download

And add a dependency code to your module's build.gradle file.

dependencies {
    implementation "com.github.skydoves:landscapist-glide:1.0.3"
}

Usage

We can request and load images simply using a GlideImage composable function.

GlideImage(
  imageModel = imageUrl,
  // Crop, Fit, Inside, FillHeight, FillWidth, None
  contentScale = ContentScale.Crop,
  // shows a placeholder imageAsset when loading.
  placeHolder = imageResource(R.drawable.placeholder),
  // shows an error imageAsset when the request failed.
  error = imageResource(R.drawable.error)
)

RequestOptions and TransitionOptions

We can customize our request options using RequestOptions and TransitionOptions for applying caching strategies, loading transformations.

GlideImage(
  imageModel = poster.poster,
  requestOptions = RequestOptions()
    .override(256, 256)
    .diskCacheStrategy(DiskCacheStrategy.ALL)
    .centerCrop(),
  transitionOptions = BitmapTransitionOptions.withCrossFade(), 
  contentScale = ContentScale.Crop,
  modifier = modifier,
  alignment = Alignment.Center,
  contentScale = ContentScale.Crop,
)

RequestBuilder

Also we can request image by passing a RequestBuilder. RequestBuilder is the backbone of the request in Glide and is responsible for bringing your options together with your requested url or model to start a new load.

GlideImage(
  requestBuilder =
  Glide
    .with(ContextAmbient.current)
    .asBitmap()
    .load(poster.poster)
    .apply(RequestOptions().diskCacheStrategy(DiskCacheStrategy.ALL))
    .thumbnail(0.1f)
    .transition(withCrossFade()),
  modifier = Modifier.constrainAs(image) {
    centerHorizontallyTo(parent)
    top.linkTo(parent.top)
  }.aspectRatio(0.8f)
)

94174887-d8ab9e80-fed0-11ea-9f21-a6ed2b899339--1--1

Composable loading, success, failure

We can create our own composable functions following requesting states.

Here is an example that shows a progress indicator when loading an image,

After complete requesting, the indicator will be gone and a content image will be shown.

And if the request failed (e.g. network error, wrong destination), error text will be shown.

 GlideImage(
 imageModel = poster.poster,
 modifier = modifier,
 // shows a progress indicator when loading an image.
 loading = {
   ConstraintLayout(
     modifier = Modifier.fillMaxSize()
   ) {
     val indicator = createRef()
     CircularProgressIndicator(
       modifier = Modifier.constrainAs(indicator) {
         top.linkTo(parent.top)
         bottom.linkTo(parent.bottom)
        start.linkTo(parent.start)
        end.linkTo(parent.end)
       }
     )
   }
 },
 // shows an error text message when request failed.
 failure = {
   Text(text = "image request failed.")
 })

Fresco

Download

And add a dependency code to your module's build.gradle file.

dependencies {
    implementation "com.github.skydoves:landscapist-fresco:<version>"
}

Initialize

We should initialize Fresco using ImagePipelineConfig in our Application class.

If we need to fetch images from the network, recommend using OkHttpImagePipelineConfigFactory.

By using an ImagePipelineConfig, we can customize caching, networking, and thread pool strategies.

Here are more references related to the pipeline config.

class App : Application() {

  override fun onCreate() {
    super.onCreate()

    val pipelineConfig =
      OkHttpImagePipelineConfigFactory
        .newBuilder(this, OkHttpClient.Builder().build())
        .setDiskCacheEnabled(true)
        .setDownsampleEnabled(true)
        .setResizeAndRotateEnabledForNetwork(true)
        .build()

    Fresco.initialize(this, pipelineConfig)
  }
}

94174882-d6e1db00-fed0-11ea-86ec-671b5039b1b9

Usage

We can request and load images simply using a FrescoImage composable function.

FrescoImage(
  imageUrl = stringImageUrl,
  // Crop, Fit, Inside, FillHeight, FillWidth, None
  contentScale = ContentScale.Crop,
  // shows a placeholder imageAsset when loading.
  placeHolder = imageResource(R.drawable.placeholder),
  // shows an error imageAsset when the request failed.
  error = imageResource(R.drawable.error)
)

We can customize our requests using an ImageRequest that consists only of a URI, we can use the helper method ImageRequest.fromURI.

val imageRequest = ImageRequestBuilder
  .newBuilderWithSource(uri)
  .setImageDecodeOptions(decodeOptions)
  .setLocalThumbnailPreviewsEnabled(true)
  .setLowestPermittedRequestLevel(RequestLevel.FULL_FETCH)
  .setProgressiveRenderingEnabled(false)
  .setResizeOptions(ResizeOptions(width, height))
  .build()

FrescoImage(
  imageUrl = stringImageUrl,
  imageRequest = imageRequest,
  contentScale = ContentScale.Crop)

94174882-d6e1db00-fed0-11ea-86ec-671b5039b1b9

Composable loading, success, failure

We can create our own composable functions following requesting states.

Here is an example that shows a progress indicator when loading an image,

After complete requesting, the indicator will be gone and a content image will be shown.

And if the request failed (e.g. network error, wrong destination), error text will be shown.

 FrescoImage(
 imageUrl = stringImageUrl,
 modifier = modifier,
 // shows a progress indicator when loading an image.
 loading = {
   ConstraintLayout(
     modifier = Modifier.fillMaxSize()
   ) {
     val indicator = createRef()
     CircularProgressIndicator(
       modifier = Modifier.constrainAs(indicator) {
         top.linkTo(parent.top)
         bottom.linkTo(parent.bottom)
        start.linkTo(parent.start)
        end.linkTo(parent.end)
       }
     )
   }
 },
 // shows an error text message when request failed.
 failure = {
   Text(text = "image request failed.")
 })

Also, we can customize the content image using our own composable function like below.

FrescoImage(
    imageUrl = imageUrl,
    // draw a resized image.
    success = { frescoImageState ->
      frescoImageState.imageAsset?.let {
        Image(
          asset = it,
          modifier = Modifier
            .width(128.dp)
            .height(128.dp))
      }
    },
    loading = { 
      // do something 
    })

Disable lint checking

94366110-cae45c00-0110-11eb-893e-b8a47d45f172

Landscapist uses ExperimentalCoroutinesApi internally.

So if you want to remove the IDE lint checking, add @ExperimentalCoroutinesApi annotation to your function.

And by adding below kotlin options in our build.gradle, we can remove all lint checkings.

kotlinOptions {
    freeCompilerArgs += [
        "-Xallow-jvm-ir-dependencies",
        "-Xskip-prerelease-check",
        "-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi"]
  }

GitHub