Compose AndroidView, ComposeView, Pager(ViewPager效果),SwipeRefresh(下拉刷新)

3,475 阅读4分钟

该篇文章我们会介绍AndroidView,ComposeView,Pager以及SwipeRefresh的使用。

一:AndroidView (Compose中使用View)

当我们想要在Compose中去使用View系统,那么可以使用AndroidView。先来看看它的代码:

@Composable
fun <T : View> AndroidView(
    factory: (Context) -> T,
    modifier: Modifier = Modifier,
    update: (T) -> Unit = NoOpUpdate
){...}
  • factory 想要创建的View
  • modifier 修饰符
  • update 对View的更新 直接举例子,代码如下:
@Preview
@Composable
fun androidViewTest(){
    Column() {
        Text(text = "我是Compose的Text")
        AndroidView(
            factory = {
                // 这里是用于初始化View
                val textView = TextView(it)
                textView}
            ,
            modifier = Modifier.background(Color.Blue)) {
            // 这里是用于更新view,因为文本有可能在业务中设置多次,所以写在更新部分
            it.text = "我是Android原生的TextView"
            it.setTextColor(android.graphics.Color.WHITE)
        }
    }
}

显示的效果如下:

iamge2.jpg

二:ComposeView (View中去使用Compose)

当我们想要在View系统中去使用Compose的时候,我们可以使用ComposeView。举例如下: 一个MainActivity,代码如下:

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 = "我是Compose的Text")
        }
    }
}

一个布局文件:activity_main_test.xml代码如下:

<?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="我是View系统的TextView"
        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>

实现的效果如下: image.jpg

三:Pager (ViewPager效果)

Compose中并没有找到ViewPager效果的控件,不过在官方的例子中有Pager这个控件。我们直接拿来使用,官方的Pager的地址:Pager的代码官方地址 我们想要使用Pager需要导入包,在app的build.gradle中加入如下代码:

// 我们这里使用的是0.12.0的版本
implementation "com.google.accompanist:accompanist-pager:0.12.0"

accompanist现在最新的版本是0.12.0,accompanist下有一些很多官方提供的控件,accompanist的github的地址,包括后面要讲的下拉刷新控件,也是再accompanist该项目里面。 接下来我们继续讲Pager。主要是有两个控件,一个是HorizontalPager和VerticalPager。我们先来看看它们的代码:

@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
    )
}
  • state Pager的状态。PagerState通过rememberPagerState获得。而rememberPagerState有几个参数。具体代码如下:
    @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
    )
    
    • pageCount 页数
    • initialPage 初始在第几页,默认0
    • initialPageOffset 偏移量 默认0
    • initialOffscreenLimit 是预加载的页数 默认是1
    • infiniteLoop 是否支持无限循环的效果
  • modifier 修饰符
  • reverseLayout 是否反着 默认false
  • itemSpacing 每个item的间隔 默认0dp
  • dragEnabled 是否可以滑动 默认true
  • flingBehavior
  • verticalAlignment 竖直方向的对齐方式 默认居中
  • horizontalAlignment 水平方向的对齐方式 默认居中
  • content 内容。是一个PagerScope。 举例:浏览图片的例子
@ExperimentalPagerApi
@Preview
@Composable
fun pagerTest(){
    val images = listOf<String>(
        "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=670316187,1943310392&fm=26&gp=0.jpg",
        "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3285777805,2966380382&fm=26&gp=0.jpg",
        "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3002379740,3965499425&fm=26&gp=0.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 = "图片",
                    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))
    }
}

这里说明一下,以前我们的Image讲解的文章里面使用网络图片用的是CoilImage,因为那时候用的accompanist库的版本是0.7.1。。这次用到的版本是最新的0.12.0。在0.12.0里已经没有CoilImage了,换成了一个rememberCoilPainter方法去使用。
上面例子的效果如下:

xxx.jpg

四:SwipeRefresh (下拉刷新)

SwipeRefresh是添加下拉刷新的控件。要使用该控件,需要下app的build.gradle里添加如下的引用:

// 我们这里使用的是0.12.0的版本
implementation "com.google.accompanist:accompanist-swiperefresh:0.12.0"

我们这里使用的版本是0.12.0。来具体看看代码:

@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,
) {...}
  • state 刷新的状态 通过rememberSwipeRefreshState获取。
  • onRefresh 刷新的回调
  • modifier 修饰符
  • swipeEnabled 下拉刷新是否能够使用 默认是true
  • refreshTriggerDistance 触发刷新的最小距离,默认是88dp
  • indicatorAlignment 刷新时那个刷新控件的位置。默认是顶部居中(可以修改成TopStart试试就知道了)
  • indicatorPadding 刷新控件的边距。(从展示上看可以理解成初始显示的位置,默认是0)
  • indicator 刷新控件的样式。默认实现是SwipeRefreshIndicator(转圈的效果),当然这块完全可以自定义成我们想要的效果。这里第一个参数是state 是刷新状态,第二个参数refreshTrigger就是触发下拉刷新的最小距离,也就是refreshTriggerDistance传入的值。
  • clipIndicatorToPadding 为true的话,刷新控件在indicatorPadding的位置会被遮挡,false的话则不会被遮挡
  • content 内容 举例代码如下:
@Preview
@Composable
fun refreshTest(){
    val isRefreshing = remember() {
        mutableStateOf(false)
    }
    val state = rememberSwipeRefreshState(isRefreshing = isRefreshing.value)
    val scope = rememberCoroutineScope()
    SwipeRefresh(
        state = state,
        onRefresh = {
            isRefreshing.value = true
            // 延迟500毫秒假装是加载数据,加载完数据之后,在把刷新的值改为false
            scope.launch {
                delay(500)
                isRefreshing.value = false
            }
        }
    ) {
        LazyColumn() {
            items(100) { index ->
                Text("I'm item $index", modifier = Modifier
                    .fillMaxWidth()
                    .padding(16.dp))
            }
        }
    }
}

效果如下: refresh.jpg 我们还可以通过修改SwipeRefreshIndicator的入参来修改刷新控件的样式。先来看看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
){...}
  • state 刷新状态
  • refreshTriggerDistance 触发刷新的最小距离
  • modifier 修饰符
  • fade 是否淡入淡出效果
  • scale 是否缩放效果
  • arrowEnabled 是否应在指示器上绘制箭头
  • backgroundColor 背景颜色
  • contentColor 内容颜色
  • shape 形状
  • refreshingOffset 刷新的偏移
  • largeIndication 是否是大的指示器
  • elevation 指示器下方阴影的大小 举例:比如我们可以把上面的转圈的刷新控件,背景颜色改成红色,内容颜色改成白色。代码如下:
@Preview
@Composable
fun refreshTest(){
    val isRefreshing = remember() {
        mutableStateOf(false)
    }
    val state = rememberSwipeRefreshState(isRefreshing = isRefreshing.value)
    val scope = rememberCoroutineScope()
    SwipeRefresh(
        state = state,
        onRefresh = {
            isRefreshing.value = true
            // 延迟500毫秒假装是加载数据,加载完数据之后,在把刷新的值改为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))
            }
        }
    }
}