饺子播放器Jzvd使用过程中遇到的问题汇总

饺子播放器使用问题请进群交流,如果失效,请添加cxw9681微信号进群

jzvd使用

使用jzvd控件之前,记住一句话:

无论是在xml中使用jzvd还是在代码中动态添加jzvd,jzvd的外面都要包一层,也就是在不能作为跟节点,要包在ViewGroup中。

xml中:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <cn.jzvd.JzvdStd
            android:id="@+id/jz"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </RelativeLayout>

</FrameLayout>

在代码中动态添加:

 RelativeLayout DECOR_VIEW = new RelativeLayout(jzvdContext);
 ViewGroup.LayoutParams fullLayout = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
 DECOR_VIEW.addView(this, fullLayout);
 //这个wm添加的是ViewGroup,不能直接添加jzvd
 mWindowManager.addView(DECOR_VIEW, getLayoutParams());

wm不能直接添加jzvd,需要在外边包一层。


WindowManager中添加饺子视频播放器

在WindowManager中添加jzvd,需要对jzvd进行改造(全屏播放也已经实现)。

代码如下:

package com.dynamic.widget.jzvd;

import android.content.Context;
import android.graphics.PixelFormat;
import android.media.MediaPlayer;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;

import androidx.annotation.NonNull;


import java.lang.reflect.Constructor;
import java.util.LinkedList;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Created by Nathen on 16/7/30.
 */
public abstract class Jzvd extends FrameLayout implements View.OnClickListener, SeekBar.OnSeekBarChangeListener, View.OnTouchListener {

    public static final String TAG = "JZVD";
    public static final int SCREEN_NORMAL = 0;
    public static final int SCREEN_FULLSCREEN = 1;
    public static final int STATE_IDLE = -1;
    public static final int STATE_NORMAL = 0;
    public static final int STATE_PREPARING = 1;
    public static final int STATE_PREPARING_CHANGE_URL = 2;
    public static final int STATE_PREPARING_PLAYING = 3;
    public static final int STATE_PREPARED = 4;
    public static final int STATE_PLAYING = 5;
    public static final int STATE_PAUSE = 6;
    public static final int STATE_AUTO_COMPLETE = 7;
    public static final int STATE_ERROR = 8;
    public static final int VIDEO_IMAGE_DISPLAY_TYPE_FILL_PARENT = 1;
    public static final int VIDEO_IMAGE_DISPLAY_TYPE_FILL_SCROP = 2;
    public static final int VIDEO_IMAGE_DISPLAY_TYPE_ORIGINAL = 3;
    public static Jzvd CURRENT_JZVD;
    public static LinkedList<ViewGroup> CONTAINER_LIST = new LinkedList<>();
    public static ViewGroup DECOR_VIEW;//模拟Activity的DecorView
    public static boolean TOOL_BAR_EXIST = true;
    public static boolean SAVE_PROGRESS = true;
    public static int VIDEO_IMAGE_DISPLAY_TYPE = 0;
    public static int ON_PLAY_PAUSE_TMP_STATE = 0;//这个考虑不放到库里,去自定义
    public static int backUpBufferState = -1;
    public static float PROGRESS_DRAG_RATE = 1f;//进度条滑动阻尼系数 越大播放进度条滑动越慢
    public int state = -1;
    public int screen = -1;
    public JZDataSource jzDataSource;
    public int widthRatio = 0;
    public int heightRatio = 0;
    public Class mediaInterfaceClass;
    public JZMediaInterface mediaInterface;
    public int videoRotation = 0;
    public int seekToManulPosition = -1;
    public long seekToInAdvance = 0;

    public ImageView startButton;
    public SeekBar progressBar;
    public ImageView fullscreenButton;
    public TextView currentTimeTextView, totalTimeTextView;
    public ViewGroup textureViewContainer;
    public ViewGroup topContainer, bottomContainer;
    public JZTextureView textureView;
    public boolean preloading = false;
    protected long gobakFullscreenTime = 0;//这个应该重写一下,刷新列表,新增列表的刷新,不打断播放,应该是个flag
    protected long gotoFullscreenTime = 0;
    protected Timer UPDATE_PROGRESS_TIMER;
    protected int mScreenWidth;
    protected int mScreenHeight;
    protected ProgressTimerTask mProgressTimerTask;
    protected boolean mTouchingProgressBar;
    protected float mDownX;
    protected float mDownY;
    protected boolean mChangeVolume;
    protected boolean mChangePosition;
    protected boolean mChangeBrightness;
    protected long mGestureDownPosition;
    protected long mSeekTimePosition;
    protected Context jzvdContext;
    protected long mCurrentPosition;
    /**
     * 如果不在列表中可以不加block
     */
    protected ViewGroup.LayoutParams blockLayoutParams;
    //原來(非全屏)视频的信息
    protected int blockIndex;
    protected int blockWidth;
    protected int blockHeight;

    //获取音频焦点参数  待商泰展开
    private final static int USAGE = 16;
    private final static int CONTENT_TYPE = 61;
    private AudioFocusManager mAudioFocusManager;
    WindowManager mWindowManager;

    public Jzvd(Context context) {
        super(context);
        init(context);
    }

