如何自定义监听器实现ExoPlayer UI控件仅在onTouch触发时显示?
实现ExoPlayer仅在Touch事件触发时显示播放控制器
我来给你分享两种可行的实现方案,帮你定制ExoPlayer的控制器显示逻辑,满足「仅在Touch事件触发时才显示控件」的需求:
方案一:继承PlayerControlView修改默认行为
默认的PlayerControlView会在点击、长按、拖拽等交互时自动显示控制器,我们可以通过继承它并重写相关方法,拦截不需要的触发逻辑,只保留Touch事件的显示触发:
步骤1:创建自定义PlayerControlView子类
public class TouchOnlyControlView extends PlayerControlView { public TouchOnlyControlView(Context context) { super(context); initView(); } public TouchOnlyControlView(Context context, AttributeSet attrs) { super(context, attrs); initView(); } public TouchOnlyControlView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(); } private void initView() { // 禁用默认的点击/长按触发显示逻辑 setOnClickListener(v -> {}); setOnLongClickListener(v -> true); } @Override public boolean onTouchEvent(MotionEvent event) { // 仅在触摸按下(ACTION_DOWN)时显示控制器 if (event.getAction() == MotionEvent.ACTION_DOWN) { show(); // 调用父类的显示方法 } // 拦截后续事件,避免触发默认的点击/拖拽显示逻辑 return true; } @Override protected void onFinishInflate() { super.onFinishInflate(); // 拦截进度条拖拽的默认显示触发 SeekBar progressBar = findViewById(R.id.exo_progress); if (progressBar != null) { progressBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { // 仅处理进度更新,不触发控制器显示 getPlayer().seekTo((getPlayer().getDuration() * progress) / 100); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { // 空实现,避免触发默认的显示逻辑 } @Override public void onStopTrackingTouch(SeekBar seekBar) { // 空实现,或添加自定义逻辑 } }); } } }
步骤2:在布局中使用自定义控件
把布局里的com.google.android.exoplayer2.ui.PlayerControlView替换成你的自定义类:
<com.yourpackage.TouchOnlyControlView android:id="@+id/player_control_view" android:layout_width="match_parent" android:layout_height="wrap_content" app:player_layout_id="@layout/exo_player_view"/>
步骤3:绑定播放器
在代码中把播放器和自定义控制器绑定:
SimpleExoPlayer player = new SimpleExoPlayer.Builder(context).build(); TouchOnlyControlView controlView = findViewById(R.id.player_control_view); controlView.setPlayer(player);
方案二:完全自定义控制器布局(更灵活)
如果你想要完全掌控控制器的显示/隐藏逻辑,可以自己写控制器布局,手动处理触摸事件和播放器控制:
步骤1:创建自定义控制器布局
比如custom_player_control.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/custom_control_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="16dp" android:background="#80000000" android:visibility="gone"> <!-- 默认隐藏 --> <ImageButton android:id="@+id/play_pause_btn" android:layout_width="48dp" android:layout_height="48dp" android:src="@drawable/exo_icon_play"/> <SeekBar android:id="@+id/progress_seek_bar" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginHorizontal="16dp"/> <!-- 其他控件比如音量、全屏按钮等 --> </LinearLayout>
步骤2:在PlayerView布局中引入该控制器
<com.google.android.exoplayer2.ui.PlayerView android:id="@+id/player_view" android:layout_width="match_parent" android:layout_height="match_parent" app:controller_layout_id="@layout/custom_player_control"/>
步骤3:在代码中处理触摸和控制逻辑
PlayerView playerView = findViewById(R.id.player_view); LinearLayout controlLayout = findViewById(R.id.custom_control_layout); SimpleExoPlayer player = new SimpleExoPlayer.Builder(this).build(); playerView.setPlayer(player); // 播放器视图的触摸监听:仅在触摸按下时显示控制器 playerView.setOnTouchListener((v, event) -> { if (event.getAction() == MotionEvent.ACTION_DOWN) { controlLayout.setVisibility(View.VISIBLE); // 3秒后自动隐藏(播放状态下) new Handler(Looper.getMainLooper()).postDelayed(() -> { if (player.isPlaying()) { controlLayout.setVisibility(View.GONE); } }, 3000); } return true; // 消耗事件,避免触发默认点击逻辑 }); // 播放/暂停按钮逻辑 ImageButton playPauseBtn = findViewById(R.id.play_pause_btn); playPauseBtn.setOnClickListener(v -> { if (player.isPlaying()) { player.pause(); playPauseBtn.setImageResource(R.drawable.exo_icon_play); } else { player.play(); playPauseBtn.setImageResource(R.drawable.exo_icon_pause); // 播放时启动自动隐藏 new Handler(Looper.getMainLooper()).postDelayed(() -> { if (player.isPlaying()) { controlLayout.setVisibility(View.GONE); } }, 3000); } }); // 进度条拖拽逻辑 SeekBar seekBar = findViewById(R.id.progress_seek_bar); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { long duration = player.getDuration(); long position = (duration * progress) / 100; player.seekTo(position); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { // 拖拽时保持控制器显示 controlLayout.setVisibility(View.VISIBLE); } @Override public void onStopTrackingTouch(SeekBar seekBar) { // 拖拽结束后重新启动自动隐藏 new Handler(Looper.getMainLooper()).postDelayed(() -> { if (player.isPlaying()) { controlLayout.setVisibility(View.GONE); } }, 3000); } });
两种方案各有优劣:方案一适合想保留ExoPlayer默认控制器大部分功能,只修改显示触发逻辑的场景;方案二更灵活,适合需要完全自定义控制器样式和交互的场景。
内容的提问来源于stack exchange,提问作者Elnur Hajiyev
相关产品推荐
相关产品推荐

