You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SimpleExoPlayer的Player.EventListener失效,需实现播放时显示序号文本

解决ExoPlayer播放序列视频时显示当前播放序号的问题

我看了你的代码,问题主要出在Player的重复创建和错误的状态判断逻辑上,另外要实现“X of Y”的序号显示,得用正确的方式获取当前播放的媒体索引。下面一步步给你修正:

1. 修复Player监听器失效的问题

你在onCreate里创建了player并添加了eventListener,但在initializePlayer方法里又重新创建了一个新的player对象——这就导致之前添加的监听器被绑定到了旧的player实例上,新的player根本没有这个监听器,自然不会触发回调。

解决办法:把player的创建逻辑统一,不要重复创建。可以在initializePlayer里复用已经创建的player,或者把player的初始化移到initializePlayer里,再统一添加监听器。

2. 修正播放状态判断的错误

你在onPlayerStateChanged里用PlaybackStateCompat.ACTION_SKIP_TO_NEXT和ACTION_SKIP_TO_PREVIOUS来判断播放状态,这完全不对!这些常量是媒体会话的动作类型,而playbackState参数对应的是Player.STATE_*系列常量(比如Player.STATE_READY、Player.STATE_ENDED等)。要监听视频切换动作,应该用onPositionDiscontinuity回调,或者直接获取当前播放的媒体索引。

3. 实现“X of Y”的序号显示逻辑

因为你用了ConcatenatingMediaSource拼接多个视频,所以可以通过player.getCurrentMediaItemIndex()获取当前播放的视频索引(注意索引从0开始),总数量就是urlList.size()。然后在这些时机更新UI显示:

  • 播放器Timeline变化时(onTimelineChanged)
  • 播放位置跳变/切换视频时(onPositionDiscontinuity)
  • 播放器进入就绪状态时(onPlayerStateChanged)

修改后的完整代码示例

修正后的EventListener

Player.EventListener eventListener = new Player.EventListener() {
    @Override
    public void onTimelineChanged(Timeline timeline, Object manifest) {
        // 当Timeline变化(比如加载完序列视频)时更新序号
        updatePlaybackSequence();
    }

    @Override
    public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { }

    @Override
    public void onLoadingChanged(boolean isLoading) {
        progressBar.setVisibility(isLoading ? View.VISIBLE : View.GONE);
    }

    @Override
    public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
        // 播放器就绪时更新序号
        if (playbackState == Player.STATE_READY) {
            updatePlaybackSequence();
        }
    }

    @Override
    public void onRepeatModeChanged(int repeatMode) { }

    @Override
    public void onPlayerError(ExoPlaybackException error) { }

    @Override
    public void onPositionDiscontinuity() {
        // 切换上/下一个视频时更新序号
        updatePlaybackSequence();
    }

    @Override
    public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { }
};

// 新增更新序号的工具方法
private void updatePlaybackSequence() {
    if (player != null && urlList != null && !urlList.isEmpty()) {
        int currentIndex = player.getCurrentMediaItemIndex() + 1; // 转成从1开始的序号
        int totalCount = urlList.size();
        // 替换成你的TextView控件,显示"1 of 6"这类文本
        sequenceTextView.setText(String.format("%d of %d", currentIndex, totalCount));
    }
}

修正后的initializePlayer方法

private void initializePlayer() {
    simpleExoPlayerView.requestFocus();
    // 复用已创建的player,避免重复实例化
    if (player == null) {
        bandwidthMeter = new DefaultBandwidthMeter();
        TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
        trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
        LoadControl loadControl = new DefaultLoadControl();
        player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, loadControl);
        simpleExoPlayerView.setPlayer(player);
        // 确保监听器绑定到当前的player实例
        player.addListener(eventListener);
    }

    MediaSource[] mediaSources = new MediaSource[urlList.size()];
    for (int i = 0; i < urlList.size(); i++) {
        mediaSources[i] = buildMediaSource(Uri.parse(urlList.get(i)));
    }
    MediaSource mediaSource = mediaSources.length == 1 ? mediaSources[0] : new ConcatenatingMediaSource(mediaSources);
    player.prepare(mediaSource);
    player.setPlayWhenReady(true); // 准备完成后自动播放
}

修正后的onCreate方法

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_video);
    ButterKnife.bind(this);
    presenter = new VideosPresenter(this);
    simpleExoPlayerView = findViewById(R.id.player_view);
    mediaDataSourceFactory = new DefaultDataSourceFactory(this, Util.getUserAgent(this, getString(R.string.app_name)), null);
    presenter.getVideoUrls();
    // 移除提前创建player的逻辑,统一交给initializePlayer处理
}

额外注意事项

  • 记得在Activity销毁时释放player资源,避免内存泄漏:
@Override
protected void onDestroy() {
    super.onDestroy();
    if (player != null) {
        player.release();
        player = null;
    }
}
  • 如果你的ExoPlayer版本是2.16+,ExoPlayerFactory已被废弃,建议改用ExoPlayer.Builder创建实例:
player = new ExoPlayer.Builder(this)
        .setTrackSelector(trackSelector)
        .setLoadControl(loadControl)
        .build();

内容的提问来源于stack exchange,提问作者Ashish Tiwari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:08:52