This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money

preface

Pull-down refresh is a common requirement in our development. SwipeRefreshLayout is officially provided to implement pull-down refresh, but we often need to customize the Header or scroll down the Header with the content. So SwipeRefreshLayout doesn’t always fit our needs. There are open source libraries on Github such as SmartRefreshLayout that do pull down refresh when developing with XML. SmartRefreshLayout for Compose SmartRefreshLayout for Compose SmartRefreshLayout for Compose SmartRefreshLayout for Compose SmartRefreshLayout

rendering

Let’s start with the final renderings

The basic use The custom Header
Lottie Header I’m FixedBehind.
FixedFront FixedContent

features

  1. Easy to access, easy to use, quickly realize the drop-down refresh function
  2. Support customizationHeader.HeaderThe drop-down status can be observed and updatedUI
  3. The customHeadersupportLottieAnd support to observe the start of the pull-down state with the pause animation
  4. Support customizationTranslate.FixedBehind.FixedFront.FixedContentEqual rolling mode
  5. Support andPagingCombined with the realization of sliding loading more functions

use

Access to the

Step 1: In your project build.gradle add:

allprojects {
	repositories {
		...
		mavenCentral()
	}
}
Copy the code

Step 2: In your application’s build.gradle add:

dependencies {
        implementation 'the IO. Making. Shenzhen2017: compose - refreshlayout: 1.0.0'
}
Copy the code

Simple to use

The SwipeRefreshLayout function contains the following parameters:

  1. isRefreshing: Indicates whether the system is refreshing
  2. onRefresh: Triggers the refresh callback
  3. modifier: Style modifier
  4. swipeStyle: Drop-down refresh mode
  5. swipeEnabled: Indicates whether pull-down refresh is allowed
  6. refreshTriggerRate: Refresh the effective height andindicatorHeight ratio
  7. maxDragRate: Maximum refresh distance andindicatorHeight ratio
  8. indicator: user-definedindicator, has default values

By default, we simply pass in isRefreshing(isRefreshing) and onRefresh triggers the refresh callback

@Composable
fun BasicSample(a) {
    var refreshing by remember { mutableStateOf(false) }
    LaunchedEffect(refreshing) {
        if (refreshing) {
            delay(2000)
            refreshing = false
        }
    }
    SwipeRefreshLayout(isRefreshing = refreshing, onRefresh = { refreshing = true{})/ /...}}Copy the code

As shown above: Setting refreshing to true when the refresh callback is triggered and false when the refresh is complete enables a simple pull-down refresh

The customHeader

SwipeRefreshLayout supports passing in custom headers, as shown below:

@Composable
fun CustomHeaderSample(a) {
    var refreshing by remember { mutableStateOf(false) }
    LaunchedEffect(refreshing) {
        if (refreshing) {
            delay(2000)
            refreshing = false
        }
    }

    SwipeRefreshLayout(
        isRefreshing = refreshing,
        onRefresh = { refreshing = true },
        indicator = {
            BallRefreshHeader(state = it)
        }) {
        	/ /...}}Copy the code

As shown in the preceding figure, BallRefreshHeader is a user-defined Header, and the SwipeRefreshState is passed in the Header. The following parameters can be obtained using the SwipeRefreshState

  1. isRefreshing: Indicates whether the system is refreshing
  2. isSwipeInProgress: Indicates whether the file is rolling
  3. maxDrag: Maximum pull-down distance
  4. refreshTrigger: Refreshes the trigger distance
  5. headerState: Refreshes the status, includingPullDownToRefresh.Refreshing.ReleaseToRefreshThree state
  6. indicatorOffset: HeaderThe offset

All of these parameters are MutableState and we can watch them change in order to update the Header UI

The customLottile Header

Compose now supports Lottie, and once we add Lottie dependencies to Compose, we can easily implement a Lottie Header and play the animation while refreshing and pause the animation at other times, as shown in the following example:

@Composable
fun LottieHeaderOne(state: SwipeRefreshState) {
    var isPlaying by remember {
        mutableStateOf(false)}val speed by remember {
        mutableStateOf(1f)
    }
    isPlaying = state.isRefreshing
    val lottieComposition by rememberLottieComposition(
        spec = LottieCompositionSpec.RawRes(R.raw.refresh_one),
    )
    val lottieAnimationState by animateLottieCompositionAsState(
        composition = lottieComposition, // Animation resource handle
        iterations = LottieConstants.IterateForever, // Number of iterations
        isPlaying = isPlaying, // Animation playback state
        speed = speed, // Animation speed state
        restartOnPlay = false // Whether to restart after pausing from the beginning
    )
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .wrapContentHeight(), contentAlignment = Alignment.Center
    ) {
        LottieAnimation(
            lottieComposition,
            lottieAnimationState,
            modifier = Modifier.size(150.dp)
        )

    }
}
Copy the code

Custom slide mode

