《Jetpack Compose从入门到实战》第九章 Accompanist 与第三方组件库

news2024/11/27 19:53:41

在这里插入图片描述

目录

  • Accompanist
    • SystemUiController
    • Pager
    • SwipeRefresh
    • Flow Layout
    • Insets
  • Lottie
  • Coil
    • AsyncImage
    • SubcomposeAsyncImage
    • AsyncImagePainter

Accompanist

  • 最新可用版本
  • accompanist官方文档

SystemUiController

  • 依赖:implementation “com.google.accompanist:accompanist-systemuicontroller:<version>”
@Composable
fun SystemUiTest() {
    Box(modifier = Modifier.fillMaxSize()) {

        val systemUiController = rememberSystemUiController()
        val useDarkIcons = MaterialTheme.colors.isLight
        SideEffect {
            systemUiController.setSystemBarsColor(color = Color.Transparent, darkIcons = useDarkIcons)
        }
//        TopAppBar(title = { Text(text = "TopAppBar") }, modifier = Modifier.statusBarsPadding(), backgroundColor = Color.Gray)
        //使用com.google.accompanist:accompanist-insets-ui:0.30.1 实现沉浸式状态栏的效果
        TopAppBar(title = { Text(text = "TopAppBar") }, backgroundColor = Color.Gray, contentPadding = WindowInsets.statusBars.asPaddingValues())

    }
}
@Composable
fun SystemUiControllerDemo() {
    val systemUiController = rememberSystemUiController()
    val useDarkIcons = MaterialTheme.colors.isLight

    val colorPanel = listOf(
        Color.Gray,
        Color.Red,
        Color.Black,
        Color.Cyan,
        Color.Transparent,
        Color.DarkGray,
        Color.LightGray,
        Color.Yellow
    )

    SideEffect {
        systemUiController.setSystemBarsColor(Color.Transparent, useDarkIcons)
    }
    Column(Modifier.systemBarsPadding().fillMaxSize().background(Color(0xFF0079D3))) {
        colorPanel.forEach {
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(48.dp)
                    .background(it)
                    .clickable {
                        systemUiController.setSystemBarsColor(it, useDarkIcons)
                    }
            )
        }
    }
}

  • 略直接设置状态栏和底部导航栏颜色的方法

Pager

  • implementation “com.google.accompanist:accompanist-pager:$accompanist_version”
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PagerTest() {
    Box(modifier = Modifier.fillMaxSize()) {
        val pagerState = rememberPagerState()
        val scope = rememberCoroutineScope()
        HorizontalPager(pageCount = 3, modifier = Modifier.fillMaxSize(), state = pagerState) { page ->
            when (page) {
                0 -> ColorBox(color = Color.Blue, pageIndex = page)
                1 -> ColorBox(color = Color.Cyan, pageIndex = page)
                2 -> ColorBox(color = Color.Magenta, pageIndex = page)
            }
        }
        SideEffect {
            scope.launch {
                delay(3000)
                pagerState.scrollToPage(2)
            }
        }
    }
}

@Composable
fun ColorBox(color: Color, pageIndex: Int) {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(color = color), contentAlignment = Alignment.Center
    ) {
        Text(text = "page $pageIndex")
    }
}
  • 略直接跳转到某个页面的方法
@OptIn(ExperimentalPagerApi::class)
@Composable
fun PagerDemo() {

    val pagerState = rememberPagerState()
    val scope = rememberCoroutineScope()
    var selectedScreens by remember { mutableStateOf(0) }

    val screens = listOf(
        Screens("首页", Icons.Filled.Home) { Home() },
        Screens("我喜欢的", Icons.Filled.Favorite) { Favorite() },
        Screens("设置", Icons.Filled.Settings) { Settings() }
    )

    Scaffold(
        bottomBar = {
            BottomNavigationBar(
                selectedScreens,
                screens,
                onClick = {
                    selectedScreens = it
                    scope.launch { pagerState.scrollToPage(selectedScreens) }
                }
            )
        }
    ) {
        HorizontalPager(
            count = screens.size,
            modifier = Modifier.fillMaxSize(),
            state = pagerState
        ) { page ->
            screens.forEachIndexed { index, screens ->
                when (page) {
                    index -> screens.content()
                }
            }
        }
    }

    LaunchedEffect(pagerState) {
        snapshotFlow { pagerState.currentPage }.collect { page ->
            selectedScreens = page
        }
    }

}

