Compose笔记(七十)--movableContentWithReceiverOf

这一节主要了解一下Compose中的movableContentWithReceiverOf,在Jetpack Compose开发中movableContentWithReceiverOf是用于创建可移动的组合内容的API,其核心作用是通过保留组合状态,实现将一段Composable内容封装为可在不同组合节点间"移动"的对象,避免重复重组和重新布局,提升性能。简单总结:

API:

MovableContent:可移动内容的核心类型,是封装后的Composable内容句柄

movableContentWithReceiverOf:创建带"接收者(Receiver)"的可移动内容,接收者可传递上下文数据

栗子:

Kotlin 复制代码
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.movableContentWithReceiverOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

data class ContentReceiver(
    val title: String,
    val color: Color,
    val count: Int 
)

@Composable
fun MovableContentWithReceiverDemo() {

    var count by remember { mutableStateOf(0) }
    var isRed by remember { mutableStateOf(false) }

    val movableContent = remember {
        movableContentWithReceiverOf<ContentReceiver> {
                Box(
                    modifier = Modifier
                        .size(200.dp)
                        .background(this.color.copy(alpha = 0.6f))
                        .padding(16.dp),
                    contentAlignment = androidx.compose.ui.Alignment.Center
                ) {
                    Column(horizontalAlignment = androidx.compose.ui.Alignment.CenterHorizontally) {
                        Text(text = this@movableContentWithReceiverOf.title, color = Color.White, fontSize = 18.sp)
                        Text(text = "计数:${this@movableContentWithReceiverOf.count}", color = Color.White, fontSize = 14.sp)
                    }
                }
        }
    }

    val receiver = ContentReceiver(
        title = "带接收者的可复用内容",
        color = if (isRed) Color.Red else Color.Green,
        count = count
    )

    Column(modifier = Modifier.padding(16.dp)) {
        movableContent(receiver)

        Button(
            onClick = { count++ },
            modifier = Modifier.padding(top = 16.dp)
        ) {
            Text(text = "增加计数")
        }

        Button(
            onClick = { isRed = !isRed },
            modifier = Modifier.padding(top = 8.dp)
        ) {
            Text(text = "切换颜色")
        }
    }
}
Kotlin 复制代码
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ExperimentalComposeApi
import androidx.compose.runtime.InternalComposeApi
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.runtime.movableContentWithReceiverOf

data class CardReceiver(
    val cardId: Int,
    val title: String,
    val desc: String,
    val bgColor: Color,
    val isSelected: Boolean
)

@OptIn(ExperimentalComposeApi::class, InternalComposeApi::class) 
@Composable
fun ReusableCardGroup() {

    var selectedCardId by remember { mutableStateOf(1) }
    var clickCount by remember { mutableStateOf(0) }


    val cardContent = remember {
        movableContentWithReceiverOf<CardReceiver> {
            Card(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(8.dp),
                shape = RoundedCornerShape(12.dp),
                border = if (this.isSelected) {
                    BorderStroke(2.dp, Color.Black) 
                } else {
                    BorderStroke(1.dp, Color.Gray) 
                },
                colors = CardDefaults.cardColors(
                    containerColor = this.bgColor.copy(alpha = if (this.isSelected) 0.9f else 0.7f)
                ),
                elevation = CardDefaults.cardElevation(
                    defaultElevation = if (this.isSelected) 8.dp else 2.dp
                )
            ) {
                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(16.dp),
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {

                    Text(
                        text = this@movableContentWithReceiverOf.title,
                        fontSize = 18.sp,
                        fontWeight = FontWeight.Bold,
                        color = Color.White
                    )

                    Text(
                        text = "${this@movableContentWithReceiverOf.desc} | 全局点击数:$clickCount",
                        fontSize = 14.sp,
                        color = Color.White.copy(alpha = 0.8f),
                        modifier = Modifier.padding(top = 4.dp)
                    )

                    Text(
                        text = "卡片ID:${this@movableContentWithReceiverOf.cardId}",
                        fontSize = 12.sp,
                        color = Color.White.copy(alpha = 0.6f),
                        modifier = Modifier.padding(top = 8.dp)
                    )
                }
            }
        }
    }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Top
    ) {
        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceEvenly,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Button(
                onClick = {
                    selectedCardId = if (selectedCardId == 1) 2 else 1
                },
                modifier = Modifier.padding(4.dp)
            ) {
                Text(text = "切换选中卡片")
            }

            Button(
                onClick = { clickCount++ },
                modifier = Modifier.padding(4.dp)
            ) {
                Text(text = "点击计数+1")
            }
        }

     
        Column(modifier = Modifier.fillMaxWidth()) {
            val receiver1 = CardReceiver(
                cardId = 1,
                title = "用户信息卡片",
                desc = "姓名:张三 | 年龄:25",
                bgColor = Color(0xFF64B5F6), // 蓝色
                isSelected = selectedCardId == 1
            )
          
            val card1 = cardContent(receiver1)



           
            val receiver2 = CardReceiver(
                cardId = 2,
                title = "订单信息卡片",
                desc = "订单号:20260117 | 金额:99元",
                bgColor = Color(0xFF81C784), // 绿色
                isSelected = selectedCardId == 2
            )
           
            cardContent(receiver2)
        }

       
    }
}


@Composable
fun MovableContentDemoApp() {
    Column(modifier = Modifier.fillMaxSize()) {
        Text(
            text = "movableContentWithReceiverOf Demo",
            fontSize = 20.sp,
            fontWeight = FontWeight.Bold,
            modifier = Modifier
                .align(Alignment.CenterHorizontally)
                .padding(16.dp)
        )
        ReusableCardGroup()
    }
}

注意:

1 必须配合remember使用:movableContentWithReceiverOf需在remember块内调用,以确保组合内容在重新组合时不会被回收。

2 接收者类型需明确 指定正确的接收者类型,否则无法访问布局相关的上下文。

3 参数传递需显式 依赖项应通过参数传入。

相关推荐
AOI小白新手上路19 分钟前
DRSformer·论文蒸馏笔记:可学习 Top-k 稀疏注意力去雨网络
网络·笔记·学习
AOI小白新手上路1 小时前
单幅图像去雨·论文蒸馏笔记:统一评测、方法谱系与 HQ-RAIN 新基准
笔记
糖炒栗子要加糖2 小时前
Windows 大量小文件复制太慢?使用 Robocopy 多线程加速
笔记·提升效率
2601_961146202 小时前
我的等离子表面处理设备周度保养折腾记录
笔记·设备·设备选型
小新科研测评2 小时前
文献管理工具同步太麻烦?2026年4款多端同步工具实测,换设备不丢笔记
论文阅读·人工智能·笔记·ai·电脑
龙仔7254 小时前
RustDesk 1.5.0 升级安装故障处理笔记
笔记
星恒随风4 小时前
Linux基础IO万字详解:从文件描述符fd到重定向、FILE缓冲机制及MiniShell升级
linux·运维·笔记·学习·状态模式
喜欢打篮球的普通人5 小时前
MiniMind 学习笔记(二十二):数学 Cookbook——把概率空间、期望、似然一次讲明白
人工智能·笔记·学习
乐橙开放平台5 小时前
通道国标 ID 贴反会串店:海康大华宇视摄像头怎么用 GB28181 统一接入?
网络·笔记·物联网·自动化·音视频·智能家居
ab1237687 小时前
Spring Integration 完整笔记 + 和 spring-integration-mqtt、Paho、EMQX 的关系
java·笔记·spring