首頁 > 軟體

Android Compose實現伸縮ToolBar的思路詳解

2021-10-14 16:03:57

ScrollableAppBar

效果圖

  • 當列表向上移動時,會先帶動ToolBar向上位移,等ToolBar向上移動到最大位移量時列表向上滑動
  • 當列表向下移動時,會先帶動ToolBar向下位移,等ToolBar向下移動到最大位移量時列表向下滑動

主要思路

佈局預覽

伸縮前佈局:

伸縮後佈局:

實現過程

佈局實現

首先我們要定義兩個尺寸變數

// 應用欄高度
private val toolBarHeight = 56.dp
// 導航圖示大小
private val navigationIconSize = 50.dp

我們採用Box作為根佈局,裡面主要包含三個部分,背景圖片,頂部的TooBar以及下面的Title部分,其實現如下

//整體佈局實現
Box(modifier = Modifier
        .height(scrollableAppBarHeight) //scrollableAppBarHeight 為高度引數,為外部獲取
        .fillMaxWidth()
) {
    Image(painter = painterResource(id = backgroundImageId), contentDescription = "background", contentScale = ContentScale.FillBounds)

    // 自定義應用欄
    Row(
        modifier = modifier
            .height(toolBarHeight) //設定高度為toolBarHeight
            .fillMaxWidth(),
        verticalAlignment = Alignment.CenterVertically //設定垂直方向為居中對齊
    ) {
        // 導航圖示
        Box(modifier = Modifier.size(navigationIconSize),contentAlignment = Alignment.Center) {
            navigationIcon()
        }
    }

    // title定義
    Box(
        modifier = Modifier
            .height(toolBarHeight) //和ToolBar同高
            .fillMaxWidth()
            .align(Alignment.BottomStart),
        contentAlignment = Alignment.CenterStart
    ) {
        Text(text = title,
            color = Color.White,
            modifier = Modifier.padding(start = 20.dp).matchParentSize(), // 使用 matchParentSize 修飾符保證不影響父 Box尺寸
            fontSize = 20.sp
        )
    }
}

我們主要講解title部分

// title定義
Box(
    modifier = Modifier
        .height(toolBarHeight) //和ToolBar同高
        .fillMaxWidth()
        .align(Alignment.BottomStart),
    contentAlignment = Alignment.CenterStart
) {
    Text(text = title,
            color = Color.White,
            modifier = Modifier.padding(start = 20.dp).matchParentSize(), // 使用 matchParentSize 修飾符保證不影響父 Box尺寸
            fontSize = 20.sp
        )
}

首先為了保證title部分在完全收縮後高度和toolBar部分一致,我們設定Box佈局高度為toolBarHeight

modifier = Modifier
        .height(toolBarHeight) //和ToolBar同高
        .fillMaxWidth()

然後定義Box在根佈局裡面的對齊方式為Alignment.BottomStart

modifier = Modifier
        .height(toolBarHeight) //和ToolBar同高
        .fillMaxWidth()
        .align(Alignment.BottomStart)

之所以這樣設定,是因為我們通過觀察伸縮前和伸縮後的預覽圖可以知道如果保證此部分是底部左邊對齊,那麼在根佈局向上移動的過程中我們便可以只關心此部分在水平方向的位移即可

接著設定文字部分的對齊方式,保證title是居中靠左對齊的

contentAlignment = Alignment.CenterStart

位移實現

首先,我們要明確ScrollableAppBar最大向上偏移量等於其定義的高度收縮後的高度,即toolBarHeight的差值,即:

// 應用欄最大向上偏移量
val maxOffsetHeightPx = with(LocalDensity.current) { scrollableAppBarHeight.roundToPx().toFloat() - toolBarHeight.roundToPx().toFloat() }

其次,title部分在水平方向的位移距離其實就是導航圖示的寬度,即:

// Title 偏移量參考值
val titleOffsetWidthReferenceValue = with(LocalDensity.current) { navigationIconSize.roundToPx().toFloat() }

同時需要定義從外部獲取到的偏移量

val toolbarOffsetHeightPx: MutableState<Float> //向上偏移量

最外層佈局位移定義

為根佈局新增垂直方向上的位移

@Composable
fun ScrollableAppBar(
    modifier: Modifier = Modifier,
    title: String = stringResource(id = R.string.app_name), //預設為應用名
    navigationIcon: @Composable () -> Unit, //導航圖示
    @DrawableRes backgroundImageId: Int, // 背景圖片
    scrollableAppBarHeight: Dp, //定義的ScrollableAppBar高度
    toolbarOffsetHeightPx: MutableState<Float> //向上偏移量
) {
    Box(modifier = Modifier
        .height(scrollableAppBarHeight)
        .offset {
            IntOffset(
              x = 0,
              y = toolbarOffsetHeightPx.value.roundToInt() //設定偏移量
            )
        }
        .fillMaxWidth()
    ) {
        .... // 背景圖等內容
    }
}

toolBar垂直方向位置不變的實現

設定和父佈局相反的位移量保證toolBar處於原位置,即:

// 自定義應用欄
Row(
    modifier = modifier
        .offset {
            IntOffset(
                x = 0,
                y = -toolbarOffsetHeightPx.value.roundToInt() //保證應用欄是始終不動的
            )
        }
        .height(toolBarHeight)
        .fillMaxWidth(),
    verticalAlignment = Alignment.CenterVertically
) {
    ... // 導航圖示
}

title水平位移的實現

為了保證title均勻向右位移,用根佈局此時向上位移量和最大位移量的商再乘以水平方向上的總位移即可:

x = -((toolbarOffsetHeightPx.value / maxOffsetHeightPx) * titleOffsetWidthReferenceValue).roundToInt()

完整實現

// title部分
Box(
    modifier = Modifier
        .height(toolBarHeight) //和ToolBar同高
        .fillMaxWidth()
        .align(Alignment.BottomStart)
        .offset {
            IntOffset(
                x = -((toolbarOffsetHeightPx.value / maxOffsetHeightPx) * titleOffsetWidthReferenceValue).roundToInt(), //水平方向位移
                y = 0
            )
        },
    contentAlignment = Alignment.CenterStart
) {
    ... //title部分
}

專案地址

ScrollableAppBar 如果專案對你有所幫助,如果有改進意見還可以提交 issue

到此這篇關於Android Compose實現伸縮ToolBar的思路詳解的文章就介紹到這了,更多相關Android 伸縮ToolBar內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com