@Composable
fun Home() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Gray),
        contentAlignment = Alignment.Center
    ) {
        Text(
            text = "1. 首页🤭",
            style = MaterialTheme.typography.h5
        )
    }
}

@Composable
fun Favorite() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color(0xFFF8F8F8)),
        contentAlignment = Alignment.Center
    ) {
        Text(
            text = "2. 我喜欢的❤",
            style = MaterialTheme.typography.h5
        )
    }
}
@Composable
fun Settings() {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Text(
            text = "3. 设置⚙",
            style = MaterialTheme.typography.h5
        )
    }
}

@Composable
fun BottomNavigationBar(
    selectedScreen: Int,
    screens: List<Screens>,
    onClick: (targetIndex: Int) -> Unit
) {
    NavigationBar {
        screens.forEachIndexed { index, screen ->
            NavigationBarItem(
                icon = { Icon(screen.iconVector, contentDescription = null) },
                label = { Text(screen.label) },
                selected = selectedScreen == index,
                onClick = { onClick(index) }
            )
        }
    }
}

data class Screens(
    val label: String,
    val iconVector: ImageVector,
    val content: @Composable () -> Unit
)

SwipeRefresh

  • implementation “com.google.accompanist:accompanist-swiperefresh:$accompanist_version”
@Preview
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun SwipeRefreshTest() {
    Box(modifier = Modifier.fillMaxSize()) {
        val viewModel: MyViewModel = viewModel()
        val isRefreshing by viewModel.isRefreshing.collectAsState()
        val background by animateColorAsState(targetValue = viewModel.background, animationSpec = tween(1000), label = "backgroundAnim")
        /* SwipeRefresh(state = rememberSwipeRefreshState(isRefreshing), onRefresh = { viewModel.refresh() }) {
             Box(
                 modifier = Modifier
                     .fillMaxSize()
                     .verticalScroll(rememberScrollState())
                     .background(background)
             )
         }*/
        val pullRefreshState = rememberPullRefreshState(refreshing = isRefreshing, onRefresh = { viewModel.refresh() })
        Box(
            modifier = Modifier
                .fillMaxSize()
                .pullRefresh(pullRefreshState)
                .verticalScroll(rememberScrollState())
                .background(background),
        ) {
            PullRefreshIndicator(isRefreshing, pullRefreshState, modifier = Modifier.align(Alignment.TopCenter))
        }
    }
}

class MyViewModel : ViewModel() {
    private val _isRefreshing = MutableStateFlow(false)
    private val colorPanel = listOf(Color.Gray, Color.Red, Color.Black, Color.Cyan, Color.DarkGray, Color.LightGray, Color.Yellow)
    val isRefreshing: StateFlow<Boolean>
        get() = _isRefreshing

    var background by mutableStateOf(Color.Gray)

    fun refresh() {
        viewModelScope.launch {
            _isRefreshing.emit(true)
            delay(1000)
            background = colorPanel.random()
            _isRefreshing.emit(false)
        }
    }

}

