Jetpack Compose LeadingIconTab,Tab,TabRow,ScrollableTabRow的用法讲解

2,880 阅读4分钟

这篇文章会介绍Tab,LeadingIconTab,TabRow,以及ScrollableTabRow的用户。TabRow跟ScrollableTabRow就类似于以前View系统的TabLayout。只不过TabRow是不可滑动的,而ScrollableTabRow是可滑动的。而Tab就是TabRow跟ScrollableTabRow使用的Item的控件。LeadingIconTab跟Tab的区别就是LeadingIconTab是带Icon的。

一:Tab

首先来看看Tab的代码:

@Composable
fun Tab(
    selected: Boolean,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    selectedContentColor: Color = LocalContentColor.current,
    unselectedContentColor: Color = selectedContentColor.copy(alpha = ContentAlpha.medium),
    content: @Composable ColumnScope.() -> Unit
){
    ...
}
  • selected 是否选中
  • onClick 点击事件
  • modifier 修饰符 Modifier的讲解
  • enabled 是否可用
  • interactionSource 可以处理状态的,比如按下的时候什么效果,正常时候什么效果。类似之前再布局文件里写Selector。 比如我们下面的例子中设置,如果是选中时候边框线的颜色是绿色,没有选中时候是黑色。 interactionSource.collectIsPressedAsState() 判断是否按下状态interactionSource.collectIsFocusedAsState() 判断是否获取焦点的状态interactionSource.collectIsDraggedAsState() 判断是否拖动
    我们讲button的时候讲过,Button的讲解
  • selectedContentColor 选中时候的颜色
  • unselectedContentColor 没有选中时候的颜色
  • content 内容 举例:Tab的按下时候是字体颜色是红色,选中时候字体颜色也是红色。我们的例子中,最终Tab会跟TabRow一起使用,所以我们先给出Tab部分的代码。
@Composable
fun tabView(index:Int,text:String,tabIndex:MutableState<Int>){
    val interactionSource = remember {
        MutableInteractionSource()
    }
    val isPress = interactionSource.collectIsPressedAsState().value
    Tab(
        selected = index == tabIndex.value,
        onClick = {
            tabIndex.value = index
        },
        modifier = Modifier
            .wrapContentWidth()
            .fillMaxHeight(),
        enabled =true,
        interactionSource = interactionSource,
        selectedContentColor = Color.Red,
        unselectedContentColor = Color.Black
    ) {
        Text(text = text,color = if(isPress || index == tabIndex.value) Color.Red else Color.Black)
    }
}

二:LeadingIconTab

先来看看LeadingIconTab的代码

@ExperimentalMaterialApi
@Composable
fun LeadingIconTab(
    selected: Boolean,
    onClick: () -> Unit,
    text: @Composable (() -> Unit),
    icon: @Composable (() -> Unit),
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    selectedContentColor: Color = LocalContentColor.current,
    unselectedContentColor: Color = selectedContentColor.copy(alpha = ContentAlpha.medium)
){...}
  • selected 是否选中
  • onClick 点击事件
  • text 文本控件
  • icon Icon控件
  • modifier 修饰符
  • interactionSource 处理状态的类跟上面一致
  • selectedContentColor 选中的颜色
  • unselectedContentColor 没有选中时候的颜色 举例:Tab的按下时候是字体颜色跟Icon是红色,选中时候字体颜色和Icon也是红色。我们的例子中,最终LeadingIconTab会跟ScrollableTabRow一起使用,所以我们先给出LeadingIconTab部分的代码。
