Compose的StandardCardLayout布局设置

Compose TV中,StandardCardLayout布局封装好了1个图像和标题内容,官方的使用示例

img


按照示例我添加了4个标准卡,现在有2个问题, 一是glow焦点时发光没起效果(解决了,是需要android12以上版本才可以用),二是怎么设置获得焦点时才显示标题内容.

依赖库

 implementation("androidx.tv:tv-foundation:1.0.0-alpha08")
 implementation("androidx.tv:tv-material:1.0.0-alpha08")
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeTestTheme {
                // A surface container using the 'background' color from the theme

                Surface(
                    color = MaterialTheme.colorScheme.background
                ) {
                    TvCardTest()
                }
            }
        }
    }
}

@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun TvCardTest() {
    TvLazyRow(
        Modifier.padding(16.dp, 16.dp),
        horizontalArrangement = Arrangement.spacedBy(10.dp)
    ) {
        items(4) { value ->
            StandardCardLayout(
                modifier = Modifier
                    .size(96.dp, 80.dp)
                    .padding(6.dp),
                imageCard = { interactionSource ->
                    CardLayoutDefaults.ImageCard(
                        onClick = { /*TODO*/ },
                        interactionSource = interactionSource,
                        glow = glow(
                            focusedGlow = Glow(
                                elevation = 10.dp,
                                elevationColor = Color.Red
                            )
                        ),
                        // 关闭焦点边框
                        border = border(focusedBorder = Border.None)
                        //  设置焦点时的边框颜色
                        /*    border = border(focusedBorder = Border(BorderStroke(1.dp, Color.Red)))  */
                    )
                    {
                        Image(
                            painter = painterResource(id = R.drawable.app_icon_your_company),
                            contentDescription = null
                        )
                    }
                },
                title = { Text("card $value") }
            )
        }
    }
}

CardLayoutDefaults.ImageCard的参数

img


但是引用的border()方法和glow()方法又有点不同,不知道是不是这个原因导致发光效果没起作用,

img


img


还有一个获得焦点时才显示标题内容,看文档好像interactionSource: MutableInteractionSource是处理这方面的,但是没有示例,我就不懂怎么用.

img


设置了CardLayoutDefaults.ImageCard后,还需要在外层的Modifier中添加focusModifier来设置焦点效果。在StandardCardLayout的modifier中,使用Modifier.focusModifier来设置,用Modifier的focusRequester和visibility来控制标题的显示状态。在每个StandardCardLayout中,通过设置不同的focusRequester,并在标题处使用Modifier.visibility来控制标题的显示

援引讯飞星火:

  1. 关于glow焦点时发光没起效果的问题,你可以尝试将Glowelevation值调整为更大的值,例如20.dp,以便在获得焦点时产生更明显的发光效果。

  2. 要实现获得焦点时才显示标题内容,你可以使用Modifier.focusRequester来请求焦点,然后在Text组件中添加focusNode属性来监听焦点变化。当获得焦点时,标题内容将显示出来。以下是修改后的代码:

@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun TvCardTest() {
    TvLazyRow(
        Modifier.padding(16.dp, 16.dp),
        horizontalArrangement = Arrangement.spacedBy(10.dp)
    ) {
        items(4) { value ->
            StandardCardLayout(
                modifier = Modifier
                    .size(96.dp, 80.dp)
                    .padding(6.dp),
                imageCard = { interactionSource ->
                    CardLayoutDefaults.ImageCard(
                         { /*TODO*/ },
                        interactionSource = interactionSource,
                        glow = glow(
                            focusedGlow = Glow(
                                elevation = 20.dp, // 修改这里的值
                                elevationColor = Color.Red
                            )
                        ),
                        // 关闭焦点边框
                        border = border(focusedBorder = Border.None)
                        //  设置焦点时的边框颜色
                        /*    border = border(focusedBorder = Border(BorderStroke(1.dp, Color.Red)))  */
                    )
                    {
                        Image(
                            painter = painterResource(id = R.drawable.app_icon_your_company),
                            contentDescription = null
                        )
                    }
                },
                title = { Text("card $value", modifier = Modifier.focusRequester { true }) } // 添加focusNode属性并设置为true
            )
        }
    }
}

这样,在获得焦点时,标题内容将显示出来,并且发光效果也会更加明显。

