Android Compose实现伸缩ToolBar的思路详解

目录
  • ScrollableAppBar
    • 效果图
    • 主要思路
      • 布局预览
      • 实现过程

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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2021-10-13

Android顶部(toolbar)搜索框实现的实例详解

Android顶部(toolbar)搜索框实现的实例详解 本文介绍两种SearchView的使用情况,一种是输入框和搜索结果不在一个activity中,另一种是在一个activity中. 首先编写toolbar的布局文件 toolbar中图标在menu文件下定义一个布局文件实现 示例代码: <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.

Android中ActionBar和ToolBar添加返回箭头的实例代码

 1.ActionBar添加返回箭头 //onCreate方法中 ActionBar actionBar = this.getSupportActionBar(); actionBar.setTitle("搜索功能"); actionBar.setDisplayHomeAsUpEnabled(true); //activity类中的方法 @Override public boolean onOptionsItemSelected(MenuItem item) { if(item.get

Android Toolbar自定义标题标题居中的实例代码

自定义Toolbar,实现方式如下: 1.布局文件,在activity_main.xml  文件中写入Toolbar <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools=&q

Android ToolBar控件详解及实例

ToolBar控件详解 在Activity中添加ToolBar 1.添加库 dependencies { ... compile "com.android.support:appcompat-v7:18.0.+" } 2.Activity要继承AppCompatActivity 3.设置主题 使用ToolBar,要将系统默认的ActionBar隐藏掉 <application android:theme="@style/Theme.AppCompat.Light.NoA

Android ToolBar 修改边距的实现方法

Android ToolBar 修改边距的实现方法 效果图: 实现方式: <android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/menuToolbar" android:l

Android动态修改ToolBar的Menu菜单示例

Android动态修改ToolBar的Menu菜单 效果图 实现 实现很简单,就是一个具有3个Action的Menu,在我们滑动到不同状态的时候,把对应的Action隐藏了. 开始上货 Menu Menu下添加3个Item <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xml

Android编程动态修改RelativeLayout宽高的方法

本文实例讲述了Android编程动态修改RelativeLayout宽高的方法.分享给大家供大家参考,具体如下: 我们经常会动态修改RelativeLayout的宽高,这样的代码,比较简单,就是修改Relativelayout的LayoutParams就可以.代码一般如下: RelativeLayout ss = (RelativeLayout) findViewById(R.id.myRelativeLayout); ss.setLayoutParams(new RelativeLayout.

Android实现编程修改手机静态IP的方法

本文实例讲述了Android实现编程修改手机静态IP的方法.分享给大家供大家参考.具体如下: 这里演示通过编程方式动态修改手机静态IP的方法,可以用于wifi接入点切换 public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ContentResolver cr = this.getContentResolver();

Android简单修改原有应用和添加应用的方法

本文实例讲述了Android简单修改原有应用和添加应用的方法.分享给大家供大家参考,具体如下: 第一部分:如何修改原有的Android应用 1. 首先新建一个Android工程,加载自己的Java文件和资源文件. 2. 在自己的android工程初步调试成功以后,把该工程的java文件和资源文件复制到联系人对应的java文件和资源文件目录. 3. 在linux下执行以下命令,编译人联系人模块,把资源文件编译到联系人目录中 . build/envsetup.sh mmm packages/apps

Android中修改设备权限的方法

本文实例讲述了Android中修改设备权限的方法.分享给大家供大家参考.具体如下: 有时我们编写了驱动后,在上层程序中要访问设备,但android代码编译后的设备权限是root的,其他用户不可访问(包括system),只是就需要在android源码中将设备的权限修改下. 具体的修改位置为源码的system/core/init/devices.c文件中static struct perms_ devperms[]的定义中,如添加设备hidraw0的权限,只需添加一行: 复制代码 代码如下: { "

Android 实现夜间模式的快速简单方法实例详解

ChangeMode 项目地址:ChangeMode Implementation of night mode for Android. 用最简单的方式实现夜间模式,支持ListView.RecyclerView. Preview Usage xml android:background="?attr/zzbackground" app:backgroundAttr="zzbackground"//如果当前页面要立即刷新,这里传入属性名称 比如 R.attr.zzb

Android实现图片叠加效果的两种方法

本文实例讲述了Android实现图片叠加效果的两种方法.分享给大家供大家参考,具体如下: 效果图: 第一种: 第二种: 第一种是通过canvas画出来的效果: public void first(View v) { // 防止出现Immutable bitmap passed to Canvas constructor错误 Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.apple).copy(Bi

详解Android通过修改配置文件设置wifi密码

详解Android通过修改配置文件设置wifi密码 前言: 在一些非常规Android设备上,如眼镜/手表,输入wifi密码如同一场灾难.此时可以通过修改配置文件的方法设置wifi的ssid和密码. wifi密码配置文件 首先要保证设备已经root,wifi的配置文件在/data/misc/wifi/wpa_supplicant.conf,可以先将其pull出来,然后在下面加上network开头的那部分就ok了.然后再导入进去.下面是我的配置文件: ##### wpa_supplicant co

Android实现模仿UCweb菜单效果的方法

本文实例讲述了Android实现模仿UCweb菜单效果的方法.分享给大家供大家参考.具体如下: UCWeb的菜单看起来不错,自己模仿做一个,思路实现如下: 1.保留menu按键作用 2.用popupwindow作为菜单显示容器 3.用GridView显示所有子菜单 代码如下: 1.布局文件: popupwindow.xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:and