AbsVideoPlayerController.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. package org.yczbj.ycvideoplayerlib;
  2. import android.content.Context;
  3. import android.support.annotation.DrawableRes;
  4. import android.view.MotionEvent;
  5. import android.view.View;
  6. import android.view.WindowManager;
  7. import android.widget.FrameLayout;
  8. import android.widget.ImageView;
  9. import java.util.ArrayList;
  10. import java.util.Timer;
  11. import java.util.TimerTask;
  12. import java.util.concurrent.Executors;
  13. import java.util.concurrent.ScheduledExecutorService;
  14. import java.util.concurrent.TimeUnit;
  15. /**
  16. * @author yc
  17. * @date 2017/12/4
  18. * 参考项目:
  19. * https://github.com/CarGuo/GSYVideoPlayer
  20. * https://github.com/danylovolokh/VideoPlayerManager
  21. * https://github.com/HotBitmapGG/bilibili-android-client
  22. * https://github.com/jjdxmashl/jjdxm_ijkplayer
  23. * https://github.com/JasonChow1989/JieCaoVideoPlayer-develop 2年前
  24. * https://github.com/open-android/JieCaoVideoPlayer 1年前
  25. * https://github.com/lipangit/JiaoZiVideoPlayer 4个月前
  26. * 个人感觉jiaozi这个播放器,与JieCaoVideoPlayer-develop有惊人的类同,借鉴了上面两个项目[JieCao]
  27. *
  28. *
  29. * 注意:在对应的播放Activity页面,清单文件中一定要添加
  30. * android:configChanges="orientation|keyboardHidden|screenSize"
  31. * android:screenOrientation="portrait"
  32. *
  33. * 关于我的github:https://github.com/yangchong211
  34. * 关于我的个人网站:www.ycbjie.cn或者www.yczbj.org
  35. *
  36. * 控制器抽象类
  37. */
  38. public abstract class AbsVideoPlayerController extends FrameLayout implements View.OnTouchListener {
  39. private Context mContext;
  40. protected InterVideoPlayer mVideoPlayer;
  41. private Timer mUpdateProgressTimer;
  42. private TimerTask mUpdateProgressTimerTask;
  43. private float mDownX;
  44. private float mDownY;
  45. private boolean mNeedChangePosition;
  46. private boolean mNeedChangeVolume;
  47. private boolean mNeedChangeBrightness;
  48. private static final int THRESHOLD = 80;
  49. private long mGestureDownPosition;
  50. private float mGestureDownBrightness;
  51. private int mGestureDownVolume;
  52. private long mNewPosition;
  53. private ScheduledExecutorService pool;
  54. public AbsVideoPlayerController(Context context) {
  55. super(context);
  56. mContext = context;
  57. this.setOnTouchListener(this);
  58. }
  59. public void setVideoPlayer(InterVideoPlayer videoPlayer) {
  60. mVideoPlayer = videoPlayer;
  61. }
  62. /**
  63. * 设置试看视频时间,让使用者自己定制
  64. * @param time 时间
  65. */
  66. public abstract void setTrySeeTime(long time);
  67. /**
  68. * 设置不操作后,多久自动隐藏头部和底部布局
  69. * @param time 时间
  70. */
  71. public abstract void setHideTime(long time);
  72. /**
  73. * 设置会员权限话术内容
  74. * @param memberContent 集合
  75. */
  76. public abstract void setMemberContent(ArrayList<String> memberContent);
  77. /**
  78. * 设置会员权限类型
  79. * @param isLogin 是否登录
  80. * @param isSee 是否有权限看[及时登录,如果没有成为会员也需区分权限的]
  81. * @param type 视频类型
  82. * @param isSaveProgress 是否保存观看位置
  83. */
  84. public abstract void setMemberType(boolean isLogin , boolean isSee , int type , boolean isSaveProgress);
  85. /**
  86. * 设置加载loading类型
  87. *
  88. * @param type 加载loading的类型
  89. * 目前1,是仿腾讯加载loading
  90. * 2,是转圈加载loading
  91. */
  92. public abstract void setLoadingType(int type);
  93. /**
  94. * 设置播放的视频的标题
  95. *
  96. * @param title 视频标题
  97. */
  98. public abstract void setTitle(String title);
  99. /**
  100. * 视频底图
  101. *
  102. * @param resId 视频底图资源
  103. */
  104. public abstract void setImage(@DrawableRes int resId);
  105. /**
  106. * 视频底图ImageView控件,提供给外部用图片加载工具来加载网络图片
  107. *
  108. * @return 底图ImageView
  109. */
  110. public abstract ImageView imageView();
  111. /**
  112. * 设置总时长
  113. */
  114. public abstract void setLength(long length);
  115. /**
  116. * 设置总时长
  117. */
  118. public abstract void setLength(String length);
  119. /**
  120. * 当播放器的播放状态发生变化,在此方法中国你更新不同的播放状态的UI
  121. *
  122. * @param playState 播放状态:
  123. * <ul>
  124. * <li>{@link VideoPlayer#STATE_IDLE}</li>
  125. * <li>{@link VideoPlayer#STATE_PREPARING}</li>
  126. * <li>{@link VideoPlayer#STATE_PREPARED}</li>
  127. * <li>{@link VideoPlayer#STATE_PLAYING}</li>
  128. * <li>{@link VideoPlayer#STATE_PAUSED}</li>
  129. * <li>{@link VideoPlayer#STATE_BUFFERING_PLAYING}</li>
  130. * <li>{@link VideoPlayer#STATE_BUFFERING_PAUSED}</li>
  131. * <li>{@link VideoPlayer#STATE_ERROR}</li>
  132. * <li>{@link VideoPlayer#STATE_COMPLETED}</li>
  133. * </ul>
  134. */
  135. protected abstract void onPlayStateChanged(int playState);
  136. /**
  137. * 当播放器的播放模式发生变化,在此方法中更新不同模式下的控制器界面。
  138. *
  139. * @param playMode 播放器的模式:
  140. * <ul>
  141. * <li>{@link VideoPlayer#MODE_NORMAL}</li>
  142. * <li>{@link VideoPlayer#MODE_FULL_SCREEN}</li>
  143. * <li>{@link VideoPlayer#MODE_TINY_WINDOW}</li>
  144. * </ul>
  145. */
  146. protected abstract void onPlayModeChanged(int playMode);
  147. /**
  148. * 重置控制器,将控制器恢复到初始状态。
  149. */
  150. protected abstract void reset();
  151. /**
  152. * 开启更新进度的计时器。
  153. */
  154. protected void startUpdateProgressTimer() {
  155. cancelUpdateProgressTimer();
  156. // Java并发,Timer的缺陷,用ScheduledExecutorService替代
  157. /*if(pool==null){
  158. pool = Executors.newScheduledThreadPool(1);
  159. //pool = Executors.newSingleThreadScheduledExecutor();
  160. }
  161. if (mUpdateProgressTimerTask == null) {
  162. mUpdateProgressTimerTask = new TimerTask() {
  163. @Override
  164. public void run() {
  165. AbsVideoPlayerController.this.post(new Runnable() {
  166. @Override
  167. public void run() {
  168. updateProgress();
  169. }
  170. });
  171. }
  172. };
  173. }
  174. pool.scheduleWithFixedDelay(mUpdateProgressTimerTask,0,1000, TimeUnit.MILLISECONDS);*/
  175. if (mUpdateProgressTimer == null) {
  176. mUpdateProgressTimer = new Timer();
  177. }
  178. if (mUpdateProgressTimerTask == null) {
  179. mUpdateProgressTimerTask = new TimerTask() {
  180. @Override
  181. public void run() {
  182. AbsVideoPlayerController.this.post(new Runnable() {
  183. @Override
  184. public void run() {
  185. updateProgress();
  186. }
  187. });
  188. }
  189. };
  190. }
  191. mUpdateProgressTimer.schedule(mUpdateProgressTimerTask, 0, 1000);
  192. }
  193. /**
  194. * 取消更新进度的计时器。
  195. */
  196. protected void cancelUpdateProgressTimer() {
  197. if (mUpdateProgressTimer != null) {
  198. mUpdateProgressTimer.cancel();
  199. mUpdateProgressTimer = null;
  200. }
  201. if (mUpdateProgressTimerTask != null) {
  202. mUpdateProgressTimerTask.cancel();
  203. mUpdateProgressTimerTask = null;
  204. }
  205. }
  206. /**
  207. * 更新进度,包括进度条进度,展示的当前播放位置时长,总时长等。
  208. */
  209. protected abstract void updateProgress();
  210. @Override
  211. public boolean onTouch(View v, MotionEvent event) {
  212. // 只有全屏的时候才能拖动位置、亮度、声音
  213. if (!mVideoPlayer.isFullScreen()) {
  214. return false;
  215. }
  216. // 只有在播放、暂停、缓冲的时候能够拖动改变位置、亮度和声音
  217. if (mVideoPlayer.isIdle() || mVideoPlayer.isError() || mVideoPlayer.isPreparing()
  218. || mVideoPlayer.isPrepared() || mVideoPlayer.isCompleted()) {
  219. hideChangePosition();
  220. hideChangeBrightness();
  221. hideChangeVolume();
  222. return false;
  223. }
  224. float x = event.getX();
  225. float y = event.getY();
  226. switch (event.getAction()) {
  227. case MotionEvent.ACTION_DOWN:
  228. mDownX = x;
  229. mDownY = y;
  230. mNeedChangePosition = false;
  231. mNeedChangeVolume = false;
  232. mNeedChangeBrightness = false;
  233. break;
  234. case MotionEvent.ACTION_MOVE:
  235. float deltaX = x - mDownX;
  236. float deltaY = y - mDownY;
  237. float absDeltaX = Math.abs(deltaX);
  238. float absDeltaY = Math.abs(deltaY);
  239. if (!mNeedChangePosition && !mNeedChangeVolume && !mNeedChangeBrightness) {
  240. // 只有在播放、暂停、缓冲的时候能够拖动改变位置、亮度和声音
  241. if (absDeltaX >= THRESHOLD) {
  242. cancelUpdateProgressTimer();
  243. mNeedChangePosition = true;
  244. mGestureDownPosition = mVideoPlayer.getCurrentPosition();
  245. } else if (absDeltaY >= THRESHOLD) {
  246. if (mDownX < getWidth() * 0.5f) {
  247. // 左侧改变亮度
  248. mNeedChangeBrightness = true;
  249. mGestureDownBrightness = VideoPlayerUtils.scanForActivity(mContext)
  250. .getWindow().getAttributes().screenBrightness;
  251. } else {
  252. // 右侧改变声音
  253. mNeedChangeVolume = true;
  254. mGestureDownVolume = mVideoPlayer.getVolume();
  255. }
  256. }
  257. }
  258. if (mNeedChangePosition) {
  259. long duration = mVideoPlayer.getDuration();
  260. long toPosition = (long) (mGestureDownPosition + duration * deltaX / getWidth());
  261. mNewPosition = Math.max(0, Math.min(duration, toPosition));
  262. int newPositionProgress = (int) (100f * mNewPosition / duration);
  263. showChangePosition(duration, newPositionProgress);
  264. }
  265. if (mNeedChangeBrightness) {
  266. deltaY = -deltaY;
  267. float deltaBrightness = deltaY * 3 / getHeight();
  268. float newBrightness = mGestureDownBrightness + deltaBrightness;
  269. newBrightness = Math.max(0, Math.min(newBrightness, 1));
  270. float newBrightnessPercentage = newBrightness;
  271. WindowManager.LayoutParams params = VideoPlayerUtils.scanForActivity(mContext)
  272. .getWindow().getAttributes();
  273. params.screenBrightness = newBrightnessPercentage;
  274. VideoPlayerUtils.scanForActivity(mContext).getWindow().setAttributes(params);
  275. int newBrightnessProgress = (int) (100f * newBrightnessPercentage);
  276. showChangeBrightness(newBrightnessProgress);
  277. }
  278. if (mNeedChangeVolume) {
  279. deltaY = -deltaY;
  280. int maxVolume = mVideoPlayer.getMaxVolume();
  281. int deltaVolume = (int) (maxVolume * deltaY * 3 / getHeight());
  282. int newVolume = mGestureDownVolume + deltaVolume;
  283. newVolume = Math.max(0, Math.min(maxVolume, newVolume));
  284. mVideoPlayer.setVolume(newVolume);
  285. int newVolumeProgress = (int) (100f * newVolume / maxVolume);
  286. showChangeVolume(newVolumeProgress);
  287. }
  288. break;
  289. case MotionEvent.ACTION_CANCEL:
  290. case MotionEvent.ACTION_UP:
  291. if (mNeedChangePosition) {
  292. mVideoPlayer.seekTo(mNewPosition);
  293. hideChangePosition();
  294. startUpdateProgressTimer();
  295. return true;
  296. }
  297. if (mNeedChangeBrightness) {
  298. hideChangeBrightness();
  299. return true;
  300. }
  301. if (mNeedChangeVolume) {
  302. hideChangeVolume();
  303. return true;
  304. }
  305. break;
  306. default:
  307. break;
  308. }
  309. return false;
  310. }
  311. /**
  312. * 手势左右滑动改变播放位置时,显示控制器中间的播放位置变化视图,
  313. * 在手势滑动ACTION_MOVE的过程中,会不断调用此方法。
  314. *
  315. * @param duration 视频总时长ms
  316. * @param newPositionProgress 新的位置进度,取值0到100。
  317. */
  318. protected abstract void showChangePosition(long duration, int newPositionProgress);
  319. /**
  320. * 手势左右滑动改变播放位置后,手势up或者cancel时,隐藏控制器中间的播放位置变化视图,
  321. * 在手势ACTION_UP或ACTION_CANCEL时调用。
  322. */
  323. protected abstract void hideChangePosition();
  324. /**
  325. * 手势在右侧上下滑动改变音量时,显示控制器中间的音量变化视图,
  326. * 在手势滑动ACTION_MOVE的过程中,会不断调用此方法。
  327. *
  328. * @param newVolumeProgress 新的音量进度,取值1到100。
  329. */
  330. protected abstract void showChangeVolume(int newVolumeProgress);
  331. /**
  332. * 手势在左侧上下滑动改变音量后,手势up或者cancel时,隐藏控制器中间的音量变化视图,
  333. * 在手势ACTION_UP或ACTION_CANCEL时调用。
  334. */
  335. protected abstract void hideChangeVolume();
  336. /**
  337. * 手势在左侧上下滑动改变亮度时,显示控制器中间的亮度变化视图,
  338. * 在手势滑动ACTION_MOVE的过程中,会不断调用此方法。
  339. *
  340. * @param newBrightnessProgress 新的亮度进度,取值1到100。
  341. */
  342. protected abstract void showChangeBrightness(int newBrightnessProgress);
  343. /**
  344. * 手势在左侧上下滑动改变亮度后,手势up或者cancel时,隐藏控制器中间的亮度变化视图,
  345. * 在手势ACTION_UP或ACTION_CANCEL时调用。
  346. */
  347. protected abstract void hideChangeBrightness();
  348. }