Flow Layout

  • 会自动换行的row和Column
  • implementation “com.google.accompanist:accompanist-flowlayout:$accompanist_version”
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun FlowLayoutTest() {
    Column(modifier = Modifier.fillMaxSize()) {
        FlowRow(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly, verticalAlignment = Alignment.CenterVertically, maxItemsInEachRow = 3) {
            Text(text = "text1")
            Text(text = "text2")
            Text(text = "text3")
            Text(text = "text4")
            Text(text = "text5")
            Text(text = "text6")
        }
        Divider(modifier = Modifier.fillMaxWidth())
        FlowColumn(modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.SpaceEvenly, horizontalAlignment = Alignment.CenterHorizontally, maxItemsInEachColumn = 3) {
            Text(text = "text1")
            Text(text = "text2")
            Text(text = "text3")
            Text(text = "text4")
            Text(text = "text5")
            Text(text = "text6")
        }
    }
}
@Composable
fun Tag(
    modifier: Modifier = Modifier,
    shape: Shape = CircleShape,
    elevation: Dp = 0.dp,
    leadingIcon: @Composable (() -> Unit)? = null,
    trailingIcon: @Composable (() -> Unit)? = null,
    text: String,
    textStyle: TextStyle = TextStyle(
        fontWeight = FontWeight.Normal,
        fontSize = 16.sp,
        letterSpacing = 0.15.sp
    ),
    backgroundColor: Color = Color(0xFFE8E8E8),
    border: BorderStroke? = null,
    onClick:() -> Unit
){

    Surface(
        shape = shape,
        color = backgroundColor,
        modifier = modifier,
        elevation = elevation,
        border = border
    ) {
        Row(
            modifier = Modifier
                .clickable(
                    onClick = onClick
                )
                .padding(start = 15.dp, end = 15.dp, top = 8.dp, bottom = 8.dp),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            when{
                leadingIcon != null -> {
                    CompositionLocalProvider(
                        LocalContentAlpha provides ContentAlpha.high,
                        content = leadingIcon
                    )
                    Spacer(Modifier.padding(horizontal = 4.dp))
                    Text(
                        text = text,
                        style = textStyle,
                    )
                }
                trailingIcon != null -> {
                    Text(
                        text = text,
                        style = textStyle,
                    )
                    Spacer(Modifier.padding(horizontal = 4.dp))
                    CompositionLocalProvider(
                        LocalContentAlpha provides ContentAlpha.high,
                        content = trailingIcon
                    )
                }
                else -> {
                    Text(
                        text = text,
                        style = textStyle,
                    )
                }
            }
        }
    }
}

@Composable
fun FlowLayoutDemo() {
    Surface(
        modifier = Modifier
            .systemBarsPadding()
            .fillMaxWidth(),
        elevation = 8.dp
    ) {
        FlowRow(
            modifier = Modifier.padding(8.dp),
            crossAxisSpacing = 12.dp,
            mainAxisSpacing = 10.dp
        ) {
            Tag(
                leadingIcon = {
                    Icon(painterResource(id = R.drawable.wechat), null, tint = Color.White)
                },
                text = "WeChat",
                elevation = 6.dp,
                textStyle = TextStyle(Color.White),
                backgroundColor = Color(0xFF07C160)
            ) { }
            Tag(
                leadingIcon = {
                    Icon(painterResource(id = R.drawable.twitter), null, tint = Color.White)
                },
                text = "Twitter",
                elevation = 6.dp,
                textStyle = TextStyle(Color.White),
                backgroundColor = Color(0xFF1DA1F2)
            ) { }
            Tag(
                leadingIcon = {
                    Icon(painterResource(id = R.drawable.github), null, tint = Color.White)
                },
                text = "Github",
                elevation = 6.dp,
                textStyle = TextStyle(Color.White),
                backgroundColor = Color(0xFF181717)
            ) { }
            Tag(
                leadingIcon = {
                    Icon(painterResource(id = R.drawable.microsoftedge), null, tint = Color(0xFF0078D7))
                },
                text = "Edge",
                elevation = 6.dp
            ) { }
            Tag(
                leadingIcon = {
                    Icon(painterResource(id = R.drawable.microsoft), null, tint = Color(0xFF5E5E5E))
                },
                text = "Microsoft",
                elevation = 6.dp
            ) { }
        }
    }
}