引用 皆我百晓生 小程序回复内容续答:

要解决glow焦点发光效果不起作用的问题,需要确保你的设备符合以下要求:

  1. 物理设备或模拟器需要运行Android 12或更高版本。
  2. 需要将Compose环境更新到最新版本,以确保使用的是最新的Compose TV库。

关于发光效果设置,你可以尝试在StandardCardLayout布局的imageCard参数中使用glow参数,并将它传递给CardLayoutDefaults.ImageCard函数。确保传递正确的Glow参数来实现发光效果。例如:

glow = glow(
    focusedGlow = Glow(
        elevation = 100.dp, // 调整发光效果的大小
        elevationColor = Color.Red
    )
)

这样会使焦点在图像周围产生红色的发光效果。你可以根据需要调整elevation参数来改变发光效果的大小。

对于获得焦点时才显示标题内容的需求,你可以使用interactionSource参数来处理它。通过为每个StandardCardLayout创建一个独立的MutableInteractionSource实例,你可以使用该实例来跟踪焦点状态,并在焦点变化时更新标题的可见性。

在StandardCardLayout布局中添加interactionSource参数,并为每个StandardCardLayout创建一个独立的MutableInteractionSource实例。例如:

StandardCardLayout(
    modifier = Modifier
        .size(96.dp, 80.dp)
        .padding(6.dp),
    imageCard = { interactionSource ->
        // ...
    },
    title = { Text("card $value", modifier = Modifier.alpha(if (interactionSource.isFocused) 1f else 0f)) }
)

在这个例子中,我们使用了alpha修饰符来控制标题的可见性。当焦点在卡片上时,标题的alpha值为1,即完全可见;当焦点离开卡片时,标题的alpha值为0,即完全不可见。

这样设置后,当卡片获得焦点时,标题内容将显示出来。

是安卓开发版本的问题吗?

结合GPT给出回答如下请题主参考
好的,对于您遇到的问题,首先是焦点时发光没有起效果的问题,可能是由于您没有为标准卡设置焦点处理器。

为了解决这个问题,您可以为每个标准卡添加一个焦点处理器,代码示例如下:

Modifier.focusOrder { // 设置焦点顺序
    FocusOrder(1) // 设置焦点顺序为1
}
.focusable(true) // 设置可获得焦点
.onFocusChanged { focused ->
    if (focused) {
        // 处理焦点获得时的逻辑
    } else {
        // 处理焦点失去时的逻辑
    }
    // 发光效果可以在这里进行处理
}

另外一个问题是关于Compose TV中StandardCardLayout的使用示例。下面是一个官方提供的使用示例:

StandardCardLayout(
    image = {
        Image(
            painter = rememberImagePainter(data = "https://xxx.com/image.png"),
            contentDescription = null,
            modifier = Modifier.aspectRatio(16F/9F),
            contentScale = ContentScale.Crop
        )
    },
    title = {
        Text("Title")
    },
    subtitle = {
        Text("Subtitle")
    }
)

您可以根据这个示例来修改或添加您的标准卡,也可以根据自己的需求进行调整。

希望这些信息可以帮助到您!

glow()的使用需要在 Android 12 及以上版本才有效。在 Android 12 之前的版本,你可能无法直接使用这个发光效果。如果你的应用的最低支持版本小于 Android 12,那么你需要考虑其他的方式来实现类似的效果
关于获得焦点时才显示标题内容,你需要使用 interactionSource 来监听焦点状态的变化,然后在焦点变为获得焦点时显示标题内容
示例

StandardCardLayout(
    modifier = Modifier
        .size(96.dp, 80.dp)
        .padding(6.dp),
    imageCard = { interactionSource ->
        val isFocused = remember(interactionSource) { mutableStateOf(false) }
        CardLayoutDefaults.ImageCard(
            onClick = { /*TODO*/ },
            interactionSource = interactionSource,
            onFocusChanged = { focusState ->
                isFocused.value = focusState.isFocused
            }
        ) {
            Image(
                painter = painterResource(id = R.drawable.app_icon_your_company),
                contentDescription = null
            )
        }
    },
    title = {
        if (isFocused.value) {
            Text("Your Title Here")
        }
    }
)


CardLayout布局管理器的使用
可以参考下


