该篇文章我们会介绍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)
}
}
}
显示的效果如下:
二: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>
实现的效果如下:
三: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方法去使用。
上面例子的效果如下:
四: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))
}
}
}
}
效果如下:
我们还可以通过修改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))
}
}
}
}