iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >AndroidCompose实现联系人列表流程
  • 909
分享到

AndroidCompose实现联系人列表流程

AndroidComposeAndroidCompose实现联系人 2023-03-10 11:03:29 909人浏览 八月长安
摘要

目录准备数据思路代码实现 准备数据 data class ContactEntity( val letter: Char, val name: String,

准备数据

data class ContactEntity(
    val letter: Char,
    val name: String,
    val color: Color
)

fun getContactData(): MutableList<ContactEntity> {
    val contactList = mutableListOf<ContactEntity>()
    (65..90).forEach { letter ->
//        val random = (5..20).random()
        val random = 5
        repeat(random) { index ->
            contactList.add(
                ContactEntity(
                    letter = letter.toChar(), name = "联系人  $index",
                    color = Color(
                        red = (0..255).random(),
                        blue = (0..255).random(),
                        green = (0..255).random()
                    )
                )
            )
        }
    }
    return contactList
}

fun getCharList(): MutableList<Char> {
    val charList = mutableListOf<Char>()
    (65..90).forEach { letter ->
        charList.add(letter.toChar())
    }
    return charList
}

思路

  • 整体是由Box布局包裹, 左侧是LazyColumn 右侧放置自定义布局
  • 左侧LazyColumn用state来观察滑动的一些参数,来控制右侧字母的位置
  • 右侧使用canvas绘制字母 在触控的时候使用scrollToItem准确的定位到左侧应该滑动的位置,感觉有些不准确,不知道是计算的问题还是bug,在谷歌上看到类似的issue提交。
  • 中间显示滑动到的字母,也可以使用贝塞尔曲线来绘制类似于水滴的样式,懒得去搞了。
  • 利用derivedStateOf来跟踪变化的remember数据,这样可以减少性能的损耗,但是感觉有个地方没处理好,就是触摸的时候需要改变,滑动的时候也需要改变,而derivedStateOf是val类型的,不能直接赋值,所以又设置了一个remember变量。有可以优化的地方请指出。
  • 数据和样式都可以自定义,非常方便

代码实现

@OptIn(ExperimentalTextapi::class)
@Composable
fun ContactPage(navCtrl: NavHostController, title: String) {
    //联系人数据
    val contactList = getContactData()
    //字母数据
    val charList = getCharList()
    val offsetY = with(LocalDensity.current) {
        20.dp.toPx()
    }
    val offsetX = with(LocalDensity.current) {
        25.dp.toPx()
    }
    val coroutineScope = rememberCoroutineScope()
    val textMeasure = rememberTextMeasurer()
    val state = rememberLazyListState()
    //触摸的位置
    var touchOffset by remember() {
        mutableStateOf(Offset.Zero)
    }
    //触摸到的上一次的字母下标
    var touchIndexLast by remember {
        mutableStateOf(-1)
    }
    //触摸的字母的下标
    val touchIndex = remember(touchOffset.y) {
        derivedStateOf {
            if (touchOffset.y > 0f) {
                //通过偏移的倍数计算滑动到哪个字母的位置了
                val y = (touchOffset.y / offsetY).roundToInt()
                if (y in 0..25) {
                    touchIndexLast = y
                    y
                } else {
                    touchIndexLast
                }
            } else touchIndexLast
        }
    }
    //触摸到的字符的index
    val touchLetterIndex = remember {
        derivedStateOf {
            if (state.isScrollInProgress && state.layoutInfo.visibleItemsInfo.isNotEmpty()) {
                val key = state.layoutInfo.visibleItemsInfo[0].key
                if (key is Int && key < contactList.size) {
                    val letter = contactList[key].letter
                    val findIndex = charList.indexOfFirst {
                        it == letter
                    }
                    findIndex
                } else {
                    touchIndex.value
                }
            } else {
                touchIndex.value
            }
        }
    }
    //上一次选择的letter
    var lastLetter by remember {
        mutableStateOf("")
    }
    //滑动到的letter
    val scrollLetter = remember {
        derivedStateOf {
            if (touchIndex.value > 0) {
                val letter = (65 + touchIndex.value).toChar()
                coroutineScope.launch {
                    //找到相应的item
                    val index = getIndex(contactList, letter)
                    state.scrollToItem(index, scrollOffset = 20)
                }
                val str = letter.toString()
                lastLetter = str
                str
            } else {
                lastLetter
            }
        }
    }
    CommonToolbar(navCtrl, title) {
        Box(modifier = Modifier.fillMaxSize()) {
            LazyColumn(modifier = Modifier.fillMaxWidth(), state = state, content = {
                contactList.forEachIndexed { index, contactEntity ->
                    item(key = index) {
                        Column(
                            modifier = Modifier.fillMaxWidth()
                        ) {
                            Row(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .padding(horizontal = 10.dp),
                                verticalAlignment = Alignment.CenterVertically
                            ) {
                                Box(
                                    modifier = Modifier
                                        .size(50.dp)
                                        .clip(CircleShape)
                                        .background(
                                            color = contactEntity.color,
                                        ), contentAlignment = Alignment.Center
                                ) {
                                    Text(
                                        text = "${contactEntity.letter}",
                                        fontSize = 16.sp,
                                        color = Color.White,
                                        fontWeight = FontWeight.SemiBold,
                                    )
                                }
                                Text(
                                    text = contactEntity.name,
                                    modifier = Modifier
                                        .padding(20.dp)
                                        .weight(1f)
                                        .padding(horizontal = 10.dp, vertical = 16.dp)
                                )
                            }
                            Divider()
                        }
                    }
                }
                item {
                    Text(
                        text = "共${contactList.size}联系人",
                        fontSize = 12.sp,
                        color = Color(0xFF333333),
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(vertical = 20.dp),
                        textAlign = TextAlign.Center
                    )
                }
            })
            //绘制右侧的26个字母
            Canvas(modifier = Modifier
                .padding(top = 30.dp)
                .width(50.dp)
                .fillMaxHeight()
                .align(Alignment.TopEnd)
                .pointerInput(Unit) {
                    coroutineScope {
                        while (true) {
                            // down事件
                            val downPointerInputChange = awaitPointerEventScope {
                                awaitFirstDown()
                            }
                            // 如果位置不在手指按下的位置,先动画的形式过度到手指按下的位置
                            if (touchOffset.x != downPointerInputChange.position.x && touchOffset.y != downPointerInputChange.position.y) {
                                launch {
                                    touchOffset = downPointerInputChange.position
                                }
                            }
                            // touch Move事件
                            // 滑动的时候,box随着手指的移动去移动
                            awaitPointerEventScope {
                                drag(downPointerInputChange.id, onDrag = {
                                    touchOffset = it.position
                                })
                            }
                            // 在手指弹起的时候,才通过动画的形式,回到原点的位置
                            val dragUpOrCancelPointerInputChange = awaitPointerEventScope {
                                awaitDraGorCancellation(downPointerInputChange.id)
                            }
                            // 等于空,说明已经抬起
                            if (dragUpOrCancelPointerInputChange == null) {
                                launch {
                                    touchOffset = Offset.Zero
                                }
                            }
                        }
                    }
                }, onDraw = {
                charList.forEachIndexed { index, char ->
                    drawText(
                        size = Size(width = offsetY, offsetY),
                        textMeasurer = textMeasure, text = "$char",
                        style = TextStyle(
                            fontWeight = if (touchLetterIndex.value == index) FontWeight.SemiBold else FontWeight.Medium,
                            color = if (touchLetterIndex.value == index) Color.Blue else Color(
                                0xFF333333
                            ),
                            textAlign = TextAlign.Center,
                            fontSize = if (touchLetterIndex.value == index) 16.sp else 14.sp
                        ),
                        topLeft = Offset(offsetX, offsetY * index),
                    )
                }
            })
            //中间显示的大写字母
            val textMeasurer1 = rememberTextMeasurer()
            if (touchOffset.x != 0f && touchOffset.y != 0f && scrollLetter.value.isNotEmpty()) {
                val annotatedString = AnnotatedString(
                    scrollLetter.value, spanStyle = SpanStyle(
                        fontWeight = FontWeight.Medium,
                        color = Color(0xFF333333),
                        fontSize = 20.sp,
                    )
                )
                val textLayoutResult = textMeasurer1.measure(text = annotatedString)
                val textSize = textLayoutResult.size
                Canvas(modifier = Modifier
                    .align(Alignment.Center)
                    .requiredSize(width = 50.dp, height = 50.dp), onDraw = {
                    //底部颜色
                    drawRoundRect(
                        color = Color(0xA403A9F4), cornerRadius = CornerRadius(10f, 10f)
                    )
                    //绘制字母
                    drawText(
                        textMeasurer = textMeasurer1,
                        text = annotatedString,
                        topLeft = Offset(
                            (size.width - textSize.width) / 2f,
                            (size.height - textSize.height) / 2f
                        )
                    )
                })
            }
        }
    }
}
private fun getIndex(list: MutableList<ContactEntity>, letter: Char): Int {
    val findIndex = list.indexOfFirst { contactEntity ->
        contactEntity.letter == letter
    }
    return findIndex
}

代码位置点我

到此这篇关于Android Compose实现联系人列表流程的文章就介绍到这了,更多相关Android Compose内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: AndroidCompose实现联系人列表流程

本文链接: https://www.lsjlt.com/news/199259.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • AndroidCompose实现联系人列表流程
    目录准备数据思路代码实现 准备数据 data class ContactEntity( val letter: Char, val name: String, ...
    99+
    2023-03-10
    Android Compose Android Compose实现联系人
  • Android Compose如何实现联系人列表
    这篇文章主要介绍“Android Compose如何实现联系人列表”,在日常操作中,相信很多人在Android Compose如何实现联系人列表问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答...
    99+
    2023-07-05
  • android仿微信联系人索引列表功能
    前言  因为自己在做的一个小软件里面需要用到从A-Z排序的ListView,所以自然而然的想到了微信的联系人,我想要的就是那样的效果。本来没打算自己去写,想要第三方写好的东西,搜了几个之后发现有的太复杂了,有的简单是...
    99+
    2023-05-30
  • Java实现联系人管理系统
    基于Java的联系人管理系统,供大家参考,具体内容如下 基于eclipse做的一个简单的联系人管理系统 对于联系人姓名首字母进行了一个排序,对于存入信息时手机号码与电子邮箱做了一个判...
    99+
    2024-04-02
  • Android仿微信联系人列表字母侧滑控件
    仿微信联系人列表字母侧滑控件, 侧滑控件参考了以下博客:Android实现ListView的A-Z字母排序和过滤搜索功能首先分析一下字母侧滑控件应该如何实现,根据侧滑控件的高度和字母的数量来平均计算每个字母应该占据的高度。在View的onD...
    99+
    2023-05-31
    android 侧滑控件
  • Java实战个人博客系统的实现流程
    springboot+mybatis+前端vue,使用前后端分离架构实现的个人博客系统,共7个模块,首页,写博客,博客详情页,评论管理,文章分类,标签管理和文章归档。 运行环境: j...
    99+
    2024-04-02
  • C#多线程系列之工作流实现
    目录前言节点ThenParallelScheduleDelay试用一下顺序节点并行任务编写工作流接口构建器工作流构建器依赖注入实现工作流解析前言 前面学习了很多多线程和任务的基础知识...
    99+
    2024-04-02
  • Javaweb实现完整个人博客系统流程
    目录一、项目背景二、项目功能三、项目的基本流程1.准备工作2.数据库设计3.准备前端页面4.实现前端匹配的Servlet所需功能5.项目难点一、项目背景 在学习完JavaWeb相关知...
    99+
    2024-04-02
  • Android MVVM架构实现RecyclerView列表详解流程
    目录效果图导入引用导入Recyclerview依赖导入dataBinding引用代码解析建立实体类建立RecyclerView子项适配器建立适配器设置子项点击事件adapter全部代...
    99+
    2024-04-02
  • vue实现列表左右联动效果
    本文实例为大家分享了vue实现列表左右联动效果的具体代码,供大家参考,具体内容如下 先谈需求:左侧为分类列表,点击分类名右侧商品列表会滑动对应分类到顶部;右侧商品列表滑动到某一分类时...
    99+
    2024-04-02
  • 怎么用Java实现联系人管理系统
    本文小编为大家详细介绍“怎么用Java实现联系人管理系统”,内容详细,步骤清晰,细节处理妥当,希望这篇“怎么用Java实现联系人管理系统”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。基于eclipse做的一个简单...
    99+
    2023-06-29
  • 小程序实现瀑布流动态加载列表
    本文实例为大家分享了小程序实现瀑布流动态加载列表的具体代码,供大家参考,具体内容如下 最近业务需要做一个商城列表,就自己写了一个瀑布流来加载列表。 这个列表在很多地方都需要用到,就...
    99+
    2024-04-02
  • Android实现手机联系人分栏效果
    本文实例为大家分享了Android实现手机联系人分栏效果的具体代码,供大家参考,具体内容如下 小编在项目时期遇见了制作手机联系人分栏效果,查询了很多资料,现在总结如下: 添加的代码并...
    99+
    2024-04-02
  • Javaweb如何实现完整个人博客系统流程
    这篇文章主要讲解了“Javaweb如何实现完整个人博客系统流程”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Javaweb如何实现完整个人博客系统流程”吧!项目的基本流程1.准备工作pom....
    99+
    2023-06-29
  • Mybatis实现查询相册数据列表流程讲解
    目录1.书写执行的SQL语句2.在项目的根包下创建pojo.vo.AlbumListItemVO类3.在AlbumMapper.java中添加抽象方法4.在AlbumMapper.x...
    99+
    2022-12-09
    Mybatis查询相册数据列表 Mybatis查询数据
  • 小程序如何实现列表滚动上下联动效果
    这篇文章主要为大家展示了“小程序如何实现列表滚动上下联动效果”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“小程序如何实现列表滚动上下联动效果”这篇文章吧。1、背景最近在做公司的一款小程序,其中有...
    99+
    2023-06-22
  • Android ContentProvider实现获取手机联系人功能
    在之前项目中有用到关于获取手机联系人的部分,闲置就想和大家分享一下,话不多说,上代码:java部分:package com.example.content; import android.content.ContentResolver; ...
    99+
    2023-05-30
    android contentprovider 联系人
  • JS+AJAX实现省市区的下拉列表联动
    本文实例为大家分享了JS+AJAX实现省市区下拉列表联动的具体代码,供大家参考,具体内容如下 效果图如下,DB中存取的数据来抽取. 前台JSP页面的实现 <div cla...
    99+
    2024-04-02
  • Mybatis + js 实现下拉列表二级联动效果
     一、业务需求 实现省份与城市的二级联动 二、实现效果 三、代码实现 1. province_city.jsp 前端界面实现 <%@ page contentT...
    99+
    2024-04-02
  • Android怎么实现手机联系人分栏效果
    今天小编给大家分享一下Android怎么实现手机联系人分栏效果的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。效果图:adap...
    99+
    2023-06-29
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作