@ExperimentalMaterialApi
@Composable
fun leadingIconTabView(index:Int,text:String,tabIndex:MutableState<Int>){
    val interactionSource = remember {
        MutableInteractionSource()
    }
    val isPress = interactionSource.collectIsPressedAsState().value
    val imageVector = when(index){
        0-> Icons.Filled.Home
        1-> Icons.Filled.Shop
        2-> Icons.Filled.Favorite
        3-> Icons.Filled.School
        4-> Icons.Filled.ShoppingBag
        5-> Icons.Filled.DesktopMac
        6-> Icons.Filled.Message
        7-> Icons.Filled.FmdGood
        8-> Icons.Filled.Domain
        else ->Icons.Filled.Person
    }
    LeadingIconTab(
        selected = index == tabIndex.value,
        onClick = {
            tabIndex.value = index
        },
        text = {
            Text(text = text,color = if(isPress || index == tabIndex.value) Color.Red else Color.Black)
        },
        icon = {
            Icon(imageVector, contentDescription = "icon图标",tint = if(isPress || index == tabIndex.value) Color.Red else Color.Black)
        },
        modifier = Modifier.wrapContentWidth().fillMaxHeight().background(Color.White),
        enabled = true,
        interactionSource = interactionSource,
        selectedContentColor = Color.Red,
        unselectedContentColor = Color.Black
    )
}

三:TabRow

先来看看TabRow的代码

@Composable
fun TabRow(
    selectedTabIndex: Int,
    modifier: Modifier = Modifier,
    backgroundColor: Color = MaterialTheme.colors.primarySurface,
    contentColor: Color = contentColorFor(backgroundColor),
    indicator: @Composable (tabPositions: List<TabPosition>) -> Unit = @Composable { tabPositions ->
        TabRowDefaults.Indicator(
            Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
        )
    },
    divider: @Composable () -> Unit = @Composable {
        TabRowDefaults.Divider()
    },
    tabs: @Composable () -> Unit
){
...
}
  • selectedTabIndex 选中的index位置
  • modifier 修饰符
  • backgroundColor 背景颜色
  • contentColor 内容颜色
  • indicator 选中的下面那条线 默认是实现是TabRowDefaults.Indicator。我们来看看TabRowDefaults.Indicator的代码
    @Composable
      fun Indicator(
          modifier: Modifier = Modifier,
          height: Dp = IndicatorHeight,
          color: Color = LocalContentColor.current
      ) {
          Box(
              modifier
                  .fillMaxWidth()
                  .height(height)
                  .background(color = color)
          )
      }
    
    • modifier修饰符
    • height 高度
    • color 颜色 所以其实我们可以自定义
  • divider tabRow下面那条线的 这个是一个Divider 默认实现是TabRowDefaults.Divider()。Divider的用法我们前面文章讲过 Divider用法
  • tabs tabRow的内容
@Preview()
@Composable
fun tabRowTest(){
    val tabIndex = remember {
        mutableStateOf(0)
    }
    val tabDatas = ArrayList<String>().apply {
        add("语文")
        add("数学")
        add("英语")
    }
    TabRow(
        selectedTabIndex = tabIndex.value,
        modifier = Modifier
            .fillMaxWidth()
            .height(50.dp),
        backgroundColor = Color.Green,
        contentColor = Color.Black,
        divider = {
            TabRowDefaults.Divider()
        },
        indicator = {
            TabRowDefaults.Indicator(
                Modifier.tabIndicatorOffset(it[tabIndex.value]),
                color = Color.Blue,
                height = 2.dp
            )
        }
    ) {
        tabDatas.forEachIndexed{
                index, s ->
            tabView(index,s,tabIndex)
        }
    }
}

@Composable
fun tabView(index:Int,text:String,tabIndex:MutableState<Int>){
    val interactionSource = remember {
        MutableInteractionSource()
    }
    val isPress = interactionSource.collectIsPressedAsState().value
    Tab(
        selected = index == tabIndex.value,
        onClick = {
            tabIndex.value = index
        },
        modifier = Modifier
            .wrapContentWidth()
            .fillMaxHeight(),
        enabled =true,
        interactionSource = interactionSource,
        selectedContentColor = Color.Red,
        unselectedContentColor = Color.Black
    ) {
        Text(text = text,color = if(isPress || index == tabIndex.value) Color.Red else Color.Black)
    }
}

