提问人:Saher Al-Sous 提问时间:8/8/2021 更新时间:8/8/2021 访问量:5896
Jetpack Compose:将数据从惰性列传递到另一个可组合项
Jetpack Compose: passing data from lazy column into another composable
问:
我希望你们都做得很好。
我对 Kotlin 很陌生,所以请耐心等待我。
我在 jetpack compose 中创建了一个页面,如下图所示,它需要装饰,但至少我想实现该功能。简单的应用程序界面,左侧是惰性列,右侧是数据显示。
左侧是一个惰性列,显示 ArrayList 中的项列表。 惰性列中包含可单击的卡片。
我需要的是,一旦使用单击列中的项目,其余项目详细信息就会显示在右侧的第二个可组合项中。
代码如下所示
@Composable
fun Greeting() {
Row(
modifier = Modifier.fillMaxSize()
) {
LazyColumn(
modifier = Modifier
.fillMaxHeight()
.width(150.dp)
.background(color = Color.LightGray)
) {
items(photoList) { item ->
ProfileCard(photo = item) { <--- i need the item into the other composable.
// photoContent(
// id = item.id,
// owner = item.owner,
// secret = item.secret,
// server = item.server,
// farm = item.farm,
// title = item.title,
// ispublic = item.ispublic,
// isfriend = item.isfriend,
// isfamily = item.isfamily,
// url_s = item.url_s,
// height_s = item.height_s,
// width_s = item.width_s
// )
}
}
}
photoContent(
id = "",
owner = "",
secret = "",
server = "",
farm = 0,
title = "",
ispublic = 0,
isfamily = 0,
isfriend = 0,
url_s = "",
height_s = 0,
width_s = 0
)
}
}
此点击功能来自显示的卡片
@Composable
fun ProfileCard(photo: photo, clickAction: () -> Unit) {
Card( //We changed the default shape of Card to make a cut in the corner.
modifier = Modifier
.padding(top = 4.dp, bottom = 4.dp, start = 16.dp, end = 16.dp)
.fillMaxWidth()
.wrapContentHeight(
align = Alignment.Top
)
.clickable { clickAction.invoke() }, //<-- moving the action to main composable.
elevation = 8.dp,
backgroundColor = Color.White // Uses Surface color by default, so we have to override it.
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
ProfileContent(photo, Alignment.Start)
}
}
}
我尝试了几种方法将数据传递到另一个可组合项中,但我总是收到错误:
@composable调用只能从@composable函数的上下文中进行
而且我无法删除注释,因为它具有可组合的内容......@Composable
如何以这种方式传递数据?或者没有导航是不可能的?但这需要用户打开另一个页面,而不是当前页面......
答:
基本上,您需要的是为您的选择创建一个状态。 请参阅此示例:
假设这个班级是你的班级......Photo
data class MyUser(
val name: String,
val surname: String
)
然后,像这样定义你的屏幕:
@Composable
fun MyScreen(users: List<MyUser>) {
// This state will control the selection
var selectedUser by remember {
mutableStateOf<MyUser?>(null)
}
Row(Modifier.fillMaxSize()) {
LazyColumn(
Modifier
.weight(.3f)
.background(Color.Gray)) {
items(users) {
// This would be your ProfileCard
Text(
text = "${it.name} - ${it.surname}",
modifier = Modifier
.clickable {
// when you click on an item,
// the state is updated
selectedUser = it
}
.padding(16.dp)
)
}
}
Column(Modifier.weight(.7f)) {
// if the selection is not null, display it...
// this would be your PhotoContent
selectedUser?.let {
Text(it.name)
Text(it.surname)
}
}
}
}
像这样调用这个函数...
@Composable
fun Greeting() {
// Just a fake user list...
val users = (1..50).map { MyUser("User $it", "Surname $it") }
MyScreen(users = users)
}
会是这样的
评论
听着,孩子,你需要改变你的思维方式。
好的,让我们深入研究
Jetpack Compose 的核心是状态,状态主要以变量的形式保存。如果一个变量在很多地方被用作状态,你必须确保它得到很好的维护。不允许随意修改和读取。因此,存储状态的正确方法是在 .ViewModel
因此,首先创建一个 VM,例如
class ProfileViewModel : ViewModel() {
/*This variable shall store the photo,
Which I plan to use as an ID for the Profile Card.*/
var selectedProfile by mutableStateOf(Photo(/*Default Arguments*/)) //Store state as mutableStateOf to ensure proper recompostions
private set //Do not allow external modification, i.e., outside the ViewModel
//We'll be modifying the variable through this method
fun onSelectProfile(photo: Photo) {
selectedProfile = Photo
}
}
这只是一个简单的演示,因此我将视图模型限制为此。
接下来是活动,我们初始化视图模型
imports ...
class MainActivity : ComponentActivity() {
private val profileViewModel by viewModels<ProfileViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Greeting(
selectedProfile = profileViewModel.selectedProfile
onSelectProfile = profileViewModel::onSelectProfile
)
}
很明显,我对您的 Greeting Composable 进行了一些修改。让我们看看它们是什么:-
我基本上添加了两个参数。现在学习这个。在需要修改存储在使用它以外的位置的状态的任何场景中,并且有时还需要修改它,您必须将两个参数传递到可组合项中 - 一个用于读取值,另一个用于写入值。有道理,不是吗。这就是我在这里所做的。
我们将值从存储它的地方向下传递,将其提供给可组合项。为了让可组合项触发修改,我们传递了一个参数,该参数一直链接到存储位置。在本例中,我们将 Greeting Composable 链接到 viewmodel 中定义的。因此,的调用方是 Greeting Composable,但该方法最终在视图模型内部调用,因为它传递了。当 viewmodel 被执行时,变量会更新(参见 viewmodel 中的方法,它会更新变量)。现在,可组合项正在读取变量,因此,可组合项本身获取变量的更新值(因为我们使用的是 ,因此触发了重构)。onChange
onSelectProfile
onSelectProfile
onSelectProfile
onSelectProfile
selectedProfile
mutableStateOf
因此,看看可组合项
@Composable
fun Greeting(
selectedProfile: Photo,
onSelectProfile: (photo: Photo) -> Unit
) {
Row(
modifier = Modifier.fillMaxSize()
) {
LazyColumn(
modifier = Modifier
.fillMaxHeight()
.width(150.dp)
.background(color = Color.LightGray)
) {
items(photoList) { item ->
ProfileCard(photo = item, clickAction = onSelectProfile(item)) //We pass the photo like that
}
}
photoContent(
id = selectedProfile.id,
owner = selectedProfile.,
secret = selectedProfile.<respectiveProperty>,
server = selectedProfile.<respectiveProperty>,
farm = selectedProfile.<respectiveProperty>,
title = selectedProfile.<respectiveProperty>,
ispublic = selectedProfile.<respectiveProperty>,
isfamily = selectedProfile.<respectiveProperty>,
isfriend = selectedProfile.<respectiveProperty>,
url_s = selectedProfile.<respectiveProperty>,
height_s = selectedProfile.<respectiveProperty>,
width_s = selectedProfile.<respectiveProperty>
)
}
}
你看,这就是你一直在做的事情。你创建了一个作为参数,不是吗?你正在经历同样的过程,你只需要继续向上。clickAction()
你看,这里的事件在层次结构中向上流动,导致 viewmodel,然后 viewmodel 将变量(以及 state)的更新值传递给 Composables。在任何事件向上流动,状态向动的系统中,我们称之为单向数据流,请注意,这是 Compose 的核心。有一些用于建立此数据流的规则。阅读文档并获取 Compose State Codelab 来了解它,尽管这里已经非常清楚地说明了这个概念。
谢谢!
旁注:还有其他方法可以初始化 ViewModel。建议使用工厂。请查看文档。祝你学习编码好运。
评论