JavaFX+VLCJ播放多路视频偶发IndexOutOfBoundsException如何解决
问题根因
该偶发异常由JavaFX布局计算时的节点引用混乱导致,核心错误点有两个:
- 你已经在初始化阶段给所有
ImageView设置了GridPane的行列约束,但在play按钮的点击回调中重复将同一批ImageView添加到GridPane的子节点列表,同一个节点多次加入父容器会导致布局系统维护的内部索引计算出错,就是你看到的Index -1 out of bounds报错。 - 你复用了同一个
ColumnConstraints、RowConstraints实例添加到所有行列,共享实例的属性变更会干扰布局计算,提升异常出现概率。
解决方案
1. 调整节点添加逻辑
初始化阶段就把所有ImageView加入GridPane,点击播放时仅处理播放器绑定和播放逻辑,不要操作布局节点的增删。
2. 每个行列单独创建约束实例
不要复用同一个约束对象,每个列、每个行都新建独立的ColumnConstraints、RowConstraints实例。
修改后的参考代码
import javafx.application.Application; import javafx.application.Platform; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.image.ImageView; import javafx.scene.layout.*; import javafx.stage.Stage; import java.util.ArrayList; import java.util.List; import uk.co.caprica.vlcj.factory.MediaPlayerFactory; import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer; import static uk.co.caprica.vlcj.javafx.videosurface.ImageViewVideoSurfaceFactory.videoSurfaceForImageView; public class VideoTest extends Application { MediaPlayerFactory mediaPlayerFactory; List<ImageView> imageViewList; GridPane gridPane; List<EmbeddedMediaPlayer> embeddedMediaPlayerList = new ArrayList<>(); public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage primaryStage) { mediaPlayerFactory = new MediaPlayerFactory(); for (int i = 0; i < 16; i++) { embeddedMediaPlayerList.add(mediaPlayerFactory.mediaPlayers().newEmbeddedMediaPlayer()); } AnchorPane anchorPane = new AnchorPane(); Button button = new Button("play"); int mVideoWidth = 187; int mVideoHeight = 331; button.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { for (int j = 0; j < 16; j++) { ImageView videoImageView = imageViewList.get(j); embeddedMediaPlayerList.get(j).videoSurface().set(videoSurfaceForImageView(videoImageView)); embeddedMediaPlayerList.get(j).media().play("xxx.avi"); } } }); gridPane = new GridPane(); int rows = 2; int cols = 8; // 每个行单独创建RowConstraints实例 for (int row = 0; row < rows; row++) { RowConstraints fillRow = new RowConstraints(); fillRow.setVgrow(Priority.ALWAYS); fillRow.setFillHeight(true); gridPane.getRowConstraints().add(fillRow); } // 每个列单独创建ColumnConstraints实例 for (int col = 0; col < cols; col++) { ColumnConstraints fillColumn = new ColumnConstraints(); fillColumn.setHgrow(Priority.ALWAYS); fillColumn.setFillWidth(true); gridPane.getColumnConstraints().add(fillColumn); } int i = 0; imageViewList = new ArrayList<>(); for (int j = 0; j < 16; j++) { ImageView iv = new ImageView(); // 提前设置ImageView的固定属性,不要放到播放回调里 iv.setPreserveRatio(true); iv.setFitWidth(mVideoWidth); iv.setFitHeight(mVideoHeight); GridPane.setHgrow(iv, Priority.ALWAYS); imageViewList.add(iv); } for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { GridPane.setConstraints(imageViewList.get(i), col, row); i++; } } // 初始化阶段就把所有ImageView加入GridPane gridPane.getChildren().addAll(imageViewList); AnchorPane.setTopAnchor(gridPane, 100.0); anchorPane.getChildren().add(gridPane); anchorPane.getChildren().add(button); Scene scene = new Scene(anchorPane, 1080, 800); primaryStage.setScene(scene); // 窗口关闭时释放资源,避免内存泄漏 primaryStage.setOnCloseRequest(e -> { embeddedMediaPlayerList.forEach(EmbeddedMediaPlayer::release); mediaPlayerFactory.release(); Platform.exit(); }); primaryStage.show(); } }
内容的提问来源于stack exchange,提问作者pkxutao
相关产品推荐
相关产品推荐