Android前沿技术学习——Compose布局原理+配置布局+四大块技术全方面解析! - 知乎 一、简介布局系统的 Jetpack Compose 实现有两个主要目标:一是实现高性能,二是让开发者能够轻松编写自定义布局。在 Compose 中,通过避免多次测量布局子级可实现高性能。如果需要进行多次测量,Compose 具有一个… https://zhuanlan.zhihu.com/p/540295707

@OptIn(ExperimentalTvMaterial3Api::class) 
@Composable
fun TvCardTest() {

  TvLazyRow(
    Modifier.padding(16.dp, 16.dp),
    horizontalArrangement = Arrangement.spacedBy(10.dp)
  ) {
    items(4) { value ->
      
      val interactionSource = remember { MutableInteractionSource() }
      
      val showTitle = remember { mutableStateOf(false) }
      
      LaunchedEffect(interactionSource) {
        interactionSource.interactions.collect { interaction ->
          
          when (interaction) {
            is PressInteraction.Press -> {
              showTitle.value = true
            }
            is PressInteraction.Release, is PressInteraction.Cancel -> {
              showTitle.value = false 
            }
          }
        }
      }
      
      StandardCardLayout(
        modifier = Modifier
          .size(96.dp, 80.dp)
          .padding(6.dp),
        imageCard = { interactionSource ->
          
          CardLayoutDefaults.ImageCard(
            onClick = { /*TODO*/ },
            interactionSource = interactionSource,
            glow = glow(
              focusedGlow = Glow(
                elevation = 10.dp,
                elevationColor = Color.Red  
              )
            ),
            border = border(focusedBorder = Border.None)
            {
              Image(
                painter = painterResource(id = R.drawable.app_icon_your_company),
                contentDescription = null
              )
            }
          )
          
        },
        title = {
          if (showTitle.value) {
            Text("card $value") 
          }
        }
      )
    }
  }

}


在StandardCardLayout布局中,要实现获得焦点时才显示标题内容,可以通过使用states属性来实现。
首先,需要创建一个自定义的StandardCardLayout布局,然后通过states属性来定义标题的显示条件。具体来说,可以使用isSelected状态来控制标题的显示。

参考gpt

以下是一个示例代码,演示了如何设置获得焦点时显示标题内容:

@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun TvCardTest() {
    TvLazyRow(
        Modifier.padding(16.dp, 16.dp),
        horizontalArrangement = Arrangement.spacedBy(10.dp)
    ) {
        items(4) { value ->
            var isFocused by remember { mutableStateOf(false) }

            StandardCardLayout(
                modifier = Modifier
                    .size(96.dp, 80.dp)
                    .padding(6.dp)
                    .focusable()
                    .onFocusChanged { isFocused = it.isFocused },
                imageCard = { interactionSource ->
                    CardLayoutDefaults.ImageCard(
                        onClick = { /*TODO*/ },
                        interactionSource = interactionSource,
                        glow = glow(
                            focusedGlow = Glow(
                                elevation = 10.dp,
                                elevationColor = Color.Red
                            )
                        ),
                        border = border(focusedBorder = Border.None)
                    )
                    {
                        Image(
                            painter = painterResource(R.drawable.image),
                            contentDescription = "Image"
                        )
                    }
                },
                title = {
                    if (isFocused) {
                        Text("Title")
                    }
                }
            )
        }
    }
}

在上面的代码中,我们使用remember函数来创建一个可变的isFocused状态,用于保存焦点状态。然后,我们将Modifier.focusable()函数应用于StandardCardLayout的修饰符,并使用Modifier.onFocusChanged()函数来监听焦点变化并更新isFocused状态。最后,我们在title参数中使用Text组件来显示标题内容,但只有在isFocusedtrue时才显示。

升级一下版本

参考结合AI智能、文心一言等综合回答,若有帮助,恭请采纳。

StandardCardLayout是一种CardLayout布局的变体,它可以在面板中显示一个标准大小的卡片。以下是如何使用StandardCardLayout布局设置:

  1. 导入必要的类:
import java.awt.*;
import javax.swing.*;
  1. 创建一个JFrame窗口和一个JPanel面板:
