In the non-compose version I have something like the following:
在非写作版本中,我有类似以下内容的内容:
(...)
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="my label"/>
<com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="my text"/>
<com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="my text2"/>
<com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="my text3"/>
</LinearLayout>
(...)
And it repeats as many times as I need, representing a list of values with their respective texts, for example:
它根据我的需要重复多次,用各自的文本表示值的列表,例如:
User settings
Active at home
Active at office
Inactive at lab
Server connected
www.someserver.com
Debug info
u5y4t3fweryhjy43tr2
etc.
等。
And I have a helper function that runs whenever the container
is clicked, iterates all the children views of the respective container and collects all the text from any views that are TextView
.
我有一个助手函数,它在容器被单击时运行,迭代相应容器的所有子视图,并从任何TextView视图中收集所有文本。
This helps the user copy all the text inside a specific section for debugging purposes with a single click.
这有助于用户只需一次单击即可复制特定节内的所有文本以进行调试。
However, I am having a hard time replicating this mechanism when using compose.
然而,在使用Compose时,我很难复制这种机制。
I have already recreated the above layout with pure compose, for example:
我已经用纯构图重新创建了上面的布局,例如:
(...)
ListItem(
modifier = Modifier.clickable {
// no way to get headlineContent and supportingContent here
},
headlineContent = { Text(text = stringResource(id = R.string.my_label)) },
supportingContent = {
Text(text = stringResource(id = R.string.my_text))
Text(text = stringResource(id = R.string.my_text2))
Text(text = stringResource(id = R.string.my_text3))
},
)
(...)
But when it comes to implementing the onclick
mechanism to iterate all the children of the container, and extract text from any that are Text
, I cannot find a way to achieve this.
但是,在实现onClick机制以迭代容器的所有子容器并从任何文本中提取文本时,我找不到实现这一点的方法。
I got as far as using the Modifier.clickable {}
, but there is no reference to the ListItem
, or any of the children in it.
我只使用了Modifier.clickable{},但没有引用ListItem或其中的任何子项。
There can be 1 or more Text
per each content inside the ListItem
, so simply creating another function that stores the single string from each content is just not going to work.
ListItem中的每个内容可以有一个或多个文本,所以简单地创建另一个函数来存储每个内容的单个字符串是行不通的。
The content of many of these is dynamic, but the labels themselves are static, so the helper function simplified this process significantly since I would no longer need to manually code every single click action for each container, accounting for text that can be dynamically changed.
其中许多内容都是动态的,但标签本身是静态的,因此helper函数大大简化了这个过程,因为我不再需要为每个容器手动编写每个单击操作,从而考虑到可以动态更改的文本。
Is this not possible to achieve with compose in as a simple way as in kotlin? If it is, how can it be done?
这难道不可能通过像在Kotlin中那样简单的方式使用Compose来实现吗?如果是的话,怎么做呢?
NOTE: Some of the Text
may have their own unique customization, so any workaround that stores strings passed to a custom ListItem
is just not going to work for my case, that is why I focused on a way to loop or iterate any Text
inside the ListItem
注意:有些文本可能有自己独特的定制,所以任何存储传递给定制ListItem的字符串的变通方法都不适用于我的情况,这就是为什么我专注于循环或迭代ListItem中的任何文本的方法
更多回答
优秀答案推荐
You can just create another function that provides exactly the kind of API surface you want:
您只需创建另一个函数,即可提供您想要的API表面:
@Composable
fun ListItem(
headlineId: @StringRes Int,
supportingId: @StringRes Int,
onClick: (headlineText: String, supportingText: String) -> Unit
) {
val headlineText = stringResource(headlineId)
val supportingText = stringResource(supportingId)
ListItem(
modifier = Modifier.clickable {
onClick(headelineText, supportingText)
},
headlineContent = { Text(text = headlineText) },
supportingContent = { Text(text = supportingText) }
)
}
Which you can then use like:
然后您可以像这样使用它:
ListItem(R.string.my_label, R.string.my_text) { headlineText, supportingText ->
// Now you can access the text of each
}
Of course, you could instead pass the resource IDs rather than the Strings to your listener if that's what your onClick listener wants - you're in control over what level of abstraction you want to expose.
当然,您可以将资源ID而不是字符串传递给您的侦听器,如果这是您的onClick侦听器想要的-您可以控制您想要公开的抽象级别。
It might not as simple as you want, you can define classes for text data like below.
它可能不像您想要的那么简单,您可以像下面这样为文本数据定义类。
sealed class TextData(val string: String) {
@SuppressLint("ComposableNaming")
@Composable
// default composable function of text data
// to be able to override make it open
open fun toTextCompose() {
Text(text = string)
}
class HeadlineContent(string: String) : TextData(string) {
@SuppressLint("ComposableNaming")
@Composable
// just overrode to show you can make different text compose for a specifiy text data
override fun toTextCompose() {
Text(text = string, fontWeight = FontWeight.Bold)
}
}
class SupportingContent(string: String) : TextData(string)
}
You can define other classes for additional data as you want.
您可以根据需要为其他数据定义其他类。
With these classes you can filter data and compose.
使用这些类,您可以过滤数据和编写数据。
@Composable
fun ListItem(
modifier: Modifier = Modifier,
textDataList: List<TextData>,
onClick: () -> Unit
) {
Column(modifier = modifier.clickable { onClick() }) {
// make a composable function for headline contents if necessary
textDataList.filterIsInstance<TextData.HeadlineContent>().forEach { it.toTextCompose() }
Divider()
// make a composable function for supporting contents if necessary
textDataList.filterIsInstance<TextData.SupportingContent>().forEach { it.toTextCompose() }
}
}
Lastly, to retrieve a clipboard string from a list easily, make a extension function.
最后,为了方便地从列表中检索剪贴板字符串,可以创建一个扩展函数。
// it will return a string of the below format
// HeadlineContent: SupportingContent1 SupportingContent2 ...
val List<TextData>.clipboardString: String get() = joinToString(separator = " ") {
it.string + when (it) {
is HeadlineContent -> ":"
is SupportingContent -> ""
}
}
val textDataList = listOf(
TextData.HeadlineContent("Headline content"),
TextData.SupportingContent("Supporting content"),
TextData.SupportingContent("Hello world!"),
)
ListItem(
textDataList = textDataList,
onClick = {
Toast.makeText(
this,
textDataList.clipboardString,
Toast.LENGTH_SHORT
).show()
}
)
The result of the above code is like
上述代码的结果如下所示
更多回答
Thanks for the suggestion, but that will only work if I have 1 single string for the headline content and 1 single string for the supporting content. In my case each one can have multiple Text
views inside each content which makes it impossible to use with this solution, and that is why I specifically focused on looping all the Text
inside the ListItem
. Perhaps my question can be improved to make this clearer, sorry about that, I will take care of it right away.
谢谢你的建议,但这将只有当我有一个标题内容和1个支持内容的单一字符串的工作。在我的例子中,每个元素在每个内容中都可以有多个文本视图,这使得它不可能与该解决方案一起使用,这就是为什么我特别关注循环ListItem中的所有文本的原因。也许我的问题可以改进一下,让这一点更清楚,对不起,我会马上处理的。
So take two lists of headers and supporting texts, they don't have to be single texts if you don't want them to be
所以有两个标题和支持文本的列表,如果你不希望它们是单一的文本,它们不必是单一的文本
That will just overcomplicate things, some of these Text
can have their unique customizations, your suggestion will not work.
这只会使事情变得过于复杂,其中一些文本可以有其独特的定制,您的建议将不起作用。
我是一名优秀的程序员,十分优秀!