    public Jzvd(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    /**
     * 增加准备状态逻辑
     */
    public static void goOnPlayOnResume() {
        if (CURRENT_JZVD != null) {
            LogUtil.e(TAG, "goOnPlayOnResume CURRENT_JZVD.state " + CURRENT_JZVD.state );
            if (CURRENT_JZVD.state == Jzvd.STATE_PAUSE) {
                if (ON_PLAY_PAUSE_TMP_STATE == STATE_PAUSE) {
                    CURRENT_JZVD.onStatePause();
                    CURRENT_JZVD.mediaInterface.pause();
                } else {
                    CURRENT_JZVD.onStatePlaying();
                    CURRENT_JZVD.mediaInterface.start();
                }
                ON_PLAY_PAUSE_TMP_STATE = 0;
            } else if (CURRENT_JZVD.state == Jzvd.STATE_PREPARING) {
                //准备状态暂停后的
                CURRENT_JZVD.startVideo();
            }
            if (CURRENT_JZVD.screen == Jzvd.SCREEN_FULLSCREEN) {
                //JZUtils.hideStatusBar(CURRENT_JZVD.jzvdContext);
                //JZUtils.hideSystemUI(CURRENT_JZVD.jzvdContext);
            }
        }
    }

    /**
     * 增加准备状态逻辑
     */
    public static void goOnPlayOnPause() {
        if (CURRENT_JZVD != null) {
            if (CURRENT_JZVD.state == Jzvd.STATE_AUTO_COMPLETE ||
                    CURRENT_JZVD.state == Jzvd.STATE_NORMAL ||
                    CURRENT_JZVD.state == Jzvd.STATE_ERROR) {
                Jzvd.releaseAllVideos();
            } else if (CURRENT_JZVD.state == Jzvd.STATE_PREPARING) {
                //准备状态暂停的逻辑
                Jzvd.setCurrentJzvd(CURRENT_JZVD);
                CURRENT_JZVD.state = STATE_PREPARING;
            } else {
                ON_PLAY_PAUSE_TMP_STATE = CURRENT_JZVD.state;
                CURRENT_JZVD.onStatePause();
                CURRENT_JZVD.mediaInterface.pause();
            }
        }
    }

    public static void releaseAllVideos() {
        Log.d(TAG, "releaseAllVideos");
        if (CURRENT_JZVD != null) {
            CURRENT_JZVD.reset();
            CURRENT_JZVD = null;
        }
        CONTAINER_LIST.clear();
    }

    public static boolean backPress() {
        Log.d(TAG, "backPress  CONTAINER_LIST.size() = " + CONTAINER_LIST.size());
        if (!CONTAINER_LIST.isEmpty() && CURRENT_JZVD != null) {//判断条件,因为当前所有goBack都是回到普通窗口
            CURRENT_JZVD.gotoNormalScreen();
            return true;
        }
        return false;
    }

    public static void setCurrentJzvd(Jzvd jzvd) {
        if (CURRENT_JZVD != null) CURRENT_JZVD.reset();
        CURRENT_JZVD = jzvd;
    }

    public static void setTextureViewRotation(int rotation) {
        if (CURRENT_JZVD != null && CURRENT_JZVD.textureView != null) {
            CURRENT_JZVD.textureView.setRotation(rotation);
        }
    }

    public static void setVideoImageDisplayType(int type) {
        Jzvd.VIDEO_IMAGE_DISPLAY_TYPE = type;
        if (CURRENT_JZVD != null && CURRENT_JZVD.textureView != null) {
            CURRENT_JZVD.textureView.requestLayout();
        }
    }

    public abstract int getLayoutId();

    public void init(Context context) {
        View.inflate(context, getLayoutId(), this);
        jzvdContext = context;
        startButton = findViewById(R.id.start);
        fullscreenButton = findViewById(R.id.fullscreen);
        progressBar = findViewById(R.id.bottom_seek_progress);
        currentTimeTextView = findViewById(R.id.current);
        totalTimeTextView = findViewById(R.id.total);
        bottomContainer = findViewById(R.id.layout_bottom);
        textureViewContainer = findViewById(R.id.surface_container);
        topContainer = findViewById(R.id.layout_top);

        if (startButton == null) {
            startButton = new ImageView(context);
        }
        if (fullscreenButton == null) {
            fullscreenButton = new ImageView(context);
        }
        if (progressBar == null) {
            progressBar = new SeekBar(context);
        }
        if (currentTimeTextView == null) {
            currentTimeTextView = new TextView(context);
        }
        if (totalTimeTextView == null) {
            totalTimeTextView = new TextView(context);
        }
        if (bottomContainer == null) {
            bottomContainer = new LinearLayout(context);
        }
        if (textureViewContainer == null) {
            textureViewContainer = new FrameLayout(context);
        }
        if (topContainer == null) {
            topContainer = new RelativeLayout(context);
        }

        startButton.setOnClickListener(this);
        fullscreenButton.setOnClickListener(this);
        progressBar.setOnSeekBarChangeListener(this);
        bottomContainer.setOnClickListener(this);
        textureViewContainer.setOnClickListener(this);
        textureViewContainer.setOnTouchListener(this);

        mScreenWidth = getContext().getResources().getDisplayMetrics().widthPixels;
        mScreenHeight = getContext().getResources().getDisplayMetrics().heightPixels;

        state = STATE_IDLE;

        //音频焦点初始化
        mAudioFocusManager = AudioFocusManager.getInstance();
        mAudioFocusManager.init(context);
        mAudioFocusManager.setAudioFocusListener(audioFocusListener);

        mWindowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    }

    public void setUp(String url, String title) {
        setUp(new JZDataSource(url, title), SCREEN_NORMAL);
    }

    public void setUp(String url, String title, int screen) {
        setUp(new JZDataSource(url, title), screen);
    }

    public void setUp(JZDataSource jzDataSource, int screen) {
        setUp(jzDataSource, screen, JZMediaSystem.class);
    }

    public void setUp(String url, String title, int screen, Class mediaInterfaceClass) {
        setUp(new JZDataSource(url, title), screen, mediaInterfaceClass);
    }

    public void setUp(JZDataSource jzDataSource, int screen, Class mediaInterfaceClass) {
        this.jzDataSource = jzDataSource;
        this.screen = screen;
        onStateNormal();
        this.mediaInterfaceClass = mediaInterfaceClass;
    }

    public void setMediaInterface(Class mediaInterfaceClass) {
        reset();
        this.mediaInterfaceClass = mediaInterfaceClass;
    }

    @Override
    public void onClick(View v) {
        Log.e(TAG, "onClick  v.getId  " + v.getId() + "   hashCode  " +hashCode());
        int i = v.getId();
        if (i == R.id.start) {
            clickStart();
        } else if (i == R.id.fullscreen) {
            clickFullscreen();
        }
    }

    protected void clickFullscreen() {
        Log.i(TAG, "onClick fullscreen [" + this.hashCode() + "] ");
        if (state == STATE_AUTO_COMPLETE) return;
        if (screen == SCREEN_FULLSCREEN) {
            Log.d(TAG, "backPress [" + this.hashCode() + "] ");
            //quit fullscreen
            backPress();
        } else {
            Log.d(TAG, "toFullscreenActivity [" + this.hashCode() + "] ");
            gotoFullscreen();
        }
    }

    protected void clickStart() {
        Log.i(TAG, "onClick start [" + this.hashCode() + "]   state " + state);
        if (jzDataSource == null || jzDataSource.urlsMap.isEmpty() || jzDataSource.getCurrentUrl() == null) {
            Log.d(TAG, "onClick start, play video need url");
            return;
        }
        if (state == STATE_NORMAL) {
            startVideo();
        } else if (state == STATE_PLAYING) {
            Log.d(TAG, "pauseVideo [" + this.hashCode() + "] ");
            mediaInterface.pause();
            onStatePause();
        } else if (state == STATE_PAUSE) {
            mediaInterface.start();
            onStatePlaying();
        } else if (state == STATE_AUTO_COMPLETE) {
            startVideo();
        }
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        int id = v.getId();
        if (id == R.id.surface_container) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    touchActionDown(x, y);
                    break;
                case MotionEvent.ACTION_MOVE:
                    touchActionMove(x, y);
                    break;
                case MotionEvent.ACTION_UP:
                    touchActionUp();
                    break;
            }
        }
        return false;
    }

    protected void touchActionUp() {
        Log.i(TAG, "onTouch surfaceContainer actionUp [" + this.hashCode() + "] ");
        mTouchingProgressBar = false;

        if (mChangePosition) {
            mediaInterface.seekTo(mSeekTimePosition);
            long duration = getDuration();
            int progress = (int) (mSeekTimePosition * 100 / (duration == 0 ? 1 : duration));
            progressBar.setProgress(progress);
        }
        if (mChangeVolume) {
            //change volume event
        }
        startProgressTimer();
    }

    protected void touchActionMove(float x, float y) {
        Log.i(TAG, "onTouch surfaceContainer actionMove [" + this.hashCode() + "] ");
        float deltaX = x - mDownX;
        float deltaY = y - mDownY;
        if (screen == SCREEN_FULLSCREEN) {
            //拖动的是NavigationBar和状态栏
            if (mDownX > JZUtils.getScreenWidth(getContext()) || mDownY < JZUtils.getStatusBarHeight(getContext())) {
                return;
            }
        }
        if (mChangePosition) {
            long totalTimeDuration = getDuration();
            if (PROGRESS_DRAG_RATE <= 0) {
                Log.d(TAG, "error PROGRESS_DRAG_RATE value");
                PROGRESS_DRAG_RATE = 1f;
            }
            mSeekTimePosition = (int) (mGestureDownPosition + deltaX * totalTimeDuration / (mScreenWidth * PROGRESS_DRAG_RATE));
            if (mSeekTimePosition > totalTimeDuration)
                mSeekTimePosition = totalTimeDuration;
            String seekTime = JZUtils.stringForTime(mSeekTimePosition);
            String totalTime = JZUtils.stringForTime(totalTimeDuration);

            //showProgressDialog(deltaX, seekTime, mSeekTimePosition, totalTime, totalTimeDuration);
        }
        if (mChangeVolume) {
            //调节音量,暂不支持
        }

        if (mChangeBrightness) {
           //调节亮度,暂不支持
        }
    }

    protected void touchActionDown(float x, float y) {
        Log.i(TAG, "onTouch surfaceContainer actionDown [" + this.hashCode() + "] ");
        mTouchingProgressBar = true;

        mDownX = x;
        mDownY = y;
        mChangeVolume = false;
        mChangePosition = false;
        mChangeBrightness = false;
    }

    public void onStateNormal() {
        Log.i(TAG, "onStateNormal " + " [" + this.hashCode() + "] ");
        state = STATE_NORMAL;
        cancelProgressTimer();
        if (mediaInterface != null) mediaInterface.release();
    }

    public void onStatePreparing() {
        Log.i(TAG, "onStatePreparing " + " [" + this.hashCode() + "] ");
        state = STATE_PREPARING;
        resetProgressAndTime();
    }

    public void onStatePreparingPlaying() {
        Log.i(TAG, "onStatePreparingPlaying " + " [" + this.hashCode() + "] ");
        state = STATE_PREPARING_PLAYING;
    }

    public void onStatePreparingChangeUrl() {
        Log.i(TAG, "onStatePreparingChangeUrl " + " [" + this.hashCode() + "] ");
        state = STATE_PREPARING_CHANGE_URL;

        releaseAllVideos();
        startVideo();

//        mediaInterface.prepare();
    }

    public void changeUrl(JZDataSource jzDataSource, long seekToInAdvance) {
        this.jzDataSource = jzDataSource;
        this.seekToInAdvance = seekToInAdvance;
        onStatePreparingChangeUrl();
    }

    public void onPrepared() {
        Log.i(TAG, "onPrepared " + " [" + this.hashCode() + "] ");
        state = STATE_PREPARED;
        if (!preloading) {
            mediaInterface.start();//这里原来是非县城
            preloading = false;
        }
        if (jzDataSource.getCurrentUrl().toString().toLowerCase().contains("mp3") ||
                jzDataSource.getCurrentUrl().toString().toLowerCase().contains("wma") ||
                jzDataSource.getCurrentUrl().toString().toLowerCase().contains("aac") ||
                jzDataSource.getCurrentUrl().toString().toLowerCase().contains("m4a") ||
                jzDataSource.getCurrentUrl().toString().toLowerCase().contains("wav")) {
            onStatePlaying();
        }
    }

    public void startPreloading() {
        preloading = true;
        startVideo();
    }

    /**
     * 如果STATE_PREPARED就播放,如果没准备完成就走正常的播放函数startVideo();
     */
    public void startVideoAfterPreloading() {
        if (state == STATE_PREPARED) {
            mediaInterface.start();
        } else {
            preloading = false;
            startVideo();
        }
    }

    public void onStatePlaying() {
        Log.i(TAG, "onStatePlaying " + " [" + this.hashCode() + "] ");
        if (state == STATE_PREPARED) {//如果是准备完成视频后第一次播放,先判断是否需要跳转进度。
            Log.d(TAG, "onStatePlaying:STATE_PREPARED ");
            boolean requestAudioFocus = mAudioFocusManager.getAudioFocus(USAGE, CONTENT_TYPE);
            Log.d(TAG, "onStatePlaying:requestAudioFocus " + requestAudioFocus);
            if (seekToInAdvance != 0) {
                mediaInterface.seekTo(seekToInAdvance);
                seekToInAdvance = 0;
            } else {
                long position = JZUtils.getSavedProgress(getContext(), jzDataSource.getCurrentUrl());
                if (position != 0) {
                    mediaInterface.seekTo(position);//这里为什么区分开呢,第一次的播放和resume播放是不一样的。 这里怎么区分是一个问题。然后
                }
            }
        }
        state = STATE_PLAYING;
        startProgressTimer();
    }

    public void onStatePause() {
        Log.i(TAG, "onStatePause " + " [" + this.hashCode() + "] ");
        state = STATE_PAUSE;
        startProgressTimer();
        mAudioFocusManager.releaseAudioFocus(USAGE, CONTENT_TYPE);
    }

    public void onStateError() {
        Log.i(TAG, "onStateError " + " [" + this.hashCode() + "] ");
        state = STATE_ERROR;
        cancelProgressTimer();
        mAudioFocusManager.releaseAudioFocus(USAGE, CONTENT_TYPE);
    }

    public void onStateAutoComplete() {
        Log.i(TAG, "onStateAutoComplete " + " [" + this.hashCode() + "] ");
        state = STATE_AUTO_COMPLETE;
        cancelProgressTimer();
        progressBar.setProgress(100);
        currentTimeTextView.setText(totalTimeTextView.getText());
        //播放完成释放音频焦点
        mAudioFocusManager.releaseAudioFocus(USAGE, CONTENT_TYPE);
    }

    public void onInfo(int what, int extra) {
        Log.e(TAG, "onInfo what - " + what + " extra - " + extra);
        if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
            Log.d(TAG, "MEDIA_INFO_VIDEO_RENDERING_START");
            if (state == Jzvd.STATE_PREPARED
                    || state == Jzvd.STATE_PREPARING_CHANGE_URL
                    || state == Jzvd.STATE_PREPARING_PLAYING) {
                onStatePlaying();//开始渲染图像,真正进入playing状态
            }
        } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) {
            Log.d(TAG, "MEDIA_INFO_BUFFERING_START");
            backUpBufferState = state;
            setState(STATE_PREPARING_PLAYING);
        } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) {
            Log.d(TAG, "MEDIA_INFO_BUFFERING_END");
            if (backUpBufferState != -1) {
                setState(backUpBufferState);
                backUpBufferState = -1;
            }
        }
    }

    public void onError(int what, int extra) {
        Log.e(TAG, "onError " + what + " - " + extra + " [" + this.hashCode() + "] ");
        if (what != 38 && extra != -38 && what != -38 && extra != 38 && extra != -19) {
            onStateError();
            mediaInterface.release();
        }
    }

    public void onCompletion() {
        Runtime.getRuntime().gc();
        Log.i(TAG, "onAutoCompletion " + " [" + this.hashCode() + "] ");
        cancelProgressTimer();

        onStateAutoComplete();
        mediaInterface.release();
        JZUtils.saveProgress(getContext(), jzDataSource.getCurrentUrl(), 0);

        if (screen == SCREEN_FULLSCREEN) {
            gotoNormalCompletion();
        }
    }

    public void gotoNormalCompletion() {
        Log.i(TAG, "gotoNormalCompletion " + " [" + this.hashCode() + "] ");
        gobakFullscreenTime = System.currentTimeMillis();//退出全屏
        if (DECOR_VIEW != null) {
            mWindowManager.removeView(DECOR_VIEW);
            DECOR_VIEW.removeAllViews();
            DECOR_VIEW = null;
        }
        textureViewContainer.removeView(textureView);
        CONTAINER_LIST.getLast().removeViewAt(blockIndex);//remove block
        CONTAINER_LIST.getLast().addView(this, blockIndex, blockLayoutParams);
        CONTAINER_LIST.pop();
        setScreenNormal();
    }

    /**
     * 多数表现为中断当前播放
     */
    public void reset() {
        Log.i(TAG, "reset " + " [" + this.hashCode() + "] ");
        if (state == STATE_PLAYING || state == STATE_PAUSE) {
            long position = getCurrentPositionWhenPlaying();
            JZUtils.saveProgress(getContext(), jzDataSource.getCurrentUrl(), position);
        }
        cancelProgressTimer();

        onStateNormal();
        textureViewContainer.removeAllViews();

        mAudioFocusManager.releaseAudioFocus(USAGE, CONTENT_TYPE);
        if (mediaInterface != null) mediaInterface.release();
    }

    /**
     * 里面的的onState...()其实就是setState...(),因为要可以被复写,所以参考Activity的onCreate(),onState..()的方式看着舒服一些,老铁们有何高见。
     *
     * @param state stateId
     */
    public void setState(int state) {
        Log.e(TAG, "setState state " + state);
        switch (state) {
            case STATE_NORMAL:
                onStateNormal();
                break;
            case STATE_PREPARING:
                onStatePreparing();
                break;
            case STATE_PREPARING_PLAYING:
                onStatePreparingPlaying();
                break;
            case STATE_PREPARING_CHANGE_URL:
                onStatePreparingChangeUrl();
                break;
            case STATE_PLAYING:
                onStatePlaying();
                break;
            case STATE_PAUSE:
                onStatePause();
                break;
            case STATE_ERROR:
                onStateError();
                break;
            case STATE_AUTO_COMPLETE:
                onStateAutoComplete();
                break;
        }
    }

    public void setScreen(int screen) {//特殊的个别的进入全屏的按钮在这里设置  只有setup的时候能用上
        switch (screen) {
            case SCREEN_NORMAL:
                setScreenNormal();
                break;
            case SCREEN_FULLSCREEN:
                setScreenFullscreen();
                break;
        }
    }

    public void startVideo() {
        Log.d(TAG, "startVideo [" + this.hashCode() + "] ");
        setCurrentJzvd(this);
        try {
            Constructor<JZMediaInterface> constructor = mediaInterfaceClass.getConstructor(Jzvd.class);
            this.mediaInterface = constructor.newInstance(this);
        } catch (Exception e) {
            Log.d(TAG, "startVideo error : " + e.toString());
        }

        addTextureView();
        onStatePreparing();

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (screen == SCREEN_FULLSCREEN) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }
        if (widthRatio != 0 && heightRatio != 0) {
            int specWidth = MeasureSpec.getSize(widthMeasureSpec);
            int specHeight = (int) ((specWidth * (float) heightRatio) / widthRatio);
            setMeasuredDimension(specWidth, specHeight);

            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(specWidth, MeasureSpec.EXACTLY);
            int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(specHeight, MeasureSpec.EXACTLY);
            getChildAt(0).measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }

    }

    public void addTextureView() {
        Log.d(TAG, "addTextureView [" + this.hashCode() + "] ");
        if (textureView != null) textureViewContainer.removeView(textureView);
        textureView = new JZTextureView(getContext().getApplicationContext());
        textureView.setSurfaceTextureListener(mediaInterface);

        LayoutParams layoutParams =
                new LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        Gravity.CENTER);
        textureViewContainer.addView(textureView, layoutParams);
    }

    public void onVideoSizeChanged(int width, int height) {
        Log.i(TAG, "onVideoSizeChanged " + " [" + this.hashCode() + "]    width " +width  + "   height " + height);
        if (textureView != null) {
            if (videoRotation != 0) {
                textureView.setRotation(videoRotation);
            }
            textureView.setVideoSize(width, height);
        } else {
            Log.i(TAG, "onVideoSizeChanged  textureView is null");
        }
    }

    public void startProgressTimer() {
        Log.i(TAG, "startProgressTimer: " + " [" + this.hashCode() + "] ");
        cancelProgressTimer();
        UPDATE_PROGRESS_TIMER = new Timer();
        mProgressTimerTask = new ProgressTimerTask();
        UPDATE_PROGRESS_TIMER.schedule(mProgressTimerTask, 0, 300);
    }

    public void cancelProgressTimer() {
        if (UPDATE_PROGRESS_TIMER != null) {
            UPDATE_PROGRESS_TIMER.cancel();
        }
        if (mProgressTimerTask != null) {
            mProgressTimerTask.cancel();
        }
    }

    public void onProgress(int progress, long position, long duration) {
        Log.d(TAG, "onProgress: progress=" + progress + " position=" + position + " duration=" + duration);
        mCurrentPosition = position;
        if (!mTouchingProgressBar) {
            if (seekToManulPosition != -1) {
                if (seekToManulPosition > progress) {
                    return;
                } else {
                    seekToManulPosition = -1;//这个关键帧有没有必要做
                }
            } else {
                progressBar.setProgress(progress);
            }
        }
        if (position != 0) currentTimeTextView.setText(JZUtils.stringForTime(position));
        totalTimeTextView.setText(JZUtils.stringForTime(duration));
    }

    public void setBufferProgress(int bufferProgress) {
        progressBar.setSecondaryProgress(bufferProgress);
    }

    public void resetProgressAndTime() {
        mCurrentPosition = 0;
        progressBar.setProgress(0);
        progressBar.setSecondaryProgress(0);
        currentTimeTextView.setText(JZUtils.stringForTime(0));
        totalTimeTextView.setText(JZUtils.stringForTime(0));
    }

    public long getCurrentPositionWhenPlaying() {
        long position = 0;
        if (state == STATE_PLAYING || state == STATE_PAUSE || state == STATE_PREPARING_PLAYING) {
            try {
                position = mediaInterface.getCurrentPosition();
            } catch (IllegalStateException e) {
                e.printStackTrace();
                return position;
            }
        }
        return position;
    }

    public long getDuration() {
        long duration = 0;
        try {
            duration = mediaInterface.getDuration();
        } catch (Exception e) {
            e.printStackTrace();
            return duration;
        }
        return duration;
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        Log.i(TAG, "bottomProgress onStartTrackingTouch [" + this.hashCode() + "] ");
        cancelProgressTimer();
        ViewParent vpdown = getParent();
        while (vpdown != null) {
            vpdown.requestDisallowInterceptTouchEvent(true);
            vpdown = vpdown.getParent();
        }
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        Log.i(TAG, "bottomProgress onStopTrackingTouch [" + this.hashCode() + "] ");
        startProgressTimer();
        ViewParent vpup = getParent();
        while (vpup != null) {
            vpup.requestDisallowInterceptTouchEvent(false);
            vpup = vpup.getParent();
        }
        if (state != STATE_PLAYING &&
                state != STATE_PAUSE) return;
        long time = seekBar.getProgress() * getDuration() / 100;
        seekToManulPosition = seekBar.getProgress();
        mediaInterface.seekTo(time);
        Log.i(TAG, "seekTo " + time + " [" + this.hashCode() + "] ");
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        if (fromUser) {
            //设置这个progres对应的时间,给textview
            long duration = getDuration();
            currentTimeTextView.setText(JZUtils.stringForTime(progress * duration / 100));
        }
    }

    /**
     * 全屏后重新添加一个jzvd到原来的view上面
     * @param vg
     */
    public void cloneAJzvd(ViewGroup vg) {
        try {
            Constructor<Jzvd> constructor = (Constructor<Jzvd>) Jzvd.this.getClass().getConstructor(Context.class);
            Jzvd jzvd = constructor.newInstance(getContext());
            jzvd.setId(getId());
            jzvd.setMinimumWidth(blockWidth);
            jzvd.setMinimumHeight(blockHeight);
            jzvd.bringToFront();
            vg.addView(jzvd, blockIndex, blockLayoutParams);
            jzvd.setUp(jzDataSource.cloneMe(), SCREEN_NORMAL, mediaInterfaceClass);
        } catch (Exception e) {
            LogUtil.e(TAG,"cloneAJzvd error ", e);
        }
    }

    //注意方法命名,这里不能命名为getLayoutParams,因为View中也有个getLayoutParams方法
    public WindowManager.LayoutParams getWMLayoutParams() {
        WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        WindowInfoUtil.setWindowType(params);
        params.format = PixelFormat.TRANSPARENT;
        params.gravity = Gravity.TOP | Gravity.START;
        params.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;

        params.x = 460;
        params.y = 0;
        params.width = DisplayUtil.getScreenWidthByPix(getContext()) - 460;
        params.height = DisplayUtil.getScreenHeightByPix(getContext()) - 100;
        return params;
    }

    /**
     * 如果全屏或者返回全屏的视图有问题,复写这两个函数gotoScreenNormal(),根据自己布局的情况重新布局。
     */
    public void gotoFullscreen() {
        gotoFullscreenTime = System.currentTimeMillis();
        ViewGroup vg = (ViewGroup) getParent();
        jzvdContext = vg.getContext();
        blockLayoutParams = getLayoutParams();
        blockIndex = vg.indexOfChild(this);
        blockWidth = getWidth();
        blockHeight = getHeight();

        //fix: JzvdStd being added, but it already has a parent
        vg.removeView(this);//不能调用removeAllviews方法,会把和jzvd同级的其他控件也都删除
        cloneAJzvd(vg);
        //这里保存的是normal屏jzvd的Parent
        CONTAINER_LIST.add(vg);

        //在动态添加jzvd的时候,jzvd的控件要包在一个ViewGroup中,否则容易出现UI方面的问题
        DECOR_VIEW = new RelativeLayout(jzvdContext);
        ViewGroup.LayoutParams fullLayout = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        DECOR_VIEW.addView(this, fullLayout);
        //这个wm添加的是ViewGroup,不能直接添加jzvd
        mWindowManager.addView(DECOR_VIEW, getWMLayoutParams());
        setScreenFullscreen();
    }

    public void gotoNormalScreen() {//goback本质上是goto
        gobakFullscreenTime = System.currentTimeMillis();//退出全屏
        Log.e(TAG, "gotoNormalScreen  blockIndex " + blockIndex + "    blockWidth " + blockWidth + "  blockHeight " + blockHeight);
        if (DECOR_VIEW != null) {
            mWindowManager.removeView(DECOR_VIEW);
            DECOR_VIEW.removeAllViews();
            DECOR_VIEW = null;
        }
        CONTAINER_LIST.getLast().removeViewAt(blockIndex);//remove block
        //这里会把当前的jzvd添加到正常视图上,CURRENT_JZVD会在前台,否则cloneJzvd会显示在view上
        CONTAINER_LIST.getLast().addView(this, blockIndex, blockLayoutParams);
        CONTAINER_LIST.removeLast();

        setScreenNormal();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        LogUtil.d(TAG, "onDetachedFromWindow screen " + screen);
        //在正常播放时如果销毁就重置进度
        if (screen == SCREEN_NORMAL) {
            //resetProgressAndTime();
        }
    }

    public void setScreenNormal() {//TODO 这块不对呀,还需要改进,设置flag之后要设置ui,不设置ui这么写没意义呀
        screen = SCREEN_NORMAL;
    }

    public void setScreenFullscreen() {
        screen = SCREEN_FULLSCREEN;
    }

    public void onSeekComplete() {

    }

    public class ProgressTimerTask extends TimerTask {
        @Override
        public void run() {
            if (state == STATE_PLAYING || state == STATE_PAUSE || state == STATE_PREPARING_PLAYING) {
                //Log.d(TAG, "onProgressUpdate " + "[" + this.hashCode() + "] ");
                post(() -> {
                    long position = getCurrentPositionWhenPlaying();
                    long duration = getDuration();
                    int progress = (int) (position * 100 / (duration == 0 ? 1 : duration));
                    onProgress(progress, position, duration);
                });
            }
        }
    }

    public static AudioFocusManager.AudioFocusListener audioFocusListener = new AudioFocusManager.AudioFocusListener() {

        @Override
        public void onAudioFocusRequestSuccess() {
            Log.d(TAG, "audioFocusListener:onAudioFocusRequestSuccess");
        }

        @Override
        public void onAudioFocusRequestFail() {
            Log.d(TAG, "audioFocusListener:onAudioFocusRequestFail");
        }

        @Override
        public void onAudioFocusRelease() {
            Log.d(TAG, "audioFocusListener:onAudioFocusRelease");
        }

        @Override
        public void onAudioFocusChanged(int change) {
            Log.d(TAG, "audioFocusListener:onAudioFocusChanged");
        }
    };


}

