[open source project sharing] a very simple and easy-to-use composite skeleton screen. Come and have a look!

Jetpack Compose is a new library released by Google at the 2009 Google I / O conference. It is a modern toolkit for building native Android UI. It has powerful tools and intuitive Kotlin API to simplify and accelerate UI development on Android. It can help developers create views with less and more intuitive code, have more powerful functions, and improve development speed.

Today I want to share with you a simple and easy-to-use composite skeleton screen. I think it's great to use. You can have a look. I hope it will be helpful to everyone's study and work.
(PS: open source address at the end of the text)

preface

The skeleton screen is a blank version of the page. Usually, before the page is fully rendered, it will roughly outline the outline through some gray blocks, and then replace it with the real content after the data is loaded.
The loading effect of skeleton screen can provide more information and better user experience than the traditional loading effect, so it has become more and more popular
This paper mainly introduces how to use Compose to realize a simple and easy-to-use skeleton screen effect

design sketch

First look at the final rendering

characteristic

  • Easy to use, reusable page UI, no need to customize UI for skeleton screen
  • It supports setting whether the skeleton screen is displayed, which is generally used in combination with the loading state
  • Support setting skeleton screen background and highlight color
  • It supports setting the height, width and gradient width of the skeleton screen
  • Support setting the angle and direction of skeleton screen animation
  • Support setting the time and interval of skeleton screen animation

use

Access

Step 1: in the project build Add to gradle:

allprojects {
	repositories {
		...
		mavenCentral()
	}
}

Step 2: build in the application Add to gradle:

dependencies {
        implementation 'io.github.shenzhen2017:shimmer:1.0.0'
}

Simple use

@Composable
fun ShimmerSample() {
    var loading: Boolean by remember {
        mutableStateOf(true)
    }
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .shimmer(loading,config = ShimmerConfig())
    ) {
        repeat(3) {
            PlaceHolderItem()
            Spacer(modifier = Modifier.height(10.dp))
        }
    }
}

As shown above:

  • Just add shimmer to the Modifier of the Column, and all components under the Column can achieve the skeleton screen effect
  • You can control whether the skeleton screen effect is displayed through the loading parameter
  • If you need to customize the skeleton screen animation effect, you can also configure it through some parameters

The main parameters are as follows

data class ShimmerConfig(
    // Unhighlight color
    val contentColor: Color = Color.LightGray.copy(alpha = 0.3f),
    // Highlight color
    val higLightColor: Color = Color.LightGray.copy(alpha = 0.9f),
    // Gradient width
    @FloatRange(from = 0.0, to = 1.0)
    val dropOff: Float = 0.5f,
    // Highlight width
    @FloatRange(from = 0.0, to = 1.0)
    val intensity: Float = 0.2f,
    //Skeleton screen animation direction
    val direction: ShimmerDirection = ShimmerDirection.LeftToRight,
    //Animation rotation angle
    val angle: Float = 20f,
    //Animation duration
    val duration: Float = 1000f,
    //Interval between animations
    val delay: Float = 200f
)

Main principle

Reuse page UI through image blending mode

If we want to achieve the skeleton screen effect, we first think that we need to write another set of UI according to the page structure, and then display this set of UI when loading, otherwise it will be hidden

The general loading effect is realized in this way, but this will bring a problem. Different page structures are different, so don't we rewrite a set of UI for one page? This is clearly unacceptable

We can think of the structure of the page. In fact, we have written it once. Wouldn't it be good if we could reuse the page structure we wrote?

We can do this through image blending mode

Image blending mode defines the final display of images when two images are combined. In Androd, there is a corresponding API interface to support image blending mode, namely Xfermode

There are mainly 16 image blending modes. The following picture vividly illustrates the role of image blending to a certain extent. Two graphics, one circle and one side, produce different combination effects through certain calculations, as follows

We will introduce some commonly used. Other interested students can refer to them by themselves

  • SRC_IN: draw [source image] only where the source image and target image intersect
  • DST_IN: draw the [target image] only at the intersection of the source image and the target image. The drawing effect is affected by the transparency of the corresponding place of the source image
  • SRC_OUT: only draw the [source image] where the source image and the target image do not intersect. The intersecting places are filtered according to the alpha of the corresponding place of the target image. If the target image is completely opaque, it is completely filtered, and if it is completely transparent, it is not filtered
  • DST_OUT: draw the [target image] only at the place where the source image and the target image do not intersect. Filter according to the alpha of the source image at the intersection. If the source image is completely opaque, it will be completely filtered, and if it is completely transparent, it will not be filtered

If we take the UI structure of the page as the target image and the skeleton screen effect as the source image, then use SRC_IN mixed mode, the skeleton screen can be displayed only on the structure of the page and not in the blank part, so as to avoid repeated writing of the UI

Animation by panning

Above, we have realized that the skeleton screen is displayed on the page structure, but the skeleton screen effect also has an animation effect
In fact, it's also very simple. Set a gradient effect for the skeleton screen, and then make a translation animation, and then it looks like the flash animation of the skeleton screen