JFrame frame = new JFrame("Standard Card Layout Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

JPanel panel = new JPanel(new StandardCardLayout());
frame.getContentPane().add(panel);
  1. 向面板中添加卡片组件:
JLabel label1 = new JLabel("Card Example 1", JLabel.CENTER);
JLabel label2 = new JLabel("Card Example 2", JLabel.CENTER);
JLabel label3 = new JLabel("Card Example 3", JLabel.CENTER);

panel.add(label1, "card1");
panel.add(label2, "card2");
panel.add(label3, "card3");
  1. 显示卡片组件:
panel.show("card1");

每个卡片都有一个唯一的名字,可以在添加卡片时使用。使用show()方法来显示面板中的特定卡片。

完整的示例代码如下:

import java.awt.*;
import javax.swing.*;

public class StandardCardLayoutExample {
    public static void main(String[] args) {
        JFrame frame = new JFrame("Standard Card Layout Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel(new StandardCardLayout());
        frame.getContentPane().add(panel);

        JLabel label1 = new JLabel("Card Example 1", JLabel.CENTER);
        JLabel label2 = new JLabel("Card Example 2", JLabel.CENTER);
        JLabel label3 = new JLabel("Card Example 3", JLabel.CENTER);

        panel.add(label1, "card1");
        panel.add(label2, "card2");
        panel.add(label3, "card3");

        panel.show("card1");

        frame.setSize(400, 300);
        frame.setVisible(true);
    }
}

如果你的设备运行的是低于 Android 12 的版本,发光效果可能不会起作用

方法被弃用了

【以下回答由 GPT 生成】

我了解获得焦点时才显示标题内容的设置步骤。在Compose TV中,可以使用TvLazyRow组件和StandardCardLayout布局来实现此功能。

首先,需要在StandardCardLayout中改变titleCard的可见性,以便在获得焦点时显示标题内容。为了实现这一点,我们可以为titleCard创建一个MutableState变量,并在获得和失去焦点时更新它的值。

StandardCardLayout中添加一个Boolean类型的MutableState变量,用于控制标题的可见性:

@Composable
fun StandardCardLayout(
    modifier: Modifier = Modifier,
    imageCard: @Composable (interactionSource: InteractionSource) -> Unit,
    titleCard: @Composable (interactionSource: InteractionSource) -> Unit
) {
    val hasFocus = remember { mutableStateOf(false) }

    Box(
        modifier = modifier
            .focusable(true)
            .focusRequester(focusRequester { hasFocus.value = it })
            .onFocusChanged { hasFocus.value = it }
    ) {
        imageCard(interactionSource)
        if (hasFocus.value) {
            titleCard(interactionSource)
        }
    }
}

接下来,可以在StandardCardLayout中使用Box布局,将titleCard放在imageCard上方。当hasFocus的值为true时,显示titleCard

然后,在TvCardTest中,将StandardCardLayouttitleCard参数更改为使用这个更新后的版本:

@Composable
fun TvCardTest() {
    TvLazyRow(
        Modifier.padding(16.dp, 16.dp),
        horizontalArrangement = Arrangement.spacedBy(10.dp)
    ) {
        items(4) { value ->
            StandardCardLayout(
                modifier = Modifier
                    .size(96.dp, 80.dp)
                    .padding(6.dp),
                imageCard = { interactionSource ->
                    CardLayoutDefaults.ImageCard(
                        onClick = { /*TODO*/ },
                        interactionSource = interactionSource,
                        glow = glow(
                            focusedGlow = Glow(
                                elevation = 10.dp,
                                elevationColor = Color.Red
                            )
                        ),
                        border = border(focusedBorder = Border.None),
                    ) {
                        Image(
                            painter = painterResource(id = R.drawable.app_icon_your_company),
                            contentDescription = null
                        )
                    }
                },
                titleCard = { interactionSource ->
                    CardLayoutDefaults.TitleCard(
                        onClick = { /*TODO*/ },
                        interactionSource = interactionSource
                    ) {
                        if (hasFocus.value) {
                            Text(text = "Title")
                        } else {
                            Spacer(modifier = Modifier.height(16.dp))
                        }
                    }
                }
            )
        }
    }
}

这样,当卡片获得焦点时,标题会显示出来,失去焦点时则会隐藏。

请注意,以上代码为伪代码,可能并不能直接运行。具体实现的细节可能会因为Compose TV的版本变动而有所调整。请根据官方文档和示例进行具体的实现。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^