接着上一篇文章 Vitamio库打造万能播放器(三)

终于到了介绍vitamio的使用了,这个库使用起来非常简单,很容易上手,只需要将前面我们创建好的VideoPlayerActivity的VideoView相关的api的包名换一下即可,无需改动其他代码如下所示:

package mchenys.net.csdn.blog.myplayer.module.video;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.text.format.DateFormat;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;import java.util.List;import io.vov.vitamio.LibsChecker;
import io.vov.vitamio.MediaPlayer;
import io.vov.vitamio.widget.VideoView;
import mchenys.net.csdn.blog.myplayer.R;
import mchenys.net.csdn.blog.myplayer.common.base.BaseActivity;
import mchenys.net.csdn.blog.myplayer.common.config.Constant;
import mchenys.net.csdn.blog.myplayer.common.model.Video;
import mchenys.net.csdn.blog.myplayer.common.utils.LogUtils;
import mchenys.net.csdn.blog.myplayer.common.utils.TimeUtils;
import mchenys.net.csdn.blog.myplayer.common.utils.UIUtils;
import mchenys.net.csdn.blog.myplayer.common.widget.TitleBar;
import mchenys.net.csdn.blog.myplayer.common.widget.nineoldandroids.view.ViewHelper;
import mchenys.net.csdn.blog.myplayer.common.widget.nineoldandroids.view.ViewPropertyAnimator;/*** Vitamio 万能视频播放器* 直接从VideoPlayerActivity修改Copy过来,关于VideoView的api完全一样,只是导包不一样.* 1.多了这一句 if (!LibsChecker.checkVitamioLibs(this)) return; 以及内部的VideoView对全屏和按比例缩放的代码处理方式不一样* 2.布局文件换成了activity_video_player_vitamio* Created by mChenys on 2016/1/17.*/
public class VitamioVideoPlayerActivity extends BaseActivity {private VideoView mVideoView;private int mPosition;private List<Video> mVideoList;private ImageButton mBackIb, mPreIb, mNextIb, mPlayIb, mFullScreenIb, mVolumeIb;private TextView mTitleTv, mSysTimeTv, mCurrVideoPositionTv, mVideoDurationTv;private ImageView mBatteryIv;private View mBrightnessView;private LinearLayout mTopControlLl, mBottomControlLl;private Handler mHandler;private AudioManager mAudioManager;private int mMaxVolume, mCurrVolume;private SeekBar mVolumeSb, mVideoSb;private GestureDetector mGestureDetector;private float mVolumeScreenHeightScale;private float mCurrBrightness;private boolean mIsLeftDown;private float mBrightnessScreenHeightScale;private Video mVideo;@Overrideprotected void setTitleBar(TitleBar titleBar) {titleBar.setVisibility(View.GONE);}@Overrideprotected boolean isHomePage() {return false;}@Overridepublic Integer getLayoutResId() {return R.layout.activity_video_player_vitamio;}@Overridepublic void initView() {mVideoView = findView(R.id.videoView);mTitleTv = findView(R.id.tv_title);//标题mSysTimeTv = findView(R.id.tv_system_time);//系统时间mCurrVideoPositionTv = findView(R.id.tv_current_position);//当前播放位置mVideoDurationTv = findView(R.id.tv_duration);//视频时长mVolumeIb = findView(R.id.ib_voice);//静音mBackIb = findView(R.id.ib_back);//返回mPreIb = findView(R.id.ib_pre);//上一首mPlayIb = findView(R.id.ib_play);//播放mNextIb = findView(R.id.ib_next);//下一首mFullScreenIb = findView(R.id.ib_fullScreen);//全屏mBatteryIv = findView(R.id.iv_battery);//电量mVolumeSb = findView(R.id.sb_voice);//音量进度seekBarmVideoSb = findView(R.id.sb_video);//视频进度seekBarmBrightnessView = findView(R.id.view_brightness);//通过透明度改变屏幕亮度的view//默认隐藏控制面板mTopControlLl = findView(R.id.ll_top_ctrl);//顶部控制面板mBottomControlLl = findView(R.id.ll_bottom_ctrl);//底部控制面板}@Overridepublic void initListener() {//设置视频准备监听器mVideoView.setOnPreparedListener(mOnPreparedListener);//设置视频播放完毕的监听器mVideoView.setOnCompletionListener(mOnCompletionListener);//设置音量改变的seekBar监听器mVolumeSb.setOnSeekBarChangeListener(mVolumeSeekBarChangeListener);//设置视频进度改变的seekBar监听器mVideoSb.setOnSeekBarChangeListener(mVideoSeekBarChangeListener);//设置缓冲监听mVideoView.setOnBufferingUpdateListener(mOnBufferingUpdateListener);//设置视频卡顿监听器mVideoView.setOnInfoListener(mOnInfoListener);}@Overridepublic void initData() {//注册电量接收器registerBatteryReceiver();// 检查Vitamio插件是否安装if (!LibsChecker.checkVitamioLibs(this)) return;mHandler = new Handler();Uri videoUri = getIntent().getData();if (null != videoUri) {// 说明是从第三方应用跳转过来的mLoadView.setProgressBarVisiable(true);mVideoView.setVideoURI(videoUri);mTitleTv.setText(videoUri.getPath());mPreIb.setEnabled(false);mNextIb.setEnabled(false);} else {//说明是从播放列表进入的Bundle bundle = getIntent().getExtras();if (null == bundle || bundle.size() < 0) {mLoadView.setExceptionViewVisible(true);return;}mPosition = bundle.getInt(Constant.KEY_POSITION);mVideoList = bundle.getParcelableArrayList(Constant.KEY_LIST);//设置当前播放的视频setCurrPlayVideo();}//初始化音量initVolume();//初始化手势监听器initGestures();//初始化亮度值initBrightness();}@Overridepublic void reLoadData() {}/*** 设置当前播放的视频*/private void setCurrPlayVideo() {if (mVideoList == null || mVideoList.isEmpty() || mPosition == -1) {return;}mLoadView.setProgressBarVisiable(true);LogUtils.i(this, "mPosition:" + mPosition + " mVideoList:" + mVideoList);//设置上一个/下一个按钮不可用时的背景图片mPreIb.setEnabled(mPosition != 0);//当前不是第一个,则可点击上一个mNextIb.setEnabled(mPosition != mVideoList.size() - 1);//当前不是最后一个,则可以点击下一个//从列表中取出要播放的视频mVideo = mVideoList.get(mPosition);//设置播放的地址mVideoView.setVideoPath(mVideo.path);}/*** 初始化亮度值*/private void initBrightness() {mBrightnessView.setVisibility(View.VISIBLE);ViewHelper.setAlpha(mBrightnessView, 0f);//一开始完全透明mBrightnessScreenHeightScale = 1.0f / UIUtils.getScreenHeight();//透明度和屏幕高度的比例}/*** 初始化手势监听器*/private void initGestures() {//手势监听器mGestureDetector = new GestureDetector(this, mOnGestureListener);//最大音量和屏幕高度的比例mVolumeScreenHeightScale = mMaxVolume * 1.0f / UIUtils.getScreenHeight();LogUtils.i(VitamioVideoPlayerActivity.this, "mVolumeScreenHeightScale:" + mVolumeScreenHeightScale + " mMaxVolume:" + mMaxVolume+ " Env.screenHeight:" + UIUtils.getScreenHeight());}/*** 注册电量变化的接收器*/private void registerBatteryReceiver() {IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);registerReceiver(mBatteryChangeReceiver, filter);}/*** 初始化音量控制*/private void initVolume() {mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);mMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);//最大音量mCurrVolume = getStreamVolume();//当前音量mVolumeSb.setMax(mMaxVolume);mVolumeSb.setProgress(mCurrVolume);}/*** 获取当前音量** @return*/private int getStreamVolume() {return mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);}/*** 更新系统音量进度*/private void updateVolumePosition(int progress) {int streamType = AudioManager.STREAM_MUSIC;int flags = 0; //0:不显示系统音量面板,1:显示mAudioManager.setStreamVolume(streamType, progress, flags);}/*** 切换音量*/private void toggleVolume() {if (getStreamVolume() > 0) {//设置为静音mCurrVolume = getStreamVolume();updateVolumePosition(0);mVolumeSb.setProgress(0);} else {//恢复之前音量updateVolumePosition(mCurrVolume);mVolumeSb.setProgress(mCurrVolume);}}/*** 通过手势改变音量大小** @param deltaY*/private void changeGestureVolume(float deltaY) {// 1、触摸(onTouchEvent方法中处理)// 2、识别手势(GuestureDetactor)// 3、计算音量最大值与屏幕高的比例// 4、计算移动的距离等于多少对应的音量值//最终的音量=当前音量+滑动距离*比例int changeVolume = (int) (deltaY * mVolumeScreenHeightScale);int finalVolume = mCurrVolume + changeVolume;// 预防超出范围if (finalVolume > mMaxVolume) {finalVolume = mMaxVolume;} else if (finalVolume < 0) {finalVolume = 0;}updateVolumePosition(finalVolume);mVolumeSb.setProgress(finalVolume);LogUtils.i(VitamioVideoPlayerActivity.this, "当前音量:" + finalVolume);}/*** 改变亮度** @param deltaY*/private void changeGestureBrightness(float deltaY) {// 1、触摸(onTouchEvent方法中处理)// 2、识别手势(GuestureDetactor)// 3、计算亮度最大值与屏幕高的比例// 4、计算移动的距离等于多少对应的亮度值float changeBrightness = deltaY * mBrightnessScreenHeightScale;// 5、在原来亮度的基础上加上移动对应的亮度值//这里取changeBrightness的相反值,是因为往上移动需要屏幕变亮,所以透明度值要变小float finalBrightness = mCurrBrightness - changeBrightness;if (finalBrightness < 0f) {finalBrightness = 0f;} else if (finalBrightness > 0.8f) {finalBrightness = 0.8f;//这里不能太接近1,1的话就什么都看不到了.}ViewHelper.setAlpha(mBrightnessView, finalBrightness);}/*** 显示系统时间*/private void updateSysTime() {mHandler.postDelayed(mUpdateSysTimeRunnable, 1000);}/*** 更新视频的播放进度*/private void updateVideoPosition() {mHandler.postDelayed(mUpdateVideoPositionRunnable, 300); //更新的频率可以调快一点}/*** 更新视频的二级缓冲** @param percent*/private void updateSecondaryProgress(int percent) {int secondaryProgress = (int) (mVideoView.getDuration() * percent / 100f);mVideoSb.setSecondaryProgress(secondaryProgress);}/*** 播放/暂停视频*/private void play() {if (mVideoView.isPlaying()) {mVideoView.pause();} else {mVideoView.start();}setPlayBgSelector();}/*** 设置播放按钮的背景选择器*/private void setPlayBgSelector() {//如果当前是播放,则显示暂停背景图片选择器//如果当前是暂停,则显示播放背景图片选择器mPlayIb.setImageResource(mVideoView.isPlaying() ? R.drawable.selector_ib_pause : R.drawable.selector_ib_play);}/*** 播放上一个*/private void pre() {if (mPosition > 0) {mPosition--;setCurrPlayVideo();}}/*** 播放下一个*/private void next() {if (mPosition != mVideoList.size() - 1) {mPosition++;setCurrPlayVideo();}}/*** 在全屏和默认大小之间进行切换*/private void toggleFullscreen() {mVideoView.toggleFullscreen();updateFullscreenBtnBg();}/*** 更新全屏按钮的背景选择器*/private void updateFullscreenBtnBg() {// 如果当前是全屏的,则显示一个恢复默认大小的按钮// 如果当前是默认大小的,则显示一个全屏的按钮mFullScreenIb.setImageResource(mVideoView.isFullscreen() ? R.drawable.selector_ib_default_screen: R.drawable.selector_ib_full_screen);}/*** 隐藏控制面板*/private void hideControlLayout() {//瞬时执行隐藏动画
//        ViewHelper.setTranslationY(mTopControlLl, -mTopControlLl.getMeasuredHeight());//向上移动是负数
//        ViewHelper.setTranslationY(mBottomControlLl, mBottomControlLl.getMeasuredHeight());//向下移动是正数//带动画效果的隐藏控制面板ViewPropertyAnimator.animate(mTopControlLl).translationY(-mTopControlLl.getHeight());ViewPropertyAnimator.animate(mBottomControlLl).translationY(mBottomControlLl.getHeight());}/*** 单击切换控制面板的显示/隐藏*/private void toggleHideControlLayout() {//获取mTopControlLl当前相对控件Y坐标的位置float currTranslateY = ViewHelper.getTranslationY(mTopControlLl);if (currTranslateY == 0f) {//当前是显示控制面板的,需要隐藏hideControlLayout();} else {//显示控制面板ViewPropertyAnimator.animate(mTopControlLl).translationY(0f);ViewPropertyAnimator.animate(mBottomControlLl).translationY(0f);//5秒后自动隐藏控制面板HideControlLayoutDelay();}}/*** 5秒后自动隐藏控制面板*/private void HideControlLayoutDelay() {mHandler.removeCallbacks(mHideControlLayoutRunnable);mHandler.postDelayed(mHideControlLayoutRunnable, 5000);}/*** 取消5秒后自动隐藏控制面板,在界面有用户操作(按钮单击、SeekBar手动、Touch事件)的时候取消*/private void cancelHideControlLayout() {mHandler.removeCallbacks(mHideControlLayoutRunnable);}/*** 更新电量的图标** @param level*/private void updateBatteryByLevel(int level) {int resId;if (level == 0) {resId = R.drawable.ic_battery_0;} else if (level <= 10) {resId = R.drawable.ic_battery_10;} else if (level <= 20) {resId = R.drawable.ic_battery_20;} else if (level <= 40) {resId = R.drawable.ic_battery_40;} else if (level <= 60) {resId = R.drawable.ic_battery_60;} else if (level <= 80) {resId = R.drawable.ic_battery_80;} else {resId = R.drawable.ic_battery_100;}mBatteryIv.setImageResource(resId);}@Overridepublic void onClick(View v) {//操作前先取消隐藏控制版面cancelHideControlLayout();switch (v.getId()) {case R.id.ib_voice:toggleVolume();break;case R.id.ib_back:onBackPressed();break;case R.id.ib_pre:pre();break;case R.id.ib_play:play();break;case R.id.ib_next:next();break;case R.id.ib_fullScreen:toggleFullscreen();break;}//延迟隐藏控制版面HideControlLayoutDelay();}/*** 视频准备完毕的监听*/MediaPlayer.OnPreparedListener mOnPreparedListener = new MediaPlayer.OnPreparedListener() {@Overridepublic void onPrepared(MediaPlayer mp) {LogUtils.i(VitamioVideoPlayerActivity.this, ">>>>>>>>>onPrepared");//隐藏加载圈mLoadView.setHideProgressBarWidthAlpha();//开始播放视频mVideoView.start();if (null != mVideo) {//显示视频名称mTitleTv.setText(mVideo.title);}hideControlLayout();//设置视频进度的最大值mVideoSb.setMax((int) mVideoView.getDuration());//设置视频总时长mVideoDurationTv.setText(TimeUtils.format(mVideoView.getDuration()));//更新播放的进度updateVideoPosition();//设置播放按钮的背景选择器setPlayBgSelector();}};/*** 视频播放完毕的监听*/MediaPlayer.OnCompletionListener mOnCompletionListener = new MediaPlayer.OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer mp) {//播放完后,将当前播放进度设置为0// mHandler.removeCallbacks(mUpdateVideoPositionRunnable);mLoadView.setProgressBarVisiable(false);mCurrVideoPositionTv.setText(TimeUtils.format(0));mVideoSb.setProgress(0);mVideoView.seekTo(0);mPlayIb.setImageResource(R.drawable.selector_ib_play);//恢复为播放按钮}};/*** 电量改变的接收器*/BroadcastReceiver mBatteryChangeReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {
//            PrintUtils.printIntent(intent);int level = intent.getIntExtra("level", 0);//电量的等级updateBatteryByLevel(level);}};/*** 更新系统时间的Runnable*/Runnable mUpdateSysTimeRunnable = new Runnable() {@Overridepublic void run() {mSysTimeTv.setText(DateFormat.format("HH:mm:ss", System.currentTimeMillis()));updateSysTime();}};/*** 更新视频播放进度的Runnable*/Runnable mUpdateVideoPositionRunnable = new Runnable() {@Overridepublic void run() {//获取当前进度,是一个时间毫秒值int currPosition = (int) mVideoView.getCurrentPosition();//更新时间mCurrVideoPositionTv.setText(TimeUtils.format(currPosition));//更新seekBarmVideoSb.setProgress(currPosition);updateVideoPosition();}};/*** 定时隐藏控制面板的Runnable*/Runnable mHideControlLayoutRunnable = new Runnable() {@Overridepublic void run() {hideControlLayout();}};/*** 音量改变的SeekBar监听器*/SeekBar.OnSeekBarChangeListener mVolumeSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {if (fromUser) {//表示是用户拖动改变的updateVolumePosition(progress);}}//开始拖动@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {cancelHideControlLayout();}//停止拖动@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {HideControlLayoutDelay();}};/*** 视频进度改变的SeekBar监听器*/SeekBar.OnSeekBarChangeListener mVideoSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {if (fromUser) {//表示是用户拖动改变的mVideoView.seekTo(progress);}}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {cancelHideControlLayout();}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {HideControlLayoutDelay();}};/*** 视频缓冲监听器*/MediaPlayer.OnBufferingUpdateListener mOnBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() {@Overridepublic void onBufferingUpdate(MediaPlayer mp, int percent) {updateSecondaryProgress(percent);}};/*** 视频卡顿监听器*/MediaPlayer.OnInfoListener mOnInfoListener = new MediaPlayer.OnInfoListener() {@Overridepublic boolean onInfo(MediaPlayer mp, int what, int extra) {switch (what) {case MediaPlayer.MEDIA_INFO_BUFFERING_START: //开始缓冲mLoadView.setProgressBarVisiable(true);break;case MediaPlayer.MEDIA_INFO_BUFFERING_END://停止缓冲:mLoadView.setHideProgressBarWidthAlpha();break;}return true;}};/*** 手势监听器*/GestureDetector.SimpleOnGestureListener mOnGestureListener = new GestureDetector.SimpleOnGestureListener() {//长按@Overridepublic void onLongPress(MotionEvent e) {play();}//滚动@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {//获取是指滑动的距离,deltaY>0表示向上滑动,否则表示向下滑动float deltaY = e1.getY() - e2.getY();if (mIsLeftDown) {//左侧按下,改变亮度,即mBrightnessView的透明度changeGestureBrightness(deltaY);} else {//右侧按下,改变音量changeGestureVolume(deltaY);}return true;}//按下@Overridepublic boolean onDown(MotionEvent e) {mCurrVolume = getStreamVolume();//获取当前音量mCurrBrightness = mBrightnessView.getAlpha();//获取当前的亮度view的透明度mIsLeftDown = e.getX() < UIUtils.getScreenWidth() / 2; //是否在屏幕左侧按下return super.onDown(e);}//双击@Overridepublic boolean onDoubleTap(MotionEvent e) {//3种方式执行全屏//toggleFullscreen();//onClick(mFullScreenIb);mFullScreenIb.performClick();    // 当过代码的方式去单击这个按钮return true;}//单击@Overridepublic boolean onSingleTapConfirmed(MotionEvent e) {//5秒后自动隐藏toggleHideControlLayout();return true;}};@Overridepublic boolean onTouchEvent(MotionEvent event) {//传递触摸事件给手势监听器mGestureDetector.onTouchEvent(event);switch (event.getAction()) {case MotionEvent.ACTION_UP:HideControlLayoutDelay();//5秒后自动隐藏break;default:cancelHideControlLayout();//取消自动隐藏break;}return super.onTouchEvent(event);}@Overrideprotected void onStart() {super.onStart();//显示系统时间updateSysTime();}@Overrideprotected void onStop() {super.onStop();mHandler.removeCallbacks(mUpdateSysTimeRunnable);}@Overrideprotected void onDestroy() {super.onDestroy();if (null != mBatteryChangeReceiver) {unregisterReceiver(mBatteryChangeReceiver);}}
}

修改布局文件的VideoView全路径

<io.vov.vitamio.widget.VideoViewandroid:id="@id/videoView"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_centerInParent="true"/>

修改清单文件中播放器的类名

android:name=".module.video.VitamioVideoPlayerActivity"

当然上述的所有操作都是在关联了vitamio库之后的.

最后还需要在Vitamio库的VideoView源码上增加个全屏和按比例播放直接的切换.直接加在源码最后面即可:

/*** 全屏和按比例播放间的切换*/
public void toggleFullscreen() {int layout;if (mVideoLayout == VIDEO_LAYOUT_STRETCH) {//当前是全屏播放,调用后设置为按比例播放layout = VIDEO_LAYOUT_SCALE;} else {//当前是按比例播放,调用后设置为全屏播放layout = VIDEO_LAYOUT_STRETCH;}float aspectRatio = 0;setVideoLayout(layout, aspectRatio);
}
/*** 当前的播放模式是否是全屏播放*/
public boolean isFullscreen() {return mVideoLayout == VIDEO_LAYOUT_STRETCH;
}

Vitamio库打造万能播放器(四)相关推荐

  1. 使用Vitamio库打造万能播放器(一)

    Vitamio库使用起来非常简单,其api的使用和android自身的VideoView的api是一样的,Vitamio 能够流畅播放720P甚至1080P高清MKV,FLV,MP4,MOV,TS,R ...

  2. Vitamio打造万能播放器使用步骤

    在电脑了,如果有一个.mp4文件 -高码率 拷贝到手机来,用系统自带的播放器播放.--基本码率LibVLC--MediaPlayerhttp://bbs.chinavideo.org/forumdis ...

  3. Android万能播放器Vitamio

    以下是一些参考博文,记录已下,方便以后使用 Android 如何直播RTMP流 农民伯伯-使用Vitamio打造万能播放器-1 农民伯伯-使用Vitamio打造万能播放器-2 一直更新到第9篇 农民伯 ...

  4. Vitamio打造自己的Android万能播放器

    前言 虽然Android已经内置了VideoView组件和MediaPlayer类来支持开发视频播放器,但支持格式.性能等各方面都十分有限,这里与大家一起利用免费的Vitamio来打造属于自己的And ...

  5. 使用Vitamio打造自己的Android万能播放器(1)——准备

    前言 虽然Android已经内置了VideoView组件和MediaPlayer类来支持开发视频播放器,但支持格式.性能等各方面都十分有限,这里与大家一起利用免费的Vitamio来打造属于自己的And ...

  6. 使用Vitamio打造自己的Android万能播放器

    前言 虽然Android已经内置了VideoView组件和MediaPlayer类来支持开发视频播放器,但支持格式.性能等各方面都十分有限,这里与大家一起利用免费的Vitamio来打造属于自己的And ...

  7. 使用Vitamio打造自己的Android万能播放器(2)—— 手势控制亮度、音量、缩放

    前言 本章继续完善播放相关播放器的核心功能,为后续扩展打好基础. 声明 欢迎转载,但请保留文章原始出处:)  博客园:http://www.cnblogs.com 农民伯伯: http://over1 ...