SwipeRefreshLayout supports the following four slide modes

enum class SwipeRefreshStyle {
    Translate,  // Pan, that is, the content slides down with the Header. Translate is the default style
    FixedBehind, // Fixed behind, i.e. contents slide down, Header does not move
    FixedFront, // The Header is fixed in front, and neither Header nor Content is sliding
    FixedContent // The content is fixed, and the Header slides down, which is the official style
}
Copy the code

As shown above, the default mode is Translate, i.e. slide the content down with the Header. You can select the appropriate slide mode according to your requirements. For example, to achieve an official slide effect, use FixedContent

Pull up to load more

In Compose, using Paging3 directly seems to be sufficient, so the library doesn’t implement much of the pull-up functionality. Therefore, if you want to do more pull-up, you can use Paging3 yourself

The main principle of

The drop-down refresh function, in fact, is mainly a nested scroll problem, we put Header and Content in a parent layout unified management, and then need to do the following

  1. When we scroll our fingers down, first handContentProcess, ifContentScroll to the top, then the parent layout, and the parent layout according to the gesture offset, increaseoffset
  2. When we let go, determine the offset distance, trigger the refresh if it is greater than the refresh trigger distance, otherwise rebound to the top (offsetSet to zero)
  3. When we scroll our finger up, the parent layout handles it first, if the parent layoutoffset>0 is handled by the parent layout and reducedoffset, otherwise, byContentConsumption gestures

NestedScrollConnectionintroduce

Compose provides NestedScrollConnection for nested scrolling. For example, Compose provides NestedScrollConnection for nested scrolling

interface NestedScrollConnection {
    fun onPreScroll(available: Offset, source: NestedScrollSource): Offset = Offset.Zero

    fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset = Offset.Zero

    suspend fun onPreFling(available: Velocity): Velocity = Velocity.Zero

    suspend fun onPostFling(consumed: Velocity, available: Velocity) = return Velocity.Zero
}
Copy the code

As shown above, NestedScrollConnection mainly provides four interfaces

  1. onPreScroll: Intercepts the slide event, consumes it and passes it to the child layout
  2. onPostScroll: The child layout handles the slide event and then hands it to the parent layout to get how many offsets of the slide event are currently available
  3. onPreFling: FlingStart the pre-pullback
  4. onPostFling: FlingCallback after completion

It is not a Fling. OnPreFling is not a Fling where your finger is not in the air but in the air. It is not a Fling where your finger is not in the air but in the air.

The specific implementation

Having covered the general idea with the NestedScrollConnection API above, we should then need to override the following methods

  1. onPostScroll: whenContentAs we slide to the top, if we keep sliding up, we should add the parent layoutoffset, so inonPostScrollIn the judgmentavailable.y > 0And then offset it accordingly, which is a good time for us
  2. onPreScroll: When we slide up, ifoffset>0, indicates that the parent layout is offset, so we should first reduce the parent layoutoffsetUp to zero, and then pass the rest of the offset toContent, so it should be used when slidingonPreScrollIntercept judgment
  3. onPreFling: When we release the hand, we should determine whether the current offset is greater than the refresh trigger distance, if it is greater than the refresh trigger, otherwise the parent layoutoffsetSet it to 0, and that’s whereonPreFlingIt would be more appropriate to do it

The concrete implementation is as follows:

internal class SwipeRefreshNestedScrollConnection() : NestedScrollConnection {
    override fun onPreScroll(
        available: Offset,source: NestedScrollSource
    ): Offset = when {
        // If the user is sliding up, you need to intercept processing here
        source == NestedScrollSource.Drag && available.y < 0 -> onScroll(available)
        else -> Offset.Zero
    }

    override fun onPostScroll(
        consumed: Offset,available: Offset,source: NestedScrollSource
    ): Offset = when {
        // If the user is pulling down, process the remaining offsets here
        source == NestedScrollSource.Drag && available.y > 0 -> onScroll(available)
        else -> Offset.Zero
    }

    override suspend fun onPreFling(available: Velocity): Velocity {
        // If the offset is greater than the refresh trigger distance, the refresh is triggered
        if(! state.isRefreshing && state.indicatorOffset >= refreshTrigger) { onRefresh() }// Return 0 without consuming speed
        return Velocity.Zero
    }
}
Copy the code

conclusion

This article describes how to use and implement a Compose version of SmartRefreshLayout with the following features:

  1. Easy to access, easy to use, quickly realize the drop-down refresh function
  2. Support customizationHeader.HeaderThe drop-down status can be observed and updatedUI
  3. The customHeadersupportLottieAnd support to observe the start of the pull-down state with the pause animation
  4. Support customizationTranslate.FixedBehind.FixedFront.FixedContentEqual rolling mode
  5. Support andPagingCombined with the realization of sliding loading more functions

The project address

For Compose, SmartRefreshLayout is not easy to open source. If the project is helpful to you, please like it