Jetpack Compose如何将rememberLauncherForActivityResult封装为通用工具
方案一:封装Compose专用文件选择Hook(推荐)
该方案完全符合Compose生命周期管理规则,无需持有Activity实例,调用灵活,天然支持自定义MIME类型,是Compose场景下的最优解。
封装代码示例:
import android.net.Uri import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember /** * 通用文件选择器封装 * @return 第一个返回值为选中的文件Uri,第二个为启动选择器的方法,调用时传入对应MIME类型即可 */ @Composable fun rememberFilePicker(): Pair<Uri?, (mimeType: String) -> Unit> { val selectedUri = remember { mutableStateOf<Uri?>(null) } val pickerLauncher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri -> selectedUri.value = uri } val launchPicker = { mimeType: String -> pickerLauncher.launch(mimeType) } return selectedUri.value to launchPicker }
原有业务组件改造后的调用示例:
@OptIn(ExperimentalCoilApi::class) @Composable fun ContactPagePhoto() { // 直接调用封装好的工具,拿到选中的Uri和启动方法 val (selectedImageUri, launchFilePicker) = rememberFilePicker() Box( contentAlignment = Alignment.Center, modifier = Modifier .layoutId("userPhoto") .fillMaxHeight(0.4f) .fillMaxWidth(1f) ) { selectedImageUri?.let { Image( modifier = Modifier .align(Alignment.TopCenter) .fillMaxSize(1f), contentDescription = "UserImage", contentScale = ContentScale.Crop, painter = rememberImagePainter(data = it) ) } } TextButton( onClick = { // 传入图片MIME类型实现选图,后续选其他类型文件直接替换对应MIME即可 launchFilePicker("image/*") }, modifier = Modifier.layoutId("changePhoto") ) { Text( text = "Change Photo", color = Color.Black ) } }
不同文件类型的MIME参考:
- 选PDF:
launchFilePicker("application/pdf") - 选视频:
launchFilePicker("video/*") - 选所有类型文件:
launchFilePicker("*/*")
该方案的生命周期和调用它的Composable组件完全绑定,组件销毁时会自动回收启动器,不会产生内存泄漏风险。
方案二:非Compose场景下的Activity依赖封装
如果需要在非Compose的业务逻辑中调用,可以封装为依赖ComponentActivity的工具类,注意必须在Activity的onStart生命周期前初始化,否则会抛出注册异常。
封装代码示例:
import android.net.Uri import androidx.activity.ComponentActivity import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts class FilePickerHelper(activity: ComponentActivity) { private var onFilePicked: ((Uri?) -> Unit)? = null private val pickerLauncher: ActivityResultLauncher<String> = activity.registerForActivityResult( ActivityResultContracts.GetContent() ) { uri -> onFilePicked?.invoke(uri) } fun launch(mimeType: String, onResult: (Uri?) -> Unit) { this.onFilePicked = onResult pickerLauncher.launch(mimeType) } }
使用示例:
class MainActivity : ComponentActivity() { // 必须在onCreate前初始化 private val filePicker = FilePickerHelper(this) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 调用选图 filePicker.launch("image/*") { uri -> // 处理选中的Uri } } }
该方案仅建议在需要兼容非Compose场景时使用,注意不要在工具类中强持有Activity引用,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Saher Al-Sous
相关产品推荐
相关产品推荐