fun Modifier.shimmer(): Modifier = composed {
    var progress: Float by remember { mutableStateOf(0f) }
    val infiniteTransition = rememberInfiniteTransition()
    progress = infiniteTransition.animateFloat().value  // Animation effects, calculating percentages
    ShimmerModifier(visible = visible, progress = progress, config = config)
}

internal class ShimmerModifier(progress:Float) : DrawModifier, LayoutModifier {
    private val paint = Paint().apply {
        blendMode = BlendMode.SrcIn //Set blend mode
        shader = LinearGradientShader(Offset(0f, 0f),toOffset,colors,colorStops)//Set gradient
    }

    override fun ContentDrawScope.draw() {
        drawContent()
        val (dx, dy) = getOffset(progress) //Set the position of translation according to progress
        paint.shader?.postTranslate(dx, dy) // Translation operation
        it.drawRect(Rect(0f, 0f, size.width, size.height), paint = paint)//Draw skeleton screen effect
    }
}

As shown above, the main steps are:

  • Start the animation, get the current progress, and get the position of the current translation according to the progress
  • Set the background gradient color and blending mode of the skeleton screen
  • Draw skeleton screen effect

Custom skeleton screen effect

As described above, we provide some parameters to customize the effect of the skeleton screen. Other parameters are easy to understand, mainly because the following two parameters are a little difficult to understand

  • dropOff: gradient section width
  • intensity: highlight width

We know that you can customize the color of the normal part through contentColor and the color of the highlighted part through higLightColor
But how are the two colors distributed? What is the scale of the gradient? You can see the following code:

    private val paint = Paint().apply {
        shader = LinearGradientShader(Offset(0f, 0f),toOffset,colors,colorStops)//Set gradient
    }

    private val colors = listOf(
        config.contentColor,
        config.higLightColor,
        config.higLightColor,
        config.contentColor
    )

    private val colorStops: List<Float> = listOf(
        ((1f - intensity - dropOff) / 2f).coerceIn(0f, 1f),
        ((1f - intensity - 0.001f) / 2f).coerceIn(0f, 1f),
        ((1f + intensity + 0.001f) / 2f).coerceIn(0f, 1f),
        ((1f + intensity + dropOff) / 2f).coerceIn(0f, 1f)
    )

It can be seen that our color gradient has the following characteristics:

  • The gradient color distribution is: contentcolor - > higlightcolor - > higlightcolor - > contentcolor
  • LinearGradientShader uses colors to define colors, colorStops to define the distribution of color gradients, and colorStops is calculated from intensity and dropoff
  • Intensity determines the width of the highlighted part, that is, the greater the intensity, the larger the highlighted part
  • dropOff determines the width of the gradient part, that is, the larger the dropOff, the larger the gradient part

summary

Special Thanks

In the process of realizing the skeleton screen of Compose version, the following open source framework ideas are mainly used for reference. Interested students can also learn about it
Facebook's open source shimmer Android
Habib Kazemi's open source compose Shimmer

Project address

Easy to use composite skeleton screen
Open source is not easy. If the project is helpful to you, welcome to like, Star and collect~

Sorting of learning materials

Organize the general catalogue


Chapter 1 getting to know Jetpack Compose and sorting out information

  1. Why do we need a new UI tool?

  2. Jetpack Compose focus
    Accelerate development
    Powerful UI tools
    Intuitive Kotlin API

  3. API design

  4. Principles of Compose API
    Everything is a function
    Top level function
    Combination is better than inheritance
    Trust a single source

  5. Learn more about Compose
    Core
    Foundation
    Material

  6. Slot API

Chapter 2 Jetpack Compose builds Android UI and organizes information

  1. Android Jetpack Compose most complete Getting Started Guide
    Jetpack Compose environment preparation and Hello World
    layout
    Design with Material design
    Composite layout live preview
    ......

  2. In depth explanation of jetpack composition | optimizing UI construction
    Problems solved by Compose
    Analysis of Composable function
    Declarative UI
    Combination vs inheritance
    encapsulation
    recombination
    ......

  3. Thoroughly explain the implementation principle of Jetpack Compose |
    @What does the Composable annotation mean?
    Execution mode
    Positional memory
    Storage parameters
    recombination
    ......

Chapter III Jetpack Compose project actual combat drill (with Demo) sorting information

  1. Jetpack Compose app 1
    Preparation before start
    Create DEMO
    Problems encountered

  2. Jetpack Compose app 2

  3. The Jetpack Compose application is used as a countdown timer
    data structure
    Countdown function
    State mode
    Compose layout
    Draw clock

  4. Write an android App with Jetpack Compose
    preparation
    Introduce dependency
    New Activity
    Create composite
    PlayTheme
    Draw page
    Bottom navigation bar
    Management status
    Add page

  5. Write a weather application with Compose Android
    Opening
    Draw page
    Painting background
    Painting content
    ......

  6. Quickly create a "movie App" with Compose
    finished product
    Implementation scheme
    actual combat
    Insufficient
    ......

Friends in need can[ Scan QR code below ]Get it directly from me for free.

I hope this material can be a reference for small partners who want to understand, learn and apply Android Jetpack Compose.

Keywords: Android Design Pattern Flutter jetpack UI

Added by kidintraffic on Wed, 15 Dec 2021 00:59:53 +0200