Insets

  • implementation “com.google.accompanist:accompanis-inss-ui:<version>”
@Composable
fun InsetsDemo() {
    val systemUiController = rememberSystemUiController()
    val useDarkIcons = MaterialTheme.colors.isLight

    SideEffect {
        systemUiController.setSystemBarsColor(Color.Transparent, useDarkIcons)
    }
    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    Text("TopAppBar")
                },
                backgroundColor = Color.Gray,
                contentPadding = WindowInsets.statusBars.asPaddingValues()
            )
        },
        modifier = Modifier.fillMaxSize(),
        contentColor = Color.Black
    ) { }
}

Lottie

  1. 添加依赖配置 implementation “com.airbnb.android:lottie-compose:$lottieVersion”
  2. 创建Lottie动画
  • 创建两个状态用来描述动画的速度和开始暂停状态
var isPlaying by remember {
        mutableStateOf(true)
    }
    var speed by remember {
        mutableStateOf(1f)
    }
  • 加载Lottie动画资源,这里用本地加载方式
    • Lottie框架提供了加载res/raw,加载URL,加载手机目录下的静态资源,加载asset目录下的静态资源,加载json字符串的功能
    val lottieComposition by rememberLottieComposition(
        spec = RawRes(R.raw.lottie),
    )
  • 接下来创建Lottie动画状态
    val lottieAnimationState by animateLottieCompositionAsState (
        composition = lottieComposition,
        iterations = LottieConstants.IterateForever,
        isPlaying = isPlaying,
        speed = speed,
        restartOnPlay = false
    )
  • 最后设置动画资源句柄和动画状态
 LottieAnimation(
                lottieComposition,
                lottieAnimationState,
                modifier = Modifier.size(400.dp)
            )

Coil

  • 添加依赖: implementation “io.coil-kt:coil-compose:$coil_version”

AsyncImage

@Preview
@Composable
fun AsyncImageDemo() {
    AsyncImage(
        model = ImageRequest.Builder(LocalContext.current)
            .data(ImageUrl)
            .crossfade(true)
            .build(),
        //model = (ImageUrl),
        contentDescription = stringResource(R.string.description),
        placeholder = painterResource(id = R.drawable.place_holder),
        error = painterResource(id = R.drawable.error),
        onSuccess = {
            Log.d(TAG, "success")
        },
        onError = { error ->
            Log.d(TAG, "error")
        },
        onLoading = { loading ->
            Log.d(TAG, "loading")
        },
        modifier = Modifier.clip(CircleShape)
    )
}

SubcomposeAsyncImage

@Preview
@Composable
fun SubcomposeAsyncImageDemo() {
    SubcomposeAsyncImage(
        model = "ImageUrl",
        loading = { CircularProgressIndicator() },
        contentDescription = "compose_museum"
    ) 
}

@Preview
@Composable
fun SubcomposeAsyncImageDemo() {
    SubcomposeAsyncImage(
        model = "ImageUrl",
        contentDescription = "compose_museum"
    ) {
        if (painter.state is AsyncImagePainter.State.Loading || painter.state is AsyncImagePainter.State.Error) {
            CircularProgressIndicator()
        } else {
            SubcomposeAsyncImageContent()
        }
    }
}
SubcomposeAsyncImage(
            model = ImageRequest
            	.Builder(LocalContext.current)
            	.data(ImageUrl.testUrl1)
            	.size(1920,1080),
            	.build(),
            contentDescription = null,
        ) {
            val state = painter.state
            when(state) {
                is AsyncImagePainter.State.Loading -> CircularProgressIndicator()
                is AsyncImagePainter.State.Error -> Text("${state.result.throwable}")
                is AsyncImagePainter.State.Success -> SubcomposeAsyncImageContent()
                is AsyncImagePainter.State.Empty -> Text("Empty")
            }
        }

AsyncImagePainter

  • 这个组件是底层API,使用时会出现很多不可预期的行为,所以建议用前面两个
  • 如果项目要求不能用AsyncImage则用这个
  • 不能用Success状态来判断,否则图片不能加载成功
