Android系列之自定义视频播放器

  • 自述
  • xml部分
  • Java对面部分
  • 需要使用到的Animation资源
  • 需要自定义的工具类

自述

自己写的一个简易视频播放器,如果有需要的话欢迎参考和转载,但拒绝抄袭,谢谢理解与配合。
转载地址:自定义视频播放器

xml部分

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#000000"tools:context=".fragment.VideoPlayFragment"><ImageViewandroid:id="@+id/click_area"android:layout_width="match_parent"android:layout_height="0dp"android:alpha="0"app:layout_constraintBottom_toTopOf="@+id/control_ll"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><ImageViewandroid:id="@+id/playOrPause"android:layout_width="60dp"android:layout_height="60dp"android:layout_centerInParent="true"android:src="@android:drawable/ic_media_play"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.5"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><RelativeLayoutandroid:id="@+id/control_ll"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:background="#005500"android:orientation="horizontal"android:paddingBottom="5dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"><TextViewandroid:id="@+id/tv_start_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_marginLeft="10dp"android:text="00.00"android:textColor="#ffffff" /><TextViewandroid:id="@+id/tv_separate_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="1dp"android:layout_toRightOf="@+id/tv_start_time"android:text="/"android:textColor="#ffffff" /><TextViewandroid:id="@+id/tv_end_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="1dp"android:layout_toRightOf="@+id/tv_separate_time"android:text="00.00"android:textColor="#ffffff" /><ImageViewandroid:id="@+id/tv_backward"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@+id/tv_start_time"android:layout_alignParentLeft="true"android:layout_marginLeft="1dp"android:src="@android:drawable/ic_media_rew" /><SeekBarandroid:id="@+id/tv_progess"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_below="@+id/tv_start_time"android:layout_toLeftOf="@+id/tv_forward"android:layout_toRightOf="@+id/tv_backward" /><ImageViewandroid:id="@+id/tv_forward"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@+id/tv_start_time"android:layout_alignParentRight="true"android:layout_marginRight="1dp"android:src="@android:drawable/ic_media_ff" /></RelativeLayout></androidx.constraintlayout.widget.ConstraintLayout>

Java对面部分

VideoPlayFragment