其实TabRow默认是Item是会平分TabRow的宽度。而ScrollableTabRow的Item会是自适应宽度的类型

四:ScrollableTabRow

先来看看ScrollableTabRow的代码

@Composable
fun ScrollableTabRow(
    selectedTabIndex: Int,
    modifier: Modifier = Modifier,
    backgroundColor: Color = MaterialTheme.colors.primarySurface,
    contentColor: Color = contentColorFor(backgroundColor),
    edgePadding: Dp = TabRowDefaults.ScrollableTabRowPadding,
    indicator: @Composable (tabPositions: List<TabPosition>) -> Unit = @Composable { tabPositions ->
        TabRowDefaults.Indicator(
            Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
        )
    },
    divider: @Composable () -> Unit = @Composable {
        TabRowDefaults.Divider()
    },
    tabs: @Composable () -> Unit
){
...}
  • selectedTabIndex 选中的index
  • modifier修饰符
  • backgroundColor背景颜色
  • contentColor内容颜色
  • edgePadding 是ScrollableTabRow开头和结尾的留白的的大小 默认是54dp
  • indicator 选中时候下面那条线 跟上面一致
  • divider ScrollableTabRow下面的线 跟上面一致
  • tabs ScrollableTabRow的内容 举例有9个tab。tab是带Icon的LeadingIconTab,选中和按下的时候图标和文字变红。
@ExperimentalMaterialApi
@Preview()
@Composable
fun scrollableTabRowTest(){
    val tabIndex = remember {
        mutableStateOf(0)
    }
    val tabDatas = ArrayList<String>().apply {
        add("第1tab")
        add("第2tab")
        add("第3tab")
        add("第4tab")
        add("第5tab")
        add("第6tab")
        add("第7tab")
        add("第8tab")
        add("第9tab")
    }
    ScrollableTabRow(
        selectedTabIndex = tabIndex.value,
        modifier = Modifier
            .fillMaxWidth()
            .height(50.dp),
        backgroundColor = Color.White,
        contentColor = Color.Black,
        edgePadding = 10.dp,
        divider = {
            TabRowDefaults.Divider(color = Color.Red)
        },
        indicator = {
            TabRowDefaults.Indicator(
                Modifier.tabIndicatorOffset(it[tabIndex.value]),
                color = Color.Blue,
                height = 2.dp
            )
        },
        tabs = {
            tabDatas.forEachIndexed{
                    index, s ->
                leadingIconTabView(index,s,tabIndex)
            }
        })
}


@ExperimentalMaterialApi
@Composable
fun leadingIconTabView(index:Int,text:String,tabIndex:MutableState<Int>){
    val interactionSource = remember {
        MutableInteractionSource()
    }
    val isPress = interactionSource.collectIsPressedAsState().value
    val imageVector = when(index){
        0-> Icons.Filled.Home
        1-> Icons.Filled.Shop
        2-> Icons.Filled.Favorite
        3-> Icons.Filled.School
        4-> Icons.Filled.ShoppingBag
        5-> Icons.Filled.DesktopMac
        6-> Icons.Filled.Message
        7-> Icons.Filled.FmdGood
        8-> Icons.Filled.Domain
        else ->Icons.Filled.Person
    }
    LeadingIconTab(
        selected = index == tabIndex.value,
        onClick = {
            tabIndex.value = index
        },
        text = {
            Text(text = text,color = if(isPress || index == tabIndex.value) Color.Red else Color.Black)
        },
        icon = {
            Icon(imageVector, contentDescription = "icon图标",tint = if(isPress || index == tabIndex.value) Color.Red else Color.Black)
        },
        modifier = Modifier.wrapContentWidth().fillMaxHeight().background(Color.White),
        enabled = true,
        interactionSource = interactionSource,
        selectedContentColor = Color.Red,
        unselectedContentColor = Color.Black
    )
}