val painter = rememberAsyncImagePainter(
  model = ImageRequest.Builder(LocalContext.current)
    .data("https://pic-go-bed.oss-cn-beijing.aliyuncs.com/img/20220316151929.png")
    .build()
)
if (painter.state is AsyncImagePainter.State.Loading) {
  CircularProgressIndicator()
}
Image(
  painter = painter,
  contentDescription = stringResource(R.string.description)
)

《Jetpack Compose从入门到实战》第一章 全新的 Android UI 框架

《Jetpack Compose从入门到实战》 第二章 了解常用UI组件

《Jetpack Compose从入门到实战》第三章 定制 UI 视图

《Jetpack Compose从入门到实战》第八章 Compose页面 导航

《Jetpack Compose从入门到实战》第九章 Accompanist 与第三方组件库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1055904.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣 -- 494. 目标和(01背包)

参考代码&#xff1a; 未优化的代码&#xff1a; class Solution { public:int findTargetSumWays(vector<int>& nums, int target) {int nnums.size();int sum0;for(const auto& e:nums){sume;}//aimbsum//aim-btarget//aim(sumtarget)/2int aim(sumtarget)/2…

7、Nacos服务注册服务端源码分析(六)

本文收录于专栏 Nacos 中 。 文章目录 前言一、Nacos的任务设计中有哪些关键类&#xff1f;定义任务&#xff1a;NacosTask执行任务&#xff1a;NacosTaskProcessor执行引擎&#xff1a;NacosTaskExecuteEngine 二、PushDelayTaskExecuteEngine、NacosExecuteTaskExecuteEngine…

python操作.xlsx文件

from openpyxl import load_workbook from openpyxl.styles import Font,colors, Alignment from openpyxl.styles import Border, Side #打开已经存在的Excel workbook load_workbook(filenameC:\\Users\\yh\\Documents\\测试.xlsx) #创建表&#xff08;sheet&#xff09;,插…

崇州街子古镇2023中秋国庆双节第四天一瞥

今天已是2023中秋国庆双节第四天&#xff0c;上午近10时许&#xff0c;笔者继昨下午又走出寄居养老成都市崇州街子古镇青城神韵小区大门&#xff0c;看看节日气氛是否仍然浓厚...... 笔者手机拍摄&#xff1a;国旗飘扬的青城神韵小区一号大门 笔者手机拍摄&#xff1a;街子古镇…

了解”变分下界“

“变分下界”&#xff1a;在变分推断中&#xff0c;我们试图找到一个近似概率分布q(x)来逼近真实的概率分布p(x)。变分下界是一种用于评估近似概率分布质量的指标&#xff0c;通常用来求解最优的近似分布。它的计算涉及到对概率分布的积分或期望的估计

Spring5应用之JDK动态代理

作者简介&#xff1a;☕️大家好&#xff0c;我是Aomsir&#xff0c;一个爱折腾的开发者&#xff01; 个人主页&#xff1a;Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客 当前专栏&#xff1a;Spring5应用专栏_Aomsir的博客-CSDN博客 文章目录 前言JDK动态代理开…

计算机竞赛 深度学习猫狗分类 - python opencv cnn

文章目录 0 前言1 课题背景2 使用CNN进行猫狗分类3 数据集处理4 神经网络的编写5 Tensorflow计算图的构建6 模型的训练和测试7 预测效果8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习猫狗分类 ** 该项目较为新颖&a…

[C++随笔录] stack queue模拟实现

stack && queue模拟实现 stack的实现stack测试用例queue的实现queue测试用例deque stack的实现 &#x1f5e8;️stack的容器适配器应该选什么比较好呢? 首先, stack的特点是 头部入, 尾部出 ⇒ 尾插 和 尾删操作比较频繁 我们前面学过的容器有 vector 和 list, vecto…

代理服务器拒绝连接