  8. 使用Vitamio打造自己的Android万能播放器(7)——在线播放(下载视频)

    前言 本章将实现非常实用的功能--下载在线视频.涉及到多线程.线程更新UI等技术,还需思考产品的设计,如何将新加的功能更好的融入到现有的产品中,并不是简单的加一个界面就行了,欢迎大家交流产品设计和技术 ...

  9. 使用Vitamio打造自己的Android万能播放器(5)——在线播放(播放优酷视频)

    前言 为了保证每周一篇的进度,又由于Vitamio新版本没有发布, 决定推迟本地播放的一些功能(截图.视频时间.尺寸等),跳过直接写在线播放部分的章节.从Vitamio的介绍可以看得出,其支持http ...

最新文章

  1. JavaScript 表单与表单验证
  2. (转)DOS批处理的字符串功能
  3. mysql 5.5.46_MySQL 5.5.46源码安装
  4. C++字符串分割替换 ubuntu版本
  5. docker 容器 日志_如何为Docker容器设置日志轮换
  6. 【转】jsp+servlet和SSM分别是如何实现文件上传(示例)
  7. Spring Cloud教程 (二)应用程序上下文服务层次结构
  8. python给用户打标签_用户画像—打用户行为标签
  9. 关于H5页面在微信端的分享
  10. 硬见小百科:尺寸公差、形位公差、表面粗糙度数值上的关系
  11. 简要html漂浮广告代码,JS漂浮广告代码
  12. 全国省市县三级级联SQL数据库内容
  13. 地震偏移成像matlab,地震偏移成像基本原理概述.ppt
  14. 阿里程序员,过完年第一天就要被劝退!让人感觉现实是如此残酷!
  15. UUID太长怎么办?快来试试NanoId(Java版本)
  16. 发射光功率和接收灵敏度的正常范围
  17. Golang使用qrcode生成二维码,以及生成带logo的二维码
  18. 关于 Caused by: org.a.a.e: Subscriber class com.xxx.xxx.activity.MainActivity and its super class
  19. MYSqL测试题2_MySQL 练习题2 参考答案
  20. 航芯案例分享 | 驾驶员监控系统全解析 为驾驶安全护航

热门文章

  1. Spark Streaming 转向 Structured Streaming
  2. openlayers实现路径动画
  3. 蓝桥杯之单片机学习(二十五)——温度记录器(附题目和完整代码)
  4. android系统源码7.1.2_r8下载,编译,运行到nexus5X上,修改源码并编译SDK进行测试
  5. ART 101× 烤仔 × DODO | 奇幻之旅,大奖揭晓
  6. android——截屏共享的坑,mmp
  7. 使用Tortoisegit,修改账户名密码
  8. 解决停车库分时间段收费的问题
  9. win10服务器文件夹在哪里找,recent文件夹在哪里找_recent文件夹怎么清理
  10. ssh: must specify HostKeyCallback