Exoplayer+Exomedia打造自定义播放器(二)

  • Exomedia源码分析

    VideoControls 播放器基类

    继承自RelativeLayout,播放器基类。作用是提供一些默认的播放器基本组件;提供反射控制器布局、显示加载进度、播放器隐藏显示动画等方法;设置监听回调等等;

    • 基类只提供abstract方法,由子类具体实现。
    public abstract class VideoControls extends RelativeLayout {
    
        //默认的视频播放器组件
        protected TextView currentTimeTextView;
        protected TextView endTimeTextView;
        protected TextView titleTextView;
        protected TextView subTitleTextView;
        protected TextView descriptionTextView;
        ...
        
        /**
         * 反射默认播放组件(开始/暂停按钮,快进后退,进度条等)
         */
        protected void retrieveViews() {
            currentTimeTextView = (TextView) findViewById(R.id.exomedia_controls_current_time);
            endTimeTextView = (TextView) findViewById(R.id.exomedia_controls_end_time);
            titleTextView = (TextView) findViewById(R.id.exomedia_controls_title);
            subTitleTextView = (TextView) findViewById(R.id.exomedia_controls_sub_title);
            descriptionTextView = (TextView) findViewById(R.id.exomedia_controls_description);
        }
        ...
        
        //设置进度条
        public abstract void setPosition(@IntRange(from = 0) long position);
    
        //设置总时长
        public abstract void setDuration(@IntRange(from = 0) long duration);
    
        //更新进度
        public abstract void updateProgress(@IntRange(from = 0) long position, @IntRange(from = 0) long duration, @IntRange(from = 0, to = 100) int bufferPercent);
    
        //反射播放控制器总体布局
        @LayoutRes
        protected abstract int getLayoutResource();
        ...
        
    }

    VideoControlsMobile 默认播放器

    布局文件

    Exoplayer+Exomedia打造自定义播放器(二)_第1张图片
    1. exomedia_default_controls_mobile.xml
    2. 布局分为三部分:上方视频信息框,中间加载进度框,下方控制器(对应id:exomedia_controls_text_container/exomedia_controls_video_loading/exomedia_controls_interactive_container)
    3. 默认播放器中视频信息框设置为不显示.
    Java

    继承VideoControls类,具体实现父类的虚拟方法.

    public class VideoControlsMobile extends VideoControls {
        //进度条
        protected SeekBar seekBar;
        
        //用户手动拖拽进度条
        protected boolean userInteracting = false;
        
        //反射布局
        @Override
        protected int getLayoutResource() {
            return R.layout.exomedia_default_controls_mobile;
        }
        
        @Override
        public void setPosition(@IntRange(from = 0L) long position) {
            //继承自VideoView : 当前视频时间
            currentTimeTextView.setText(TimeFormatUtil.formatMs(position));
            seekBar.setProgress((int) position);
        }
    
        @Override
        public void setDuration(@IntRange(from = 0L) long duration) {
            if (duration != seekBar.getMax()) {
                //继承自VideoView : 视频长度
                endTimeTextView.setText(TimeFormatUtil.formatMs(duration));
                seekBar.setMax((int) duration);
            }
        }
    
        @Override
        public void updateProgress(@IntRange(from = 0L) long position, @IntRange(from = 0L) long duration, @IntRange(from = 0L, to = 100L) int bufferPercent) {
            if (!userInteracting) {
                //设置seekbar的缓冲进度和真实进度的关系
                seekBar.setSecondaryProgress((int) (seekBar.getMax() * ((float) bufferPercent / 100)));
                seekBar.setProgress((int) position);
                currentTimeTextView.setText(TimeFormatUtil.formatMs(position));
            }
        }
        ...
    }

    具体实现自行参考源码,这里不再赘述。

    自定义播放器

    Exomedia作为模块导入

    github下载zip文件,把其中的library作为module导入工程即可.

    布局文件

    Exoplayer+Exomedia打造自定义播放器(二)_第2张图片

    1. 复制默认播放器布局文件exomedia_default_controls_mobile.xml文件并重命名.
    2. 由于继承VideoControls类,基类中已经设置了对默认布局文件的引用,故不要删除已经存在的代码:不需要的按钮只需设置android:visibility="gone",要改变控件样式把原来id赋给新的控件即可。 具体代码请根据个人需求自行编写,下面举例说明:
    
    <LinearLayout
            android:id="@+id/exomedia_controls_text_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:background="@drawable/exomedia_default_controls_text_background"
            android:orientation="vertical"
            android:paddingBottom="16dp"
            android:paddingLeft="16dp"
            android:paddingRight="16dp"
            android:paddingTop="16dp"
            android:visibility="gone">
    
            <TextView
                android:id="@+id/exomedia_controls_title"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="@android:color/white"
                android:textSize="16sp"
                android:textStyle="bold"
                tools:text="The Video Title"/>
                ...
        LinearLayout>
    
    
        <RelativeLayout
            android:id="@+id/exomedia_controls_play_pause_btn_container"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:background="@drawable/shape_circle_enable_pausebtn">
    
            
            <ImageButton
                android:id="@+id/exomedia_controls_play_pause_btn"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@android:color/transparent"
                android:minHeight="@dimen/exomedia_min_button_height"
                android:minWidth="@dimen/exomedia_min_button_height"
                app:srcCompat="@drawable/exomedia_ic_play_arrow_white"
                tools:ignore="ContentDescription"/>
    
        RelativeLayout>
    • 背景黑色为了方便设计,真正使用时请去掉,否则会遮挡住视频播放。

    Java

    1. 与默认播放器的实现方法一样,自定义MyVideoControls extends VideoControls,并让该类反射自定义的布局文件实现定制播放器的效果。
    2. 如果在自定义的布局文件中添加了控件,在此对其业务逻辑进行处理。
    public class MyVideoControlsMobile extends VideoControls {
    
        //自己添加的开始/暂停按钮的容器
        protected RelativeLayout pauseBtnContainer;
    
        //反射自己的布局
        @Override
        protected int getLayoutResource() {
            return R.layout.my_exomedia_controls_mobile;
        }
        
        @Override
        protected void animateVisibility(boolean toVisible) {
            if (isVisible == toVisible) {
                return;
            }
    
            if (!hideEmptyTextContainer || !isTextContainerEmpty()) {
                textContainer.startAnimation(new TopViewHideShowAnimation(textContainer, toVisible, CONTROL_VISIBILITY_ANIMATION_LENGTH));
            }
    
            if (!isLoading) {
                controlsContainer.startAnimation(new BottomViewHideShowAnimation(controlsContainer, toVisible, CONTROL_VISIBILITY_ANIMATION_LENGTH));
            }
    
            //设置自定义的开始/暂停按钮的可见性
            if (toVisible) {
                pauseBtnContainer.setVisibility(View.VISIBLE);
            } else {
                pauseBtnContainer.setVisibility(View.GONE);
            }
            isVisible = toVisible;
            onVisibilityChanged();
        }
    
        @Override
        public void showLoading(boolean initialLoad) {
            if (isLoading) {
                return;
            }
    
            isLoading = true;
            loadingProgressBar.setVisibility(View.VISIBLE);
    
            if (initialLoad) {
                controlsContainer.setVisibility(View.GONE);
                //控制器不可见时,开始/暂停按钮也不可见
                pauseBtnContainer.setVisibility(View.GONE);
            } else {
                //按钮不可以点击
                pauseBtnContainer.setBackgroundResource(R.drawable.shape_circle_disable_pausebtn);
                playPauseButton.setEnabled(false);
                previousButton.setEnabled(false);
                nextButton.setEnabled(false);
                return;
            }
            show();
        }
        
        @Override
        public void finishLoading() {
            if (!isLoading) {
                return;
            }
    
            isLoading = false;
            loadingProgressBar.setVisibility(View.GONE);
            //控制器可见时,开始/暂停按钮也可见
            pauseBtnContainer.setVisibility(View.VISIBLE);
            controlsContainer.setVisibility(View.VISIBLE);
    
            playPauseButton.setEnabled(true);
            previousButton.setEnabled(enabledViews.get(com.devbrackets.android.exomedia.R.id.exomedia_controls_previous_btn, true));
            nextButton.setEnabled(enabledViews.get(com.devbrackets.android.exomedia.R.id.exomedia_controls_next_btn, true));
    
            pauseBtnContainer.setBackgroundResource(R.drawable.shape_circle_enable_pausebtn);
            updatePlaybackState(videoView != null && videoView.isPlaying());
        }
        
    }

    在此仅说明了定制播放器的流程,代码请根据具体业务需求自行实现。

    深度定制

    把Exomedia作为模组导入以后,直接修改与VideoControls相关的类和xml,以实现更加灵活、扩展性更好的自定义播放器。

    其他

    相关文章请点击下面链接:

    • Exoplayer+Exomedia打造自定义播放器(一)
    • Exoplayer+Exomedia之玩转视频播放事件监听

你可能感兴趣的:(Android开发)