在使用电脑时&#xff0c;有些时候会出现 代服务器拒绝连接 的提示&#xff0c;在这个时候通常采用的一种解决方案是&#xff1a; 首先 点击 winR &#xff0c; 后在其中输入 inetcpl.cpl&#xff0c;点击 连接->局域网设置&#xff0c;把选择框全部清空&#xff0c;点击确…

QT:绘图

widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QPaintEvent> //绘图事件class Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent 0);~Widget();void paintEvent(QPaintEvent *event); //重写绘图事件void timerEve…

Claude一个比chat-gpt相同但使用门槛更低的ai生产力

本篇文章主要介绍Claude的官网、使用方法&#xff0c;以及Claude 的特性和与chat-gpt的区别。 日期&#xff1a;2023年6月17日 作者&#xff1a;任聪聪 Claude 的介绍及相关信息 Claude 也是一个与chat-gpt等同的nlp大语言模型&#xff0c;效果和gpt几乎差不多&#xff0c;能够…

程序在线报刊第一期

文章目录 程序在线报刊第一期排序算法&#xff1a;优化数据处理效率的核心技术回顾区块链技术&#xff1a;去中心化引领数字经济新时代展望AI未来&#xff1a;智能化时代的无限可能 程序在线报刊第一期 排序算法&#xff1a;优化数据处理效率的核心技术 近年来&#xff0c;随…

区块链(9):java区块链项目的Web服务实现之实现web服务

1 引入pom依赖 <dependency><groupId>org.eclipse.jetty</groupId><artifactId>jetty-server</artifactId><version>9.4.8.v20171121</version></dependency><dependency><groupId>org.eclipse.jetty</groupId…

Tomcat多实例、负载均衡、动静分离

Tomcat多实例部署 安装jdk [rootlocalhost ~]#systemctl stop firewalld.service [rootlocalhost ~]#setenforce 0 [rootlocalhost ~]#cd /opt [rootlocalhost opt]#ls apache-tomcat-8.5.16.tar.gz jdk-8u91-linux-x64.tar.gz rh [rootlocalhost opt]#tar xf jdk-8u91-linu…

Vivado与Notepad++关联步骤

填写内容 先看"关联步骤"再看此处&#xff1a; 在“editor”栏中填写 Notepad的路径&#xff0c;并加上[file name] -n[line number]&#xff0c; 这里我的 Notepad 的路径为 C:/Program Files (x86)/Notepad/notepad.exe &#xff1b; 故这里我就填上以下内容即可…

【人工智能 | 认知观与系统类别】从宏观角度看人工智能认知观与系统类别:探索人工智能无垠领域

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

Spirng Cloud Alibaba Nacos注册中心的使用 (环境隔离、服务分级存储模型、权重配置、临时实例与持久实例)

文章目录 一、环境隔离1. Namespace&#xff08;命名空间&#xff09;&#xff1a;2. Group&#xff08;分组&#xff09;&#xff1a;3. Services&#xff08;服务&#xff09;&#xff1a;4. DataId&#xff08;数据ID&#xff09;&#xff1a;5. 实战演示&#xff1a;5.1 默…

SpringBoot终极讲义第二章笔记

01.关于Import 和 ImportResource Import注解用法(类上): 一般和Configuration一起使用,用来导入里面Bean方法返回的对象 ImportResource(类上):一般和Configuration一起使用,用来导入某个.XML文件里的bean 个人觉得这两个注解有点鸡肋 SpringBoot启动类默认扫描的是启动类…

电子地图 | VINS-FUSION | 小觅相机D系列

目录 一、相关介绍 二、VINS-FUSION环境安装及使用 &#xff08;一&#xff09;Ubuntu18.04安装配置 1、Ubuntu下载安装 2、设置虚拟内存&#xff08;可选&#xff09; &#xff08;二&#xff09;VINS-FUSION环境配置 1、ros安装 2、ceres-solver安装 3、vins-fusion…