In this article we will introduce the use of AndroidView, ComposeView, Pager and SwipeRefresh.

One: AndroidView (Compose using View)

When you want to use the View system in Compose, you can use AndroidView instead. Let’s take a look at the code:

@Composable
fun <T : View> AndroidView(
    factory: (Context) - >T,
    modifier: Modifier = Modifier,
    update: (T) - >Unit = NoOpUpdate
){... }Copy the code
  • The View that factory wants to create
  • Modifier modifier
  • Update Update to the View

For example, here’s the code:

@Preview
@Composable
fun androidViewTest(a){
    Column() {
        Text(text = "I'm Compose's Text.")
        AndroidView(
            factory = {
                // This is used to initialize the View
                val textView = TextView(it)
                textView}
            ,
            modifier = Modifier.background(Color.Blue)) {
            // This is used to update the view, because the text can be set multiple times in the business, so it is written in the update section
            it.text = "I'm an Android native TextView."
            it.setTextColor(android.graphics.Color.WHITE)
        }
    }
}
Copy the code

The display looks like this:

2: ComposeView (use Compose in View)

When we want to use Compose in the View system, we can use ComposeView. For example: a MainActivity with the following code:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main_test)
        
        val composeView = findViewById<ComposeView>(R.id.compose_view)
        composeView.setContent { 
            Text(text = "I'm Compose's Text.")}}}Copy the code

A layout file: activity_main_test.xml looks like this:

<? xml version="1.0" encoding="utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:text="I'm the TextView of the View system."
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>
Copy the code

The results are as follows:

Pager (ViewPager effect)

Compose does not have a ViewPager effect control, but Pager is available in the official example. To use Pager, we need to import the package and add the following code to our app build.gradle:

// We are using version 0.12.0 here
implementation "Com. Google. Accompanist: accompanist - pager: 0.12.0"
Copy the code

The latest version of FIRST-chair is 0.12.0, and first-chair provides many official controls. The Github address for FIRST-chair, including the drop down refresh control, is also for the second chair. Let’s move on to Pager. There are mainly two controls, one is HorizontalPager and VerticalPager. Let’s look at their code first:

@ExperimentalPagerApi
@Composable
fun HorizontalPager(
    state: PagerState,
    modifier: Modifier = Modifier,
    reverseLayout: Boolean = false,
    itemSpacing: Dp = 0.dp,
    @IntRange(from = 1) offscreenLimit: Int = 1,
    dragEnabled: Boolean = true,
    flingBehavior: FlingBehavior = PagerDefaults.defaultPagerFlingConfig(state),
    verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
    horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
    content: @Composable PagerScope.(page: Int) - >Unit,
) {
    Pager(
        state = state,
        modifier = modifier,
        isVertical = false,
        reverseLayout = reverseLayout,
        itemSpacing = itemSpacing,
        verticalAlignment = verticalAlignment,
        horizontalAlignment = horizontalAlignment,
        offscreenLimit = offscreenLimit,
        dragEnabled = dragEnabled,
        flingBehavior = flingBehavior,
        content = content
    )
}