package zhonghui.he.video.fragment;import android.content.Context;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.media.MediaPlayer;
import android.os.Bundle;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.util.TimeUtils;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.VideoView;import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatterBuilder;
import java.util.Timer;
import java.util.TimerTask;import zhonghui.he.video.R;
import zhonghui.he.video.utils.FormatTimeUtil;
import zhonghui.he.video.viewmodel.VideoPlayViewModel;public class VideoPlayFragment extends Fragment {public final static String TAG = VideoPlayFragment.class.getSimpleName();private final int UPDATE_PLAY_STATUS = 0x001;private String path;private SurfaceView surfaceView;private ImageView playOrPauseIcon;private ImageView clickArea;private MediaPlayer mediaPlayer;private RelativeLayout control;private TextView controlStartTime;private TextView controlEndTime;private ImageView controlForwardIcon;private SeekBar controlProgress;private ImageView controlBackwardIcon;private Animation fadeInAnim, fadeOutAnim, slideUpAnim, slideDownAnim;private Handler mHandler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(@NonNull Message msg) {switch (msg.what) {case UPDATE_PLAY_STATUS:updateControlShow();break;}return true;}});private Timer updatePlayStatusTimer = new Timer();private TimerTask updatePlayStatusTimerTask = new TimerTask() {@Overridepublic void run() {mHandler.sendEmptyMessage(UPDATE_PLAY_STATUS);}};private int backwardSecond = 5 * 1000;private int forwardSecond = 5 * 1000;public VideoPlayFragment() {}@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {return inflater.inflate(R.layout.fragment_video_play, container, false);}@Overridepublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);try {handle();} catch (IOException e) {e.printStackTrace();}}@Overridepublic void onStart() {super.onStart();}@Overridepublic void onPause() {super.onPause();// 通知定时器通知执行任务updatePlayStatusTimerTask.cancel();// 保存当前播放进度、播放状态VideoPlayViewModel videoPlayViewModel = new ViewModelProvider(requireActivity()).get(VideoPlayViewModel.class);videoPlayViewModel.setVideoPlayPosition(mediaPlayer.getCurrentPosition());videoPlayViewModel.setPlaying(mediaPlayer.isPlaying());// 停止播放视频并且释放占用的资源mediaPlayer.release();}private void handle() throws IOException {path = getActivity().getExternalFilesDir(null).getPath() + "/babyb.mp4";initView();animationConfig();mediaPlayConfig();clickAreaConfig();controlProgressConfig();controlBackwardAndForwardConfig();}private void controlBackwardAndForwardConfig() {controlBackwardIcon.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {int currentPosition = mediaPlayer.getCurrentPosition();currentPosition -= backwardSecond;mediaPlayer.seekTo(currentPosition);play();}});controlForwardIcon.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {int currentPosition = mediaPlayer.getCurrentPosition();currentPosition += forwardSecond;mediaPlayer.seekTo(currentPosition);play();}});}private void controlProgressConfig() {controlProgress.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {if (fromUser) {mediaPlayer.seekTo(progress);play();}}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {}});}private void animationConfig() {fadeInAnim = AnimationUtils.loadAnimation(getContext(), R.anim.fade_in);fadeOutAnim = AnimationUtils.loadAnimation(getContext(), R.anim.fade_out);slideUpAnim = AnimationUtils.loadAnimation(getContext(), R.anim.slide_up);slideDownAnim = AnimationUtils.loadAnimation(getContext(), R.anim.slide_down);fadeInAnim.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {if (fadeOutAnim.hasStarted() && !fadeOutAnim.hasEnded()) {fadeOutAnim.cancel();}playOrPauseIcon.setImageResource(android.R.drawable.ic_media_play);}@Overridepublic void onAnimationEnd(Animation animation) {playOrPauseIcon.setVisibility(View.VISIBLE);playOrPauseIcon.clearAnimation();}@Overridepublic void onAnimationRepeat(Animation animation) {}});fadeOutAnim.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {if (fadeInAnim.hasStarted() && !fadeInAnim.hasEnded()) {fadeInAnim.cancel();}playOrPauseIcon.setImageResource(android.R.drawable.ic_media_pause);}@Overridepublic void onAnimationEnd(Animation animation) {playOrPauseIcon.setVisibility(View.GONE);playOrPauseIcon.clearAnimation();}@Overridepublic void onAnimationRepeat(Animation animation) {}});slideUpAnim.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {if (slideDownAnim.hasStarted() && !slideDownAnim.hasEnded()) {slideDownAnim.cancel();}}@Overridepublic void onAnimationEnd(Animation animation) {control.setVisibility(View.VISIBLE);control.clearAnimation();}@Overridepublic void onAnimationRepeat(Animation animation) {}});slideDownAnim.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {if (slideUpAnim.hasStarted() && !slideUpAnim.hasEnded()) {slideUpAnim.cancel();}}@Overridepublic void onAnimationEnd(Animation animation) {control.setVisibility(View.GONE);control.clearAnimation();}@Overridepublic void onAnimationRepeat(Animation animation) {}});}private void initView() {View view = getView();if (view == null)throw new IllegalArgumentException("view is null");clickArea = (ImageView) view.findViewById(R.id.click_area);playOrPauseIcon = (ImageView) view.findViewById(R.id.playOrPause);control = (RelativeLayout) view.findViewById(R.id.control_ll);controlStartTime = (TextView) view.findViewById(R.id.tv_start_time);controlEndTime = (TextView) view.findViewById(R.id.tv_end_time);controlBackwardIcon = (ImageView) view.findViewById(R.id.tv_backward);controlProgress = (SeekBar) view.findViewById(R.id.tv_progess);controlForwardIcon = (ImageView) view.findViewById(R.id.tv_forward);}private void clickAreaConfig() {clickArea.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (mediaPlayer.isPlaying()) {pause();} else {play();}}});}private void surfaceViewConfig() {surfaceView.setZOrderOnTop(false);surfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {@Overridepublic void surfaceCreated(@NonNull SurfaceHolder holder) {mediaPlayer.setDisplay(holder);}@Overridepublic void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(@NonNull SurfaceHolder holder) {}});}private void mediaPlayConfig() throws IOException {mediaPlayer = new MediaPlayer();mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer mp) {pause();}});mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {@Overridepublic boolean onError(MediaPlayer mp, int what, int extra) {return false;}});mediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {@Overridepublic boolean onInfo(MediaPlayer mp, int what, int extra) {return false;}});mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {@Overridepublic void onPrepared(MediaPlayer mp) {settingDisplayArea();startUpdatePlayStatusTimer();initPlayStatus();}});mediaPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() {@Overridepublic void onSeekComplete(MediaPlayer mp) {}});mediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {@Overridepublic void onVideoSizeChanged(MediaPlayer mp, int width, int height) {}});mediaPlayer.setDataSource(path);mediaPlayer.prepareAsync();}private void initPlayStatus() {VideoPlayViewModel videoPlayViewModel = new ViewModelProvider(requireActivity()).get(VideoPlayViewModel.class);int videoPlayPosition = videoPlayViewModel.getVideoPlayPosition();boolean playing = videoPlayViewModel.isPlaying();mediaPlayer.seekTo(videoPlayPosition);if (playing) {play();} else {pauseAnimation();}}// 设置视频展示区域private void settingDisplayArea() {int videoWidth = mediaPlayer.getVideoWidth();int videoHeight = mediaPlayer.getVideoHeight();DisplayManager displayManager =(DisplayManager) getActivity().getSystemService(Context.DISPLAY_SERVICE);Display display = displayManager.getDisplay(0);DisplayMetrics displayMetrics = new DisplayMetrics();display.getRealMetrics(displayMetrics);int widthPixels = displayMetrics.widthPixels;int heightPixels = displayMetrics.heightPixels;int widthPixelsMiddle = widthPixels / 2;int heightPixelsMiddle = heightPixels / 2;int width = 0, height = 0;height = widthPixels * videoHeight / videoWidth;if (height > heightPixels) {width = videoWidth * heightPixels / videoHeight;height = width * videoHeight / videoWidth;} else {width = videoWidth * height / videoHeight;}int x = widthPixelsMiddle - width / 2;int y = heightPixelsMiddle - height / 2;// 创建并添加一个视频展示区域,设置合适的展示区域大小、位置。ConstraintLayout constraintLayout = (ConstraintLayout) getView();surfaceView = new SurfaceView(getContext());surfaceView.setX(x);surfaceView.setY(y);constraintLayout.addView(surfaceView, 0, new ConstraintLayout.LayoutParams(width, height));surfaceViewConfig();}// 更新自定义控制器展示数据,如:已播放时长、总时长、播放进度条private void updateControlShow() {controlStartTime.setText(FormatTimeUtil.format(mediaPlayer.getCurrentPosition()));controlEndTime.setText(FormatTimeUtil.format(mediaPlayer.getDuration()));controlProgress.setMax(mediaPlayer.getDuration());controlProgress.setProgress(mediaPlayer.getCurrentPosition());}private void pause() {mediaPlayer.pause();pauseAnimation();}private void play() {mediaPlayer.start();playAnimation();}private void pauseAnimation() {playOrPauseIcon.startAnimation(fadeInAnim);control.startAnimation(slideUpAnim);}private void playAnimation() {playOrPauseIcon.startAnimation(fadeOutAnim);control.startAnimation(slideDownAnim);}private void startUpdatePlayStatusTimer() {updatePlayStatusTimer.schedule(updatePlayStatusTimerTask, 0, 1000);}private void stopUpdatePlayStatusTimer() {updatePlayStatusTimer.cancel();}}

需要使用到的Animation资源

fade_in.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"android:duration="200"><alphaandroid:fromAlpha="0"android:toAlpha="1" />
</set>

fade_in.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"android:duration="1500" android:startOffset="2000"><alphaandroid:fromAlpha="1"android:toAlpha="0" />
</set>

slide_down.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"android:duration="500" android:startOffset="2000"><translateandroid:fromYDelta="0%"android:toYDelta="100%" />
</set>

slide_up.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"android:duration="200"><translateandroid:fromYDelta="100%"android:toYDelta="0%" />
</set>

需要自定义的工具类

FormatTimeUtil

package zhonghui.he.video.utils;import java.text.DecimalFormat;public class FormatTimeUtil {private static DecimalFormat decimalFormat = new DecimalFormat("00");public static String format(long milliseconds) {milliseconds = milliseconds / 1000;int second = (int) milliseconds % 60;int mirror = (int) milliseconds / 60 % 60;int hour = (int) milliseconds / 60 / 60 % 60;StringBuffer stringBuffer = new StringBuffer();return stringBuffer.append(decimalFormat.format(hour)).append(":").append(decimalFormat.format(mirror)).append(":").append(decimalFormat.format(second)).toString();}}

Android系列之自定义视频播放器相关推荐

  1. Android进阶:自定义视频播放器开发(下)

    上一篇文章我们主要讲了视频播放器开发之前需要准备的一个知识,TextureView,用于对图像流的处理.这篇文章开始构建一个基础的视频播放器. 一.准备工作 在之前的文章已经说过了,播放器也是一个vi ...

  2. Android进阶:自定义视频播放器开发(上)

    随着快手,抖音,西瓜视频等视频APP的崛起,视频播放已经成为主流,此时作为Android研发的你,想要提高自己的能力还不知道怎么开发视频播放器怎么行?所以今天就带着大家一起开发一个简易播放器:Smal ...

  3. Android播放器自定义,android surfaceView+mediaPlayer 自定义视频播放器

    你可以参考我的写法,注意点就是你自定义的MyCallBack()里面要回调,也就是我写的SurfaceCallBack()里面的SurfaceCreated()函数要实现你写的setOnPrepare ...

  4. 《android多媒体api》之MediaPlayer自定义视频播放器

    <android多媒体api>系列是整合梳理android开发中经常用到的媒体相关api:多媒体开发主要内容有音频.视频录制播放.摄像头操作.录制操作.流媒体.直播.推流.拉流等方面:最近 ...

  5. Android开发笔记(一百二十五)自定义视频播放器

    视频播放方式 在Android中播放视频的方式有两种: 1.使用MediaPlayer结合SurfaceView进行播放.其中通过SurfaceView显示视频的画面,通过MediaPlayer来设置 ...

  6. Android自定义视频播放器(三)

    参看:Android自定义视频播放器(一):https://blog.csdn.net/zxd1435513775/article/details/81507909 参看:Android自定义视频播放 ...

  7. Android 自定义视频播放器

    由于录像之后,原先选用的腾讯VOD点播播放器显示出来竖屏都变横屏了,虽然选中了现在的腾讯VOD点播,还是把Android视频播放器了解了一番. Android自定义视频播放器有以下三种: 一.Medi ...

  8. 一步步自定义视频播放器——TextureView+MediaPlayer自定义视频播放器

    本篇参考封装一个视频播放器,原文已经写的非常棒了,本篇加入了个人对其内容的理解.秉承不重复造轮子的良好理念,接下来开始拆解轮子.内容非常多,我都差点放弃写,有耐心的请往下看 github上非常棒的视频 ...

  9. HTML5 自定义视频播放器

    HTML5 自定义视频播放器 第一步:获取播放器 第二步:实现播放与暂停 第三步:实现全屏操作 第四步:实现播放的逻辑 第五步:实现播放过程的逻辑 第六步:实现视频的跳播 第七步:播放完毕后重置播放器 ...

最新文章

  1. 为什么深度学习不能取代传统的计算机视觉技术?
  2. leetcode算法题--仅仅反转字母
  3. ginkgo测试介绍
  4. 关于浏览器和浏览器内核的解释
  5. dos 删除文件夹 rd
  6. 应用程序虚拟化部署笔记二
  7. java二级考试历年真题6_计算机等级考试二级JAVA练习题及答案6
  8. Oracle 的一些语句
  9. pdfstamper生成pdf无法显示汉字_正点原子STM32F4/F7水星开发板资料连载第四十六章 汉字显示实验...
  10. [HNOI2010]BOUNCE 弹飞绵羊
  11. 2018.09.18 while循环
  12. GCC + pthread
  13. 华为cor—al10_cor al10是华为什么型号 cor al10是华为啥型号
  14. hibernate、java、数据库对应类型
  15. 中考可以使用计算机吗,中考报志愿必须用电脑吗
  16. 28muduo_net库源码分析(四)
  17. 剑指offer刷题 04. 二维数组中的查找
  18. Python del:删除对象
  19. 伪静态 全站php 跳到html,IIS下万能301跳转方法:URL伪静态重写+PHP301
  20. 逆向之Smali入门学习

热门文章

  1. python打包上传至pypi —— 具有多个目录的项目工程快速打包上传
  2. 2.6_2 NoSQL概述
  3. SVN上传文件到服务器
  4. EVE模拟交换机三层EC和Linux服务器端口绑定(bond team)对接
  5. ResultSetMetaData 中的方法介绍
  6. [tensorflow]各个tensorflow版本和CUDA版本对应,以及各个GPU版本CUDA和cuDNN对应
  7. Spring Security+JWT简述
  8. CreateProcess()函数详解
  9. NumPy处理图像:色彩取反、图片变灰、图像手绘
  10. System Generator从入门到放弃(六)-利用Vivado HLS block实现VivadoHLS调用C/C++代码