使用:

windowManager.addView(view, layoutParams);

其中,view可以使用布局inflate,看也可以动态生成view,里面要包含Jztd视频控件。

windowManager 网上封装的工具类有很多,这里就不提供了。


学习的第一步就是先找到函数的入口:根据R.id.fullscreen这个全屏按钮的点击事件,我们很快就能定位到名叫gotoScreenFullscreen的函数。

code line 737
 public void gotoScreenFullscreen() {
        ViewGroup vg = (ViewGroup) getParent();
        vg.removeView(this);
        cloneAJzvd(vg);
        CONTAINER_LIST.add(vg);//作者在这里保存了原视图的父布局
        vg = (ViewGroup) (JZUtils.scanForActivity(getContext())).getWindow().getDecorView();//和他也没有关系
        vg.addView(this, new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));

        setScreenFullscreen();
        JZUtils.hideStatusBar(getContext());
        JZUtils.setRequestedOrientation(getContext(), FULLSCREEN_ORIENTATION);
        JZUtils.hideSystemUI(getContext());//华为手机和有虚拟键的手机全屏时可隐藏虚拟键 issue:1326

    }

从上方代码第三行跳转到下面这个方法,可以看出作者用构造器构造了一个新的对象替代了原来位置的对象。

 public void cloneAJzvd(ViewGroup vg) {
        try {
            Constructor<Jzvd> constructor = (Constructor<Jzvd>) Jzvd.this.getClass().getConstructor(Context.class);
            Jzvd jzvd = constructor.newInstance(getContext());
            jzvd.setId(getId());
            vg.addView(jzvd);
            jzvd.setUp(jzDataSource.cloneMe(), SCREEN_NORMAL, mediaInterfaceClass);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

继续看第一个方法第四行开始,毫无疑问,作者获取getDecorView()根视图,然后将原来的对象放入了跟视图,同时隐藏了状态栏,也即是开启了全屏模式,汗,简单粗暴。
还不够,那如何从全屏模式回来呢,作者在打开全屏的时候,就将原视图的父容器存放到了一个链表中。

 public static boolean backPress() {
        Log.i(TAG, "backPress");
        if (CONTAINER_LIST.size() != 0 && CURRENT_JZVD != null) {//判断条件,因为当前所有goBack都是回到普通窗口
            CURRENT_JZVD.gotoScreenNormal();
            return true;
        } else if (CONTAINER_LIST.size() == 0 && CURRENT_JZVD != null && CURRENT_JZVD.screen != SCREEN_NORMAL) {//退出直接进入的全屏
            CURRENT_JZVD.clearFloatScreen();
            return true;
        }
        return false;
    }

进入正题

  public void gotoScreenNormal() {//goback本质上是goto
        gobakFullscreenTime = System.currentTimeMillis();//退出全屏
        ViewGroup vg = (ViewGroup) (JZUtils.scanForActivity(getContext())).getWindow().getDecorView();
        vg.removeView(this);
        CONTAINER_LIST.getLast().removeAllViews();
        CONTAINER_LIST.getLast().addView(this, new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        CONTAINER_LIST.pop();

        setScreenNormal();//这块可以放到jzvd中
        JZUtils.showStatusBar(getContext());
        JZUtils.setRequestedOrientation(getContext(), NORMAL_ORIENTATION);
        JZUtils.showSystemUI(getContext());
    }

看完上面的代码一开始有点蒙蔽,为啥要把父容器的中视图全清理掉。回忆之前的接入过程,是因为

<?xml version="1.0" encoding="utf-8"?>
  <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <cn.jzvd.JzvdStd
            android:id="@+id/jz_video"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

如上所示,作者一开始就要求为播放器增加一层嵌套,可能是为了图方便,毕竟这比从复杂布局中恢复容易的多。


至此了解饺子播放器全屏播放的原理了。


相关api介绍(有些可能失效)

自动播放

    自动播放有两种 这里随便选择添加一个,
    1. myJzvdStd.startButton.performClick();
    2. myJzvdStd.startVideo();

跳转制定位置播放

 //这里只有开始播放时才生效
    mJzvdStd.seekToInAdvance = 20000;
    //跳转制定位置播放
    JZMediaManager.seekTo(30000);

   2.播放sd卡下视频

    public void cpAssertVideoToLocalPath() {
            try {
                InputStream myInput;
                OutputStream myOutput = new FileOutputStream(Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera/local_video.mp4");
                myInput = this.getAssets().open("local_video.mp4");
                byte[] buffer = new byte[1024];
                int length = myInput.read(buffer);
                while (length > 0) {
                    myOutput.write(buffer, 0, length);
                    length = myInput.read(buffer);
                }
     
                myOutput.flush();
                myInput.close();
                myOutput.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    myJzvdStd.setUp(Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera/local_video.mp4", "视频标题",Jzvd.SCREEN_WINDOW_NORMAL, );

播放assets目录下的视频

 复制Demo中CustomMediaPlayerAssertFolder类到你的项目下
            ----------------------------------------------------------------------------
            JZDataSource jzDataSource = null;
            try {
                jzDataSource = new JZDataSource(getAssets().openFd("local_video.mp4"));
                jzDataSource.title = "饺子快长大";
            } catch (IOException e) {
                e.printStackTrace();
            }
            jzvdStd.setUp(jzDataSource, JzvdStd.SCREEN_WINDOW_NORMAL);
            Glide.with(this)
                    .load("http://jzvd-pic.nathen.cn/jzvd-pic/1bb2ebbe-140d-4e2e-abd2-9e7e564f71ac.png")
                    .into(jzvdStd.thumbImageView);
     
            Jzvd.setMediaInterface(new CustomMediaPlayerAssertFolder());//进入此页面修改MediaInterface,让此页面的jzvd正常工作

直接全屏播放

JzvdStd.startFullscreen(this, JzvdStd.class, VideoConstant.videoUrlList[6], "视频标题");

开启小窗播放

mJzvdStd.startWindowTiny();

列表Item划出开启小窗播放

    1.Listview
       listView.setOnScrollListener(new AbsListView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(AbsListView view, int scrollState) { 
     
                }
                @Override
                public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                    Jzvd.onScrollAutoTiny(view, firstVisibleItem, visibleItemCount, totalItemCount);
                  // Jzvd.onScrollReleaseAllVideos(view, firstVisibleItem, visibleItemCount, totalItemCount);  这是不开启列表划出小窗 同时也是画出屏幕释放JZ 划出暂停
                }
            });
    2. RecyclerView  划出列表开启小窗
       recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
                @Override
                public void onChildViewAttachedToWindow(View view) {
                    Jzvd.onChildViewAttachedToWindow(view, R.id.videoplayer);
                }
     
                @Override
                public void onChildViewDetachedFromWindow(View view) {
                    Jzvd.onChildViewDetachedFromWindow(view);
                }
            });
    2.1 RecyclerView划出屏幕释放JZ,同时也是不开启列表划出显示小窗
        recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
                @Override
                public void onChildViewAttachedToWindow(View view) {
     
                }
     
                @Override
                public void onChildViewDetachedFromWindow(View view) {
                    Jzvd jzvd = view.findViewById(R.id.videoplayer);
                    if (jzvd != null && jzvd.jzDataSource.containsTheUrl(JZMediaManager.getCurrentUrl())) {
                        Jzvd currentJzvd = JzvdMgr.getCurrentJzvd();
                        if (currentJzvd != null && currentJzvd.currentScreen != Jzvd.SCREEN_WINDOW_FULLSCREEN) {
                            Jzvd.releaseAllVideos();
                        }
                    }
                }
            });

小屏播放无声音,全屏有声音

创建一个类继承JzvdStd并在XML设置
    public class JzvdStdVolumeAfterFullscreen extends JzvdStd {
        public JzvdStdVolumeAfterFullscreen(Context context) {
            super(context);
        }
     
        public JzvdStdVolumeAfterFullscreen(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
     
        @Override
        public void onPrepared() {
            super.onPrepared();
            if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {
                JZMediaManager.instance().jzMediaInterface.setVolume(1f, 1f);
            } else {
                JZMediaManager.instance().jzMediaInterface.setVolume(0f, 0f);
            }
        }
     
        /**
         * 进入全屏模式的时候关闭静音模式
         */
        @Override
        public void startWindowFullscreen() {
            super.startWindowFullscreen();
            JZMediaManager.instance().jzMediaInterface.setVolume(1f, 1f);
        }
     
        /**
         * 退出全屏模式的时候开启静音模式
         */
        @Override
        public void playOnThisJzvd() {
            super.playOnThisJzvd();
            JZMediaManager.instance().jzMediaInterface.setVolume(0f, 0f);
        }
    }

全屏状态播放完成,不退出全屏

创建一个类继承JzvdStd并在XML设置
    public class JzvdStdAutoCompleteAfterFullscreen extends JzvdStd {
        public JzvdStdAutoCompleteAfterFullscreen(Context context) {
            super(context);
        }
     
        public JzvdStdAutoCompleteAfterFullscreen(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
     
        @Override
        public void startVideo() {
            if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {
                Log.d(TAG, "startVideo [" + this.hashCode() + "] ");
                initTextureView();
                addTextureView();
                AudioManager mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
                mAudioManager.requestAudioFocus(onAudioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
                JZUtils.scanForActivity(getContext()).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
     
                JZMediaManager.setDataSource(jzDataSource);
                JZMediaManager.instance().positionInList = positionInList;
                onStatePreparing();
            } else {
                super.startVideo();
            }
        }
     
        @Override
        public void onAutoCompletion() {
            if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {
                onStateAutoComplete();
            } else {
                super.onAutoCompletion();
            }
     
        }
    }

全屏模式下显示分享按钮

复制DEMO下的layout文件在 layout_top 布局下 添加你的分享按钮
    public class JzvdStdShowShareButtonAfterFullscreen extends JzvdStd {
     
        public ImageView shareButton;
     
        public JzvdStdShowShareButtonAfterFullscreen(Context context) {
            super(context);
        }
     
        public JzvdStdShowShareButtonAfterFullscreen(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
     
        @Override
        public void init(Context context) {
            super.init(context);
            shareButton = findViewById(R.id.share);
            shareButton.setOnClickListener(this);
     
        }
     
        @Override
        public int getLayoutId() {
            return R.layout.layout_standard_with_share_button;
        }
     
        @Override
        public void onClick(View v) {
            super.onClick(v);
            if (v.getId() == R.id.share) {
                Toast.makeText(getContext(), "Whatever the icon means", Toast.LENGTH_SHORT).show();
            }
        }
     
        @Override
        public void setUp(JZDataSource jzDataSource, int screen) {
            super.setUp(jzDataSource, screen);
            if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {
                shareButton.setVisibility(View.VISIBLE);
            } else {
                shareButton.setVisibility(View.INVISIBLE);
            }
        }
    }

小屏状态下不显示标题,全屏模式下显示标题

 public class JzvdStdShowTitleAfterFullscreen extends JzvdStd {
        public JzvdStdShowTitleAfterFullscreen(Context context) {
            super(context);
        }
     
        public JzvdStdShowTitleAfterFullscreen(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
     
        @Override
        public void setUp(JZDataSource jzDataSource, int screen) {
            super.setUp(jzDataSource, screen);
            if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {
                titleTextView.setVisibility(View.VISIBLE);
            } else {
                titleTextView.setVisibility(View.INVISIBLE);
            }
        }
    }

播放MP3

public class JzvdStdMp3 extends JzvdStd {
     
        public JzvdStdMp3(Context context) {
            super(context);
        }
     
        public JzvdStdMp3(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
     
        @Override
        public int getLayoutId() {
            return R.layout.jz_layout_standard_mp3;
        }
     
        @Override
        public void onClick(View v) {
            if (v.getId() == cn.jzvd.R.id.thumb &&
                    (currentState == CURRENT_STATE_PLAYING ||
                            currentState == CURRENT_STATE_PAUSE)) {
                onClickUiToggle();
            } else if (v.getId() == R.id.fullscreen) {
     
            } else {
                super.onClick(v);
            }
        }
     
        //changeUiTo 真能能修改ui的方法
        @Override
        public void changeUiToNormal() {
            super.changeUiToNormal();
        }
     
        @Override
        public void changeUiToPreparing() {
            super.changeUiToPreparing();
        }
     
        @Override
        public void changeUiToPlayingShow() {
            super.changeUiToPlayingShow();
            thumbImageView.setVisibility(View.VISIBLE);
     
        }
     
        @Override
        public void changeUiToPlayingClear() {
            super.changeUiToPlayingClear();
            thumbImageView.setVisibility(View.VISIBLE);
     
        }
     
        @Override
        public void changeUiToPauseShow() {
            super.changeUiToPauseShow();
            thumbImageView.setVisibility(View.VISIBLE);
     
        }
     
        @Override
        public void changeUiToPauseClear() {
            super.changeUiToPauseClear();
            thumbImageView.setVisibility(View.VISIBLE);
     
        }
     
        @Override
        public void changeUiToComplete() {
            super.changeUiToComplete();
        }
     
        @Override
        public void changeUiToError() {
            super.changeUiToError();
        }
    }
     
            jzvdStdMp3 = findViewById(R.id.jz_videoplayer_mp3);
            jzvdStdMp3.setUp(URL, "饺子摇摆", Jzvd.SCREEN_WINDOW_NORMAL);
            Glide.with(this)
                    .load(VideoConstant.videoThumbs[0][1])
                    .into(jzvdStdMp3.thumbImageView);

播放完成不显示预览图

public class JzvdStdShowTextureViewAfterAutoComplete extends JzvdStd {
        public JzvdStdShowTextureViewAfterAutoComplete(Context context) {
            super(context);
        }
     
        public JzvdStdShowTextureViewAfterAutoComplete(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
     
        @Override
        public void onAutoCompletion() {
            super.onAutoCompletion();
            thumbImageView.setVisibility(View.GONE);
        }
     
    }

Home键退出界面暂停播放,返回界面继续播放

@Override
        protected void onResume() {
            super.onResume();
            //home back
            JzvdStd.goOnPlayOnResume();
        }
     
        @Override
        protected void onPause() {
            super.onPause();
       //     Jzvd.clearSavedProgress(this, null);
            //home back
            JzvdStd.goOnPlayOnPause();
        }

边播边缓存和清晰度切换

 1. 集成videocache implementation 'com.danikula:videocache:2.7.0',并初始化
    public class ApplicationDemo extends Application {
     
        @Override
        public void onCreate() {
            super.onCreate();
    //        LeakCanary.install(this);
        }
     
        private HttpProxyCacheServer proxy;
     
        public static HttpProxyCacheServer getProxy(Context context) {
            ApplicationDemo app = (ApplicationDemo) context.getApplicationContext();
            return app.proxy == null ? (app.proxy = app.newProxy()) : app.proxy;
        }
     
        private HttpProxyCacheServer newProxy() {
            return new HttpProxyCacheServer(this);
        }
    }
    2.引用
            LinkedHashMap map = new LinkedHashMap();
     
            String proxyUrl = ApplicationDemo.getProxy(this).getProxyUrl(VideoConstant.videoUrls[0][9]);
     
            map.put("高清", proxyUrl);
            map.put("标清", VideoConstant.videoUrls[0][6]);
            map.put("普清", VideoConstant.videoUrlList[0]);
            JZDataSource jzDataSource = new JZDataSource(map, "饺子不信");
            jzDataSource.looping = true;
            jzDataSource.currentUrlIndex = 2;
            jzDataSource.headerMap.put("key", "value");//header
            mJzvdStd.setUp(jzDataSource
                    , JzvdStd.SCREEN_WINDOW_NORMAL);
            Glide.with(this).load(VideoConstant.videoThumbList[0]).into(mJzvdStd.thumbImageView);

重复播放

创建一个类集成JzvdStd并在XML设置
    public class JZVideoPlayerStandardLoopVideo extends JzvdStd{
        public JZVideoPlayerStandardLoopVideo (Context context) {
            super(context);
        }
     
        public JZVideoPlayerStandardLoopVideo (Context context, AttributeSet attrs) {
            super(context, attrs);
        }
     
        @Override
        public void onAutoCompletion() {
            super.onAutoCompletion();
            startVideo();
        }
    }
    还有一种方法就是上面清晰度切换loop循环标志

重力感应自动进入全屏

 SensorManager mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        Jzvd.JZAutoFullscreenListener mSensorEventListener = new Jzvd.JZAutoFullscreenListener();
        @Override
        protected void onResume() {
            super.onResume();
            Sensor accelerometerSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
            mSensorManager.registerListener(mSensorEventListener, accelerometerSensor, SensorManager.SENSOR_DELAY_NORMAL);
        }
     
        @Override
        protected void onPause() {
            super.onPause();
            mSensorManager.unregisterListener(mSensorEventListener);
       }

重力感应

  Jzvd.FULLSCREEN_ORIENTATION=ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
    Jzvd.NORMAL_ORIENTATION = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
    两个变量控制全屏前后的屏幕方向

不保存播放进度

Jzvd.SAVE_PROGRESS = false;

取消播放时在非WIFIDialog提示

Jzvd.WIFI_TIP_DIALOG_SHOWED=true;

清除某个URL进度

Jzvd.clearSavedProgress(this, "url");

切换播放内核

ijk
复制Demo中JZMediaIjkplayer类到你的项目下 
implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.4'
implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.4'
Jzvd.setMediaInterface(new JZMediaIjkplayer()); // ijkMediaPlayer
Mediaplayer
Jzvd.setMediaInterface(new JZMediaSystem()); // 
exo
复制Demo中JZExoPlayer类到你的项目下 
implementation 'com.google.android.exoplayer:exoplayer:2.7.1'
Jzvd.setMediaInterface(new JZExoPlayer()); //exo

用户埋点统计

    Jzvd.setJzUserAction(new MyUserActionStd());
    /**
     * 这只是给埋点统计用户数据用的,不能写和播放相关的逻辑,监听事件请参考MyJzvdStd,复写函数取得相应事件
     */
    class MyUserActionStd implements JZUserActionStd {

        @Override
        public void onEvent(int type, Object url, int screen, Object... objects) {
            switch (type) {
                case JZUserAction.ON_CLICK_START_ICON:
                    Log.i("USER_EVENT", "ON_CLICK_START_ICON" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_CLICK_START_ERROR:
                    Log.i("USER_EVENT", "ON_CLICK_START_ERROR" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_CLICK_START_AUTO_COMPLETE:
                    Log.i("USER_EVENT", "ON_CLICK_START_AUTO_COMPLETE" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_CLICK_PAUSE:
                    Log.i("USER_EVENT", "ON_CLICK_PAUSE" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_CLICK_RESUME:
                    Log.i("USER_EVENT", "ON_CLICK_RESUME" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_SEEK_POSITION:
                    Log.i("USER_EVENT", "ON_SEEK_POSITION" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_AUTO_COMPLETE:
                    Log.i("USER_EVENT", "ON_AUTO_COMPLETE" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_ENTER_FULLSCREEN:
                    Log.i("USER_EVENT", "ON_ENTER_FULLSCREEN" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_QUIT_FULLSCREEN:
                    Log.i("USER_EVENT", "ON_QUIT_FULLSCREEN" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_ENTER_TINYSCREEN:
                    Log.i("USER_EVENT", "ON_ENTER_TINYSCREEN" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_QUIT_TINYSCREEN:
                    Log.i("USER_EVENT", "ON_QUIT_TINYSCREEN" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_TOUCH_SCREEN_SEEK_VOLUME:
                    Log.i("USER_EVENT", "ON_TOUCH_SCREEN_SEEK_VOLUME" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_TOUCH_SCREEN_SEEK_POSITION:
                    Log.i("USER_EVENT", "ON_TOUCH_SCREEN_SEEK_POSITION" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;

                case JZUserActionStd.ON_CLICK_START_THUMB:
                    Log.i("USER_EVENT", "ON_CLICK_START_THUMB" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserActionStd.ON_CLICK_BLANK:
                    Log.i("USER_EVENT", "ON_CLICK_BLANK" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                default:
                    Log.i("USER_EVENT", "unknow");
                    break;
            }
        }
    }

相关函数回调,屏幕状态,播放器状态,事件
在继承JzvdStd之后,可以通过父类的mCurrentState,取得当前的播放状态。

  • CURRENT_STATE_IDLE 未知状态,指控件被new出来之后什么都没做
  • CURRENT_STATE_NORMAL 普通状态
  • CURRENT_STATE_PREPARING 视频准备状态
  • CURRENT_STATE_PREPARING_CHANGING_URL 播放中切换url的准备状态
  • CURRENT_STATE_PLAYING 播放中状态
  • CURRENT_STATE_PAUSE 暂停状态
  • CURRENT_STATE_AUTO_COMPLETE 自动播放完成状态
  • CURRENT_STATE_ERROR 错误状态

复写进入播放状态的函数,取得播放状态的回调

  • onStateNormal 进入普通状态,通常指setUp之后
  • onStatePreparing 进入准备中状态,就是loading状态
  • onStatePlaying 进入播放状态
  • onStatePause 进入暂停状态
  • onStateError 进入错误状态
  • onStateAutoComplete 进入自动播放完成状态

全屏、小窗、非全屏分别是不同的实例,在继承JzvdStd后,通过mCurrentScreen变量,取得当前屏幕类型

  • SCREEN_WINDOW_NORMAL 普通窗口(进入全屏之前的)
  • SCREEN_WINDOW_LIST 列表窗口(进入全屏之前)
  • SCREEN_WINDOW_FULLSCREEN 全屏
  • SCREEN_WINDOW_TINY 小窗

事件

  • 复写onProgress函数,取得每次播放器设置底部seekBar的进度回调
  • 调用changeUrl函数,切换url
  • 复写onClick函数,取得各种按钮的点击事件
  • 复写onTouch函数,取得全屏之后的手势操作


使用JiaoZiVideoPlayer播放视频方向不对问题

直接上代码

public class JZplayer extends JzvdStd {
    public JZplayer(Context context) {
        super(context);
    }

    public JZplayer(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void init(Context context) {
        super.init(context);
        backButton.setVisibility(GONE);
    }

    @Override
    public void onInfo(int what, int extra) {
        super.onInfo(what, extra);
        if(what==IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED){
            //这里返回了视频的旋转角度,根据角度旋转视频到正确角度
            JZMediaManager.textureView.setRotation(extra);
        }
    }
}

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值