@ExperimentalPagerApi
@Composable
fun VerticalPager(
    state: PagerState,
    modifier: Modifier = Modifier,
    reverseLayout: Boolean = false,
    itemSpacing: Dp = 0.dp,
    dragEnabled: Boolean = true,
    flingBehavior: FlingBehavior = PagerDefaults.defaultPagerFlingConfig(state),
    verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
    horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
    content: @Composable PagerScope.(page: Int) - >Unit,
) {
    Pager(
        state = state,
        modifier = modifier,
        isVertical = true,
        reverseLayout = reverseLayout,
        itemSpacing = itemSpacing,
        verticalAlignment = verticalAlignment,
        horizontalAlignment = horizontalAlignment,
        dragEnabled = dragEnabled,
        flingBehavior = flingBehavior,
        content = content
    )
}
Copy the code
  • State Indicates the Pager status. PagerState is available through rememberPagerState. RememberPagerState has several parameters. The specific code is as follows:
    @ExperimentalPagerApi
    @Composable
    fun rememberPagerState(
      @IntRange(from = 0) pageCount: Int.@IntRange(from = 0) initialPage: Int = 0.@floatrange (from = 0.0, to = 1.0) initialPageOffset: Float = 0f,
      @IntRange(from = 1) initialOffscreenLimit: Int = 1,
      infiniteLoop: Boolean = false
    )
    Copy the code
    • PageCount pages
    • InitialPage On which page it starts, defaults to 0
    • InitialPageOffset Indicates the offset. The default value is 0
    • InitialOffscreenLimit is the number of pages preloaded. The default is 1
    • InfiniteLoop supports the effect of an infiniteLoop
  • Modifier modifier
  • ReverseLayout defaults to false
  • ItemSpacing defaults to 0dp for each item
  • DragEnabled Whether it can slide by default true
  • flingBehavior
  • VerticalAlignment verticalAlignment is in the middle by default
  • HorizontalAlignment horizontalAlignment is centered by default
  • The content of content. It’s a PagerScope.

Example: An example of browsing pictures

@ExperimentalPagerApi
@Preview
@Composable
fun pagerTest(a){
    val images = listOf<String>(
        "Https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=670316187, & FM = 26 & gp = 0. 1943310392 JPG"."Https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3285777805, & FM = 26 & gp = 0. 2966380382 JPG"."Https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3002379740, & FM = 26 & gp = 0. 3965499425 JPG")
    val pagerState = rememberPagerState(pageCount = images.size)
    val painter = rememberCoilPainter(request = images[pagerState.currentPage],previewPlaceholder = R.drawable.ic_launcher_background,fadeIn = true)
    Box(modifier = Modifier
        .fillMaxSize()
        .background(Color.Black),contentAlignment = Alignment.BottomCenter) {
        HorizontalPager(
            modifier = Modifier.fillMaxSize(),
            state = pagerState
        ) {
            Box() {
                Image(
                    modifier = Modifier.fillMaxWidth(),
                    painter = painter,
                    contentDescription = "Image",
                    contentScale = ContentScale.FillWidth)
                when(painter.loadState){
                    is ImageLoadState.Loading->{
                        CircularProgressIndicator(Modifier.align(Alignment.Center))
                    }
                }
            }
        }
        Text(text = "${pagerState.currentPage+1}/${pagerState.pageCount}",color = Color.White,fontSize = 20.sp,modifier = Modifier.padding(bottom = 10.dp))
    }
}
Copy the code

I would like to clarify that CoilImage was used for the network pictures in the previous article about Image, because the version of CSIST library is 0.7.1 at that time. The version used this time is the latest 0.12.0. There is no more CoilImage in 0.12.0 and a rememberCoilPainter method is used instead. The above example has the following effect:

4: SwipeRefresh (drop down refresh)

SwipeRefresh is the control to add a drop-down refresh. To use this control, add the following reference to your app’s build.gradle:

// We are using version 0.12.0 here
implementation "Com. Google. Accompanist: accompanist - swiperefresh: 0.12.0"
Copy the code

The version we are using here is 0.12.0. Let’s look at the code in detail:

