自定义的 JiaoZiVideoPlayer 集成了锁屏和截屏功能

前言

最近开发应用需要用到视频播放功能,在使用了原生的VideoView之后发现自己能力有限无法实现公司的需求。所以只能寄希望以第三方开源库,当在GitHub看到JiaoZiVideoPlayer高达9K+的Star之后果断的使用了这个,事实也如作者所说:“可以完全自定义UI和任何功能。”

先上GitHub地址:https://github.com/lipangit/JiaoZiVideoPlayer](https://github.com/lipangit/JiaoZiVideoPlayer
版本使用的是:7.0.4

参考文章

https://juejin.im/post/5cee364e5188251bc6234313
也可以加入GitHub里面作者留下的QQ群,里面有大佬会解答一些问题

正文

功能需求:

1.显示自定义标题和视频播放前的浏览页面
2.可以小屏/全屏播放
3.全屏状态下显示返回按钮、标题、电量、时间、进度条等基本功能
4.全屏状态上下滑动右边屏幕可以增减音量,滑动左边屏幕可以调整亮度,横向滑动可以调整播放进度
5.全屏状态需要有锁屏、截屏功能

实现

功能需求上的前4个JiaoZiVideoPlayer都已经自带了,此处主要讲解第5条需求的实现。
1.阅读该库的教程文档:https://juejin.im/post/5cee364e5188251bc6234313
2.在布局中引用控件,此处使用的是库中自带的控件,如果不需要实现第5条需求,直接引用即可实现功能。注意需要在控件的外围再套一个布局,为什么?(I don't know.)


3.直接在代码中获取控件然后填入视频的链接地址和标题即可缓存就是在退出页面回来后再次回来是否继续从退出的位置继续播放,true 是,false 否;显示默认图片就是在视频播放的之前显示的预览图片,作者推荐使用Glide显示,因此我也是使用Glide自己封装了工具类。
注意:"video"即是通过findViewById获取的控件;"video.thumbImageView"是库中自带的预览图片显示的ImageView控件。

通过前3步就可以使用基本的视频播放功能了,下面实现第5个需求的功能步骤。

4.在使用完成前3步之后视频可以正确播放了,则表示第三方库导入基本成功了,如果无法播放视频或者使用一直在加载中,请检查视频的Url是否正确且能在浏览器中直接播放,如果确定Url没有问题,可能是播放器内核不支持播放需要更换浏览器内核,第1步的文章当中有相信的更换步骤,请查看。
接下来开始实现锁屏和截屏功能。
要增加功能必然需要增加执行功能的按钮,要增加按钮就需要重写第三方库的布局,找出第三方库的布局"jz_layout_std.xml",直接在Activity Studio中双击Shift弹出的窗口中搜索就能找到该布局文件,找到之后复制到自己项目的layout下,然后增加两个按钮和一个ImageView。
注意:"jz_layout_std.xml"文件可以重命名,但是不能更改或者删除里面已经有的布局以及控件的ID。只能增加控件而不能删除或者修改原有的控件


5.在布局文件创建成功后创建一个类继承"JzvdStd"把两个构造方法都实现了,然后重写getLayoutId()方法,返回第4步的布局。

@Override
    public int getLayoutId() {
        // 自定义页面需要复制库里面的布局出来使用,只可增加自己的不可以删除或修改原有的
        return R.layout.jz_layout_std;
    }

6.重写init(Context context)方法,在方法中查找控件,并且添加按钮监听

 @Override
    public void init(Context context) {
        super.init(context);
        // 获取自定义添加的控件
        ibLock = findViewById(R.id.ib_jz_layout_std_lock);
        ibScreen = findViewById(R.id.ib_jz_layout_std_screen);
        ivScreen = findViewById(R.id.iv_jz_layout_std_screen);
        ibLock.setOnClickListener(this);
        ibScreen.setOnClickListener(this);
    }

7.重写onClick(View v)方法,并编写锁屏以及截屏的代码,changeUiToPlayingShow()方法是库中自带的方法,下面讲解

 @Override
    public void onClick(View v) {
        // 控件的监听,获取的是父类的。
        super.onClick(v);
        switch (v.getId()) {
            case R.id.ib_jz_layout_std_lock:
                ivScreen.setVisibility(GONE);
                if (locked) {
                    // 已经上锁,再次点击解锁
                    changeUiToPlayingShow();
                    ibLock.setImageResource(R.drawable.jz_click_lock_selector);
                    ToastUtils.showShort(getContext().getString(R.string.common_video_unlock));
                } else {
                    // 上锁
                    changeUiToPlayingClear();
                    ibLock.setImageResource(R.drawable.jz_click_locked_selector);
                    ToastUtils.showShort(getContext().getString(R.string.common_video_locked));
                }
                locked = !locked;
                break;
            case R.id.ib_jz_layout_std_screen:
                Bitmap bitmap = textureView.getBitmap();
                if (bitmap == null) {
                    ToastUtils.showShort("图片获取失败");
                    return;
                }
                // 保存图片
                boolean b = MyImageUtils.compressAndSaveImage(getContext(), bitmap, AppConstant.IMG_SELECTOR_DIR_PATH_OF_COMPRESS, 100);
                MyImageUtils.showImageForBitmap(getContext(), bitmap, ivScreen);
                ivScreen.setVisibility(VISIBLE);
                ToastUtils.showShort(getContext().getString(b ? R.string.common_save_img_succeed : R.string.common_save_img_failed));
                break;
        }
    }

以上已经完成了基本的功能,下面做按钮的显示和隐藏处理,在小屏播放的时候不需要显示锁屏和截屏功能

8.重写一些库中的方法,都带有详细注释,请认真查看

  //这里是播放的时候点击屏幕出现的UI
    @Override
    public void changeUiToPlayingShow() {
        // 此处做锁屏功能的按钮显示,判断是否锁屏状态,并且需要注意当前屏幕状态
        if (!locked) {
            super.changeUiToPlayingShow();
            // 判断是否全屏
            if (screen == SCREEN_FULLSCREEN)
                ibScreen.setVisibility(View.VISIBLE);
        }
        if (screen == SCREEN_FULLSCREEN)
            ibLock.setVisibility(ibLock.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            // 用户滑动屏幕的操作,返回true来屏蔽音量、亮度、进度的滑动功能
            case MotionEvent.ACTION_MOVE:
                if (locked)
                    return true;
        }
        return super.onTouch(v, event);
    }

    //这里是播放的时候屏幕上面UI消失  只显示下面底部的进度条UI
    @Override
    public void changeUiToPlayingClear() {
        super.changeUiToPlayingClear();
        ibLock.setVisibility(View.INVISIBLE);
        ibScreen.setVisibility(View.INVISIBLE);
    }

    // 点击暂停按钮执行的回调
    @Override
    public void onStatePause() {
        super.onStatePause();
        ibLock.setVisibility(View.INVISIBLE);
    }

    //这里是暂停的时候屏幕出现的UI
    @Override
    public void changeUiToPauseShow() {
        super.changeUiToPauseShow();
        if (screen == SCREEN_FULLSCREEN) {
            ibScreen.setVisibility(View.VISIBLE);
        }
    }

    //这里是暂停的时候点击屏幕消失的UI,只显示下面底部的进度条UI
    @Override
    public void changeUiToPauseClear() {
        super.changeUiToPauseClear();
        ibLock.setVisibility(View.INVISIBLE);
        ibScreen.setVisibility(View.INVISIBLE);
    }

    //这里是出错的UI
    @Override
    public void changeUiToError() {
        super.changeUiToError();
        ibLock.setVisibility(View.INVISIBLE);
        ibScreen.setVisibility(View.INVISIBLE);
    }

    // 点击屏幕会出现所有控件,一定时间后消失的回调
    @Override
    public void dissmissControlView() {
        super.dissmissControlView();
        // 需要在UI线程进行隐藏
        post(() -> {
            ibLock.setVisibility(View.INVISIBLE);
            ibScreen.setVisibility(View.INVISIBLE);
        });
    }

注意:"screen"是库中已经存在的变量,表示当前屏幕状态;"SCREEN_FULLSCREEN"表示全屏状态,"SCREEN_NORMAL"表示小屏状态;截图功能是根据获取当前播放的帧数拿到Bitmap方法然后保存Bitmap到本地,然后通知系统相册刷新图片。
其中需要注意的是在dissmissControlView()方法中隐藏控件需要在UI线程中进行操作;
怎样获取当前帧数的Bitmap呢?在经历了一个下午的坑坑坑之后还是无法解决,无奈只能直接询问作者,幸好作者回复了我(此处表示真的非常感谢~)
获取当前帧的方法:在库的Jzvd类中存在一个变量"JZTextureView textureView"其中有一个getBitmap()方法可以直接获取当前帧画面。

9.自定义JzvdStd后,在布局中引用自定义的JzvdStd,在查找控件的时候变量类型定义成自定义的JzvdStd即可,调用方法跟第3步是一样的。其中使用Glide显示图片和保存Bitmap的工具类就不提供了,百度一下就有了。

代码

import android.content.Context;
import android.graphics.Bitmap;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;

import com.babybath2.R;
import com.babybath2.constants.AppConstant;
import com.babybath2.utils.MyImageUtils;
import com.blankj.utilcode.util.ToastUtils;

import cn.jzvd.JzvdStd;

/**
 * 自定义的集成第三方的播放控件,增加了锁屏和截屏功能
 */
public class MyJzvStd extends JzvdStd {
    private ImageButton ibLock;
    private ImageButton ibScreen;
    private ImageView ivScreen;
    /**
     * 是否上锁
     */
    private boolean locked;

    public MyJzvStd(Context context) {
        super(context);
    }

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

    @Override
    public void onStatePreparing() {
        super.onStatePreparing();
        thumbImageView.setVisibility(View.GONE);
    }

    @Override
    public void onAutoCompletion() {
        super.onAutoCompletion();
        // 播放完之后不显示预览图片
        thumbImageView.setVisibility(View.GONE);
    }

    @Override
    public int getLayoutId() {
        // 自定义页面需要复制库里面的布局出来使用,只可增加自己的不可以删除或修改原有的
        return R.layout.jz_layout_std;
    }

    @Override
    public void init(Context context) {
        super.init(context);
        // 获取自定义添加的控件
        ibLock = findViewById(R.id.ib_jz_layout_std_lock);
        ibScreen = findViewById(R.id.ib_jz_layout_std_screen);
        ivScreen = findViewById(R.id.iv_jz_layout_std_screen);
        ibLock.setOnClickListener(this);
        ibScreen.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        // 控件的监听,获取的是父类的。
        super.onClick(v);
        switch (v.getId()) {
            case R.id.ib_jz_layout_std_lock:
                ivScreen.setVisibility(GONE);
                if (locked) {
                    // 已经上锁,再次点击解锁
                    changeUiToPlayingShow();
                    ibLock.setImageResource(R.drawable.jz_click_lock_selector);
                    ToastUtils.showShort(getContext().getString(R.string.common_video_unlock));
                } else {
                    // 上锁
                    changeUiToPlayingClear();
                    ibLock.setImageResource(R.drawable.jz_click_locked_selector);
                    ToastUtils.showShort(getContext().getString(R.string.common_video_locked));
                }
                locked = !locked;
                break;
            case R.id.ib_jz_layout_std_screen:
                Bitmap bitmap = textureView.getBitmap();
                if (bitmap == null) {
                    ToastUtils.showShort("图片获取失败");
                    return;
                }
                // 保存图片
                boolean b = MyImageUtils.compressAndSaveImage(getContext(), bitmap, AppConstant.IMG_SELECTOR_DIR_PATH_OF_COMPRESS, 100);
                MyImageUtils.showImageForBitmap(getContext(), bitmap, ivScreen);
                ivScreen.setVisibility(VISIBLE);
                ToastUtils.showShort(getContext().getString(b ? R.string.common_save_img_succeed : R.string.common_save_img_failed));
                break;
        }
    }

    //这里是播放的时候点击屏幕出现的UI
    @Override
    public void changeUiToPlayingShow() {
        // 此处做锁屏功能的按钮显示,判断是否锁屏状态,并且需要注意当前屏幕状态
        if (!locked) {
            super.changeUiToPlayingShow();
            // 判断是否全屏
            if (screen == SCREEN_FULLSCREEN)
                ibScreen.setVisibility(View.VISIBLE);
        }
        if (screen == SCREEN_FULLSCREEN)
            ibLock.setVisibility(ibLock.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            // 用户滑动屏幕的操作,返回true来屏蔽音量、亮度、进度的滑动功能
            case MotionEvent.ACTION_MOVE:
                if (locked)
                    return true;
        }
        return super.onTouch(v, event);
    }

    //这里是播放的时候屏幕上面UI消失  只显示下面底部的进度条UI
    @Override
    public void changeUiToPlayingClear() {
        super.changeUiToPlayingClear();
        ibLock.setVisibility(View.INVISIBLE);
        ibScreen.setVisibility(View.INVISIBLE);
    }

    // 点击暂停按钮执行的回调
    @Override
    public void onStatePause() {
        super.onStatePause();
        ibLock.setVisibility(View.INVISIBLE);
    }

    //这里是暂停的时候屏幕出现的UI
    @Override
    public void changeUiToPauseShow() {
        super.changeUiToPauseShow();
        if (screen == SCREEN_FULLSCREEN) {
            ibScreen.setVisibility(View.VISIBLE);
        }
    }

    //这里是暂停的时候点击屏幕消失的UI,只显示下面底部的进度条UI
    @Override
    public void changeUiToPauseClear() {
        super.changeUiToPauseClear();
        ibLock.setVisibility(View.INVISIBLE);
        ibScreen.setVisibility(View.INVISIBLE);
    }

    //这里是出错的UI
    @Override
    public void changeUiToError() {
        super.changeUiToError();
        ibLock.setVisibility(View.INVISIBLE);
        ibScreen.setVisibility(View.INVISIBLE);
    }

    // 点击屏幕会出现所有控件,一定时间后消失的回调
    @Override
    public void dissmissControlView() {
        super.dissmissControlView();
        // 需要在UI线程进行隐藏
        post(() -> {
            ibLock.setVisibility(View.INVISIBLE);
            ibScreen.setVisibility(View.INVISIBLE);
        });
    }
}

End

第一次写这么长的博客,有错误希望各位大佬指出。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351