Inhalt von Exoplayern überschneiden sich gegenseitig
Posted: 29 Jul 2025, 04:44
Ich habe eine ähnliche Frage gestellt, die ungelöst ist. Ich frage noch einmal mit weiteren Informationen. In meiner Android -App gibt es eine Netzansicht, die aus Gitterartikeln besteht, die ein Video oder ein Bild sein können. Im Folgenden von 3 Elementen. Der erste und der 3. sind Videos. Die Ausrichtung des zweiten Videos ist Porträt, und sie überläuft den Rand seines übergeordneten Containers in das erste Video. nutzen. Ich stellte fest, dass es nur dann passiert, wenn sich zwei Exoplayer gegenseitig überlappen. Im folgenden Raster ist der 4. Element auch ein Porträtvideo. Das Element oben ist ein Bild und das Video, das es in seinem Container fein bleibt.
Code: Select all
@Composable
fun MediaPreviewGrid(
mediaItems: List,
viewModel: TweetViewModel,
onFullScreenVideo: ((String, MimeiId) -> Unit)? = null, // Callback for full-screen video
) {
val tweet by viewModel.tweetState.collectAsState()
val navController = LocalNavController.current
val maxItems = when (mediaItems.size) {
1 -> 1
2, 3 -> mediaItems.size
else -> 4
}
// Set up sequential playback for multiple videos
val videoMids = remember {
limitedMediaList.mapIndexedNotNull { index, item ->
if (inferMediaTypeFromAttachment(item) == MediaType.Video) item.mid else null
}
}
Box(
modifier = Modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.CenterHorizontally)
) {
when (limitedMediaList.size) {
1 -> {
val aspectRatio = if (aspectRatioOf(limitedMediaList[0]) > 0.8f) {
aspectRatioOf(limitedMediaList[0])
} else {
0.8f
}
MediaItemView(
limitedMediaList,
modifier = Modifier
.fillMaxWidth()
.aspectRatio(aspectRatio)
.clipToBounds()
.clickable {
val params = MediaViewerParams(
mediaItems.map {
MediaItem(
getMediaUrl(it.mid, tweet.author?.baseUrl.orEmpty()).toString(),
it.type
)
}, 0, tweet.mid, tweet.authorId
)
navController.navigate(NavTweet.MediaViewer(params))
},
index = 0,
numOfHiddenItems = if (mediaItems.size > maxItems) mediaItems.size - maxItems else 0,
autoPlay = firstVideoIndex == 0,
inPreviewGrid = true,
viewModel = viewModel,
onFullScreenVideo = onFullScreenVideo
)
}
2 -> {}
3 -> {}
4 -> {}
}
}
}
@Composable
fun MediaItemView(
mediaItems: List,
modifier: Modifier = Modifier,
index: Int,
numOfHiddenItems: Int = 0, // add a PLUS sign to indicate more items not shown
autoPlay: Boolean = false, // autoplay first video item, index 0
inPreviewGrid: Boolean = true, // use real aspectRatio when not displaying in preview grid.
viewModel: TweetViewModel,
onFullScreenVideo: ((String, MimeiId) -> Unit)? = null // Callback for full-screen video
) {
val tweet by viewModel.tweetState.collectAsState()
val attachments = mediaItems.map {
val inferredType = inferMediaTypeFromAttachment(it)
val mediaUrl = getMediaUrl(it.mid, tweet.author?.baseUrl.orEmpty()).toString()
MediaItem(mediaUrl, inferredType)
}
val attachment = attachments[index]
val navController = LocalNavController.current
Box(
modifier = modifier
.background(Color.Gray.copy(alpha = 0.1f))
.clipToBounds(),
contentAlignment = Alignment.Center
) {
when (attachment.type) {
MediaType.Image -> {
// Use a Box with clickable modifier to handle image clicks
Box(
modifier = modifier
.clipToBounds()
.clickable {
goto(index)
}
) {
ImageViewer(
attachment.url,
modifier = Modifier.fillMaxSize(),
enableLongPress = false // Disable long press to allow clickable to work
)
}
}
MediaType.Video -> {
VideoPreview(
url = attachment.url,
modifier = modifier,
index = index,
autoPlay = autoPlay,
inPreviewGrid = inPreviewGrid,
aspectRatio = mediaItems[index].aspectRatio,
callback = { goto(index) },
videoMid = mediaItems[index].mid
)
}
MediaType.Audio -> {
val backgroundModifier = if (index % 2 != 0) { // Check if index is odd
modifier.background(Color.Black.copy(alpha = 0.05f)) // Slightly darker background
} else {
modifier
}
Box(
modifier = backgroundModifier
.clipToBounds()
.clickable {
goto(index)
}
) {
AudioPreview(mediaItems, index, Modifier.fillMaxSize(), tweet)
}
}
else -> { // add link to download other file type
BlobLink(mediaItems[index], attachment.url, modifier)
}
}
if (numOfHiddenItems > 0) {
/**
* Show a PLUS sign and number to indicate more items not shown
* */
Box(
modifier = Modifier
.matchParentSize()
.background(Color(0x40FFFFFF)), // Lighter shaded background
contentAlignment = Alignment.Center
) {
Row(modifier = Modifier.align(Alignment.Center))
{
Icon(
imageVector = Icons.Outlined.Add,
contentDescription = null,
tint = Color.White,
modifier = Modifier
.size(50.dp)
.alpha(0.8f)
)
Text(
text = numOfHiddenItems.toString(),
color = Color.White,
fontSize = 50.sp, // Adjust this value as needed
textAlign = TextAlign.Center,
modifier = Modifier
.alpha(0.8f)
)
}
}
}
}
}
< /code>
@Composable
fun VideoPreview(
url: String,
modifier: Modifier,
index: Int,
autoPlay: Boolean = false,
inPreviewGrid: Boolean = true,
aspectRatio: Float?,
callback: (Int) -> Unit,
videoMid: MimeiId? = null
) {
val context = LocalContext.current
var isVideoVisible by remember { mutableStateOf(false) }
var isMuted by remember { mutableStateOf(preferenceHelper.getSpeakerMute()) }
var isLoading by remember {
mutableStateOf(videoMid?.let { !VideoManager.isVideoPreloaded(it) } ?: true)
}
val exoPlayer = remember(url, videoMid) {
if (videoMid != null) {
VideoManager.getVideoPlayer(context, videoMid, url)
} else {
createExoPlayer(context, url, MediaType.Video)
}
}
// Preload video if not already cached
Box(
modifier = modifier
.clipToBounds()
.background(MaterialTheme.colorScheme.surfaceVariant) // Material3 surface variant for loading background
.onGloballyPositioned { layoutCoordinates ->
isVideoVisible = isElementVisible(layoutCoordinates)
}
.clickable {
// Auto-start video in full screen
callback(index)
}
) {
AndroidView(
factory = {
PlayerView(context).apply {
player = exoPlayer
useController = false // No controls in preview mode
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
// Set background color to light gray (Material3 surface variant equivalent)
setBackgroundColor(android.graphics.Color.rgb(245, 245, 245))
// Show buffering indicator
setShowBuffering(PlayerView.SHOW_BUFFERING_ALWAYS)
}
},
modifier = Modifier.fillMaxWidth()
)
// Show loading indicator when video is loading
if (isLoading) {
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.surfaceVariant),
contentAlignment = androidx.compose.ui.Alignment.Center
) {
CircularProgressIndicator(
modifier = Modifier.size(32.dp),
color = MaterialTheme.colorScheme.primary
)
}
}
}
}