@Composable
fun SwipeRefresh(
    state: SwipeRefreshState,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier,
    swipeEnabled: Boolean = true,
    refreshTriggerDistance: Dp = 80.dp,
    indicatorAlignment: Alignment = Alignment.TopCenter,
    indicatorPadding: PaddingValues = PaddingValues(0.dp),
    indicator: @Composable (state: SwipeRefreshState, refreshTrigger: Dp) -> Unit = { s, trigger ->
        SwipeRefreshIndicator(s, trigger)
    },
    clipIndicatorToPadding: Boolean = true,
    content: @Composable() - >Unit,
) {...}
Copy the code
  • State the refresh status Through rememberSwipeRefreshState access.
  • OnRefresh callback to refresh
  • Modifier modifier
  • SwipeEnabled Whether the swipeEnabled drop-down refresh can be used The default value is true
  • RefreshTriggerDistance Specifies the minimum distance to trigger the refresh. The default is 88dp
  • IndicatorAlignment Indicates the refreshing control alignment position. The default is top centered (you can change it to TopStart)
  • IndicatorPadding Indicates the margin of the refresh control. (The default position is 0)
  • Indicator Refreshes the style of the control. The default implementation is the SwipeRefreshIndicator, which can be customized to the desired effect. The first parameter is state, which is the refresh state, and the second parameter refreshTrigger is the minimum distance that the refreshTriggerDistance is passed in.
  • If clipIndicatorToPadding is true, the refresh control at the indicatorPadding position will be blocked. If it is false, the refresh control will not be blocked
  • The content content

Example code is as follows:

@Preview
@Composable
fun refreshTest(a){
    val isRefreshing = remember() {
        mutableStateOf(false)}val state = rememberSwipeRefreshState(isRefreshing = isRefreshing.value)
    val scope = rememberCoroutineScope()
    SwipeRefresh(
        state = state,
        onRefresh = {
            isRefreshing.value = true
            // Delay 500 ms pretending to load data, after loading data, change the refreshed value to false
            scope.launch {
                delay(500)
                isRefreshing.value = false
            }
        }
    ) {
        LazyColumn() {
            items(100) { index ->
                Text("I'm item $index", modifier = Modifier
                    .fillMaxWidth()
                    .padding(16.dp))
            }
        }
    }
}
Copy the code

The effect is as follows:We can also change the style of the refresh control by modifying the input parameter of the SwipeRefreshIndicator. Take a look at the code for SwipeRefreshIndicator:

@Composable
fun SwipeRefreshIndicator(
    state: SwipeRefreshState,
    refreshTriggerDistance: Dp,
    modifier: Modifier = Modifier,
    fade: Boolean = true,
    scale: Boolean = false,
    arrowEnabled: Boolean = true,
    backgroundColor: Color = MaterialTheme.colors.surface,
    contentColor: Color = contentColorFor(backgroundColor),
    shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
    refreshingOffset: Dp = 16.dp,
    largeIndication: Boolean = false,
    elevation: Dp = 6.dp ){... }Copy the code
  • State Refresh status
  • RefreshTriggerDistance Specifies the minimum distance to trigger the refresh
  • Modifier modifier
  • Does it fade in or out
  • Scale Specifies whether to scale
  • ArrowEnabled Whether arrows should be drawn on the indicator
  • BackgroundColor backgroundColor
  • ContentColor the contentColor
  • Shape shape
  • RefreshingOffset Specifies the offset for refreshing
  • LargeIndication Is a large indicator
  • Elevation The size of the shadow below the elevation indicator

For example, we could change the background color of the circle refresh control above to red and the content color to white. The code is as follows:

@Preview
@Composable
fun refreshTest(a){
    val isRefreshing = remember() {
        mutableStateOf(false)}val state = rememberSwipeRefreshState(isRefreshing = isRefreshing.value)
    val scope = rememberCoroutineScope()
    SwipeRefresh(
        state = state,
        onRefresh = {
            isRefreshing.value = true
            // Delay 500 ms pretending to load data, after loading data, change the refreshed value to false
            scope.launch {
                delay(500)
                isRefreshing.value = false} }, indicator= { state, refreshTrigger -> SwipeRefreshIndicator(state,refreshTrigger,backgroundColor = Color.Red,contentColor = Color.White) } )  { LazyColumn() { items(100) { index ->
                Text("I'm item $index", modifier = Modifier
                    .fillMaxWidth()
                    .padding(16.dp))
            }
        }
    }
}
Copy the code