MediaPlayer+surfaceView实现视频播放器

来源:互联网 发布:慕恋的喜欢lofter乐乎 编辑:程序博客网 时间:2024/06/05 21:18

最近项目中要用到视频播放器,就先写了个Demo,在写的过程中遇到些问题,来和大家分享一下:

1.Demo是基于Android电视的,因此与Android手机有点区别,但问题不大

2.MediaPlayer有个Bug,当视频播放完后getDuration(获取视频总时间)和getCurrentPosition(获取视频当前播放时间)方法获取到的数字不一样,相差300ms左右,如果单纯的播放不受影响,但是如果需要显示播放进度或播放时间就会有点小麻烦,暂时还没找到好的方法,只能在视频播放结束时处理时间,如果你们有好的方法也可以交流一下。

3.项目要实现显示当前时间的TextView与进度条同步移动,界面添加一个ViewGroup,然后在ViewGroup中放置TextView,通过TextView的Layout方法就能实现TextView的同步移动。

代码的解析挺详细,下面我就直接粘代码:

主Activity:

import java.io.IOException;import java.text.SimpleDateFormat;import java.util.TimeZone;import java.util.Timer;import java.util.TimerTask;import com.example.play.R;import com.luluvideo.view.TextMoveLayout;import android.app.Activity;import android.content.Intent;import android.content.res.AssetManager;import android.graphics.Color;import android.graphics.Typeface;import android.media.AudioManager;import android.media.MediaPlayer;import android.net.Uri;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.util.Log;import android.view.KeyEvent;import android.view.Menu;import android.view.SurfaceHolder;import android.view.SurfaceView;import android.view.View;import android.view.ViewGroup;import android.view.View.OnClickListener;import android.view.Window;import android.view.WindowManager;import android.view.animation.AlphaAnimation;import android.view.animation.Animation;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.ProgressBar;import android.widget.TextView;public class MainActivity extends Activity implements OnClickListener {private SurfaceView videoSurfaceView;// 视频播放器private MediaPlayer mediaPlayer;// 视频播放控制器private ImageView videoStart;// 播放private ImageView videoForward;// 快进private ImageView videoBack;// 快退private ImageView videoReport;// 举报视频private LinearLayout seekLayout;private LinearLayout videoProgress;private ProgressBar progressBar;// 视频进度条private TextView totalTime;// 播放总时间private View view;// 记忆获取焦点控件private TextView movieName;// 电影名字private TextView currentTime;// 当前播放时间private ViewGroup.LayoutParams layoutParams;private TextMoveLayout textMoveLayout;// 放置当前时间控件的ViewGroupprivate int screenWidth;// 屏幕宽度private Typeface roboto;// 自定义Roboto字体private float moveStep = 0;// 当前时间控件移动步长private SimpleDateFormat formatter;// 转换时间格式的类private SimpleDateFormat hourFormatter;private boolean isPlaying = true;// 视频播放状态标识private boolean isVisible = false;// 视频进度条隐藏或显示标识private boolean hourOrMin = false;private boolean threadRunning = true;private int postion = 0;private String uri = null;private AlphaAnimation animation_appear;// 进度条显现动画private AlphaAnimation animation_daiappear;// 进度条消失动画private float progressLength = 0;// 进度条相对长度private Thread thread = null;private MyTimerTask task;public static int PROGRESS_VISUBLE = 0;public static int PROGRESS_RATE = 1;public int i = 0;private Timer timer;/** * 刷新进度条和当前时间 */private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {if (msg.what == PROGRESS_RATE) {// 显示进度条progressBar.setProgress((Integer) msg.obj);// 设置当前时间currentTime.layout((int) ((Integer) msg.obj * moveStep), 0,(int) (screenWidth), 80);if (hourOrMin) {currentTime.setText(hourFormatter.format((Integer) msg.obj));} else {currentTime.setText(formatter.format((Integer) msg.obj));}} else if (msg.what == PROGRESS_VISUBLE) {Log.e("timer", msg.obj + "");if ((Integer) msg.obj == 4) {view = getCurrentFocus();// videoProgress// .setAnimation(animationAppear(animation_appear));// videoProgress.setVisibility(View.GONE);seekLayout.setAnimation(animationAppear(animation_appear));seekLayout.setVisibility(View.GONE);isVisible = false;timer.cancel();}}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN); // 全屏getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // 应用运行时,保持屏幕高亮,不锁屏initData();initView();getparams();movieName.setText("大圣归来");}private void getparams() {// TODO Auto-generated method stubIntent intent = getIntent();}private void initData() {// TODO Auto-generated method stubscreenWidth = getWindowManager().getDefaultDisplay().getWidth();progressLength = (float) (screenWidth * 0.625);formatter = new SimpleDateFormat("mm:ss");// 初始化Formatter的转换格式。formatter.setTimeZone(TimeZone.getTimeZone("GMT+00:00"));hourFormatter = new SimpleDateFormat("HH:mm:ss");// 初始化Formatter的转换格式。hourFormatter.setTimeZone(TimeZone.getTimeZone("GMT+00:00"));mediaPlayer = new MediaPlayer();layoutParams = new ViewGroup.LayoutParams((int) (screenWidth), 20);uri = "android.resource://" + getPackageName() + "/" + R.raw.test;AssetManager mgr = getAssets();// 得到AssetManagerroboto = Typeface.createFromAsset(mgr, "fonts/roboto.ttf");// 根据路径得到TypefaceisPlaying = true;isVisible = false;hourOrMin = false;threadRunning = true;}private void initView() {videoSurfaceView = (SurfaceView) findViewById(R.id.videoSurfaceView);videoSurfaceView.getHolder().setKeepScreenOn(true);videoSurfaceView.getHolder().addCallback(new SurfaceViewVideo());currentTime = new TextView(this);currentTime.setTextColor(Color.rgb(255, 255, 255));currentTime.setTextSize(12);currentTime.setTypeface(roboto);videoProgress = (LinearLayout) findViewById(R.id.vedioProgress);videoStart = (ImageView) videoProgress.findViewById(R.id.videoStart);videoForward = (ImageView) videoProgress.findViewById(R.id.videoForward);videoBack = (ImageView) videoProgress.findViewById(R.id.videoBack);videoReport = (ImageView) videoProgress.findViewById(R.id.videoReqort);progressBar = (ProgressBar) videoProgress.findViewById(R.id.videoProgress);totalTime = (TextView) videoProgress.findViewById(R.id.videoTotal);movieName = (TextView) findViewById(R.id.movieName);seekLayout = (LinearLayout) findViewById(R.id.seekLayout);movieName.setTypeface(roboto);totalTime.setTypeface(roboto);videoStart.setFocusable(true);videoStart.requestFocus();videoStart.setOnClickListener(this);videoForward.setFocusable(true);videoForward.setOnClickListener(this);videoBack.setFocusable(true);videoBack.setOnClickListener(this);videoReport.setFocusable(true);videoReport.setOnClickListener(this);videoProgress.findViewById(R.id.videoForward).setOnClickListener(this);videoProgress.findViewById(R.id.videoBack).setOnClickListener(this);videoProgress.findViewById(R.id.videoReqort).setOnClickListener(this);textMoveLayout = (TextMoveLayout) videoProgress.findViewById(R.id.videoCurrent);textMoveLayout.addView(currentTime, layoutParams);currentTime.layout(0, 0, (int) (screenWidth), 80);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.main, menu);return true;}/** * 设置播放文件 */public void play() throws IllegalArgumentException, SecurityException,IllegalStateException, IOException {mediaPlayer.reset();mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);mediaPlayer.setDataSource(MainActivity.this, Uri.parse(uri));// 把视频输出到SurfaceView上mediaPlayer.setDisplay(videoSurfaceView.getHolder());mediaPlayer.prepare();mediaPlayer.start();progressBar.setMax(mediaPlayer.getDuration());moveStep = (float) ((progressLength / (float) mediaPlayer.getDuration()));// 开启进度刷新线程thread = new Thread(runnable);thread.start();}private Animation animationAppear(Animation animation) {animation = new AlphaAnimation(1.0f, 0.0f);animation.setDuration(1000);animation.setFillAfter(true);return animation;}private Animation animationDiaappear(Animation animation) {animation = new AlphaAnimation(0.0f, 1.0f);animation.setDuration(1000);animation.setFillAfter(true);return animation;}/* * SurfaceHolder可以调用 SurfaceView播放视频 */private class SurfaceViewVideo implements SurfaceHolder.Callback {@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {}@Overridepublic void surfaceCreated(SurfaceHolder holder) {if (postion == 0) {try {play();mediaPlayer.seekTo(postion);if (mediaPlayer.getDuration() >= 3600000) {totalTime.setText(hourFormatter.format(mediaPlayer.getDuration()));hourOrMin = true;} else {totalTime.setText(formatter.format(mediaPlayer.getDuration()));hourOrMin = false;}// totalTime.setText(formatter.format(3600000));} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalStateException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {}}@Overrideprotected void onPause() {if (mediaPlayer.isPlaying()) {// 保存当前播放的位置postion = mediaPlayer.getCurrentPosition();mediaPlayer.stop();}super.onPause();}@Overrideprotected void onDestroy() {if (thread != null) {threadRunning = false;thread = null;}if (mediaPlayer.isPlaying()) {mediaPlayer.stop();}mediaPlayer.release();super.onDestroy();}@Overridepublic void onClick(View v) {// TODO Auto-generated method stubswitch (v.getId()) {case R.id.videoForward:mediaGo();break;case R.id.videoBack:mediaBack();break;case R.id.videoStart:mediaPlay();break;case R.id.videoReqort:mediaReport();break;default:break;}}// 视频快进private void mediaGo() {}// 视频快退private void mediaBack() {}// 视频举报private void mediaReport() {}private void startTimer() {i = 0;if (timer != null) {if (task != null) {task.cancel(); // 将原任务从队列中移除timer.cancel();}}task = new MyTimerTask(); // 新建一个任务timer = new Timer(true);timer.schedule(task, 1000, 1000); // 延时1000ms后执行,1000ms执行一次}// 视频播放private void mediaPlay() {if (isPlaying) {mediaPlayer.pause();videoStart.setImageResource(R.drawable.video_pause_style);isPlaying = false;} else {mediaPlayer.start();videoStart.setImageResource(R.drawable.video_start_style);isPlaying = true;}}// 定义遥控按键@Overridepublic boolean dispatchKeyEvent(KeyEvent event) {// TODO Auto-generated method stubif (event.getKeyCode() == KeyEvent.KEYCODE_BACK&& event.getAction() == KeyEvent.ACTION_DOWN && isVisible) {view = getCurrentFocus();seekLayout.setAnimation(animationAppear(animation_appear));isVisible = false;seekLayout.setVisibility(View.GONE);if (timer != null) {if (task != null) {task.cancel(); // 将原任务从队列中移除timer.cancel();}}return true;} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN&& event.getAction() == KeyEvent.ACTION_DOWN && !isVisible) {if (view != null) {view.setFocusable(true);view.requestFocus();}seekLayout.setAnimation(animationDiaappear(animation_daiappear));isVisible = true;seekLayout.setVisibility(View.VISIBLE);startTimer();return true;} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT&& event.getAction() == KeyEvent.ACTION_DOWN && isVisible) {startTimer();} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT&& event.getAction() == KeyEvent.ACTION_DOWN && isVisible) {startTimer();}return super.dispatchKeyEvent(event);}/** *  */private Runnable runnable = new Runnable() {@Overridepublic void run() {while (threadRunning&& mediaPlayer.getCurrentPosition() < mediaPlayer.getDuration()) {Message msg = handler.obtainMessage();msg.what = PROGRESS_RATE;msg.obj = mediaPlayer.getCurrentPosition();Log.e("currentPosition", mediaPlayer.getCurrentPosition() + "");handler.sendMessage(msg);try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}};class MyTimerTask extends TimerTask {@Overridepublic void run() {Message message = new Message();message.what = PROGRESS_VISUBLE;message.obj = ++i;handler.sendMessage(message);}}}
自定义的ViewGroup:

import android.content.Context;import android.util.AttributeSet;import android.view.ViewGroup;/** *  * @author YiWei * TextMoveLayout用来放置显示时间的TextView,从而实现时间随进度条移动 * */public class TextMoveLayout extends ViewGroup {public TextMoveLayout(Context context) {super(context);}public TextMoveLayout(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}public TextMoveLayout(Context context, AttributeSet attrs) {super(context, attrs);}/** * onLayout用来布局子控件,实时调用onLayout就可以改变子布局位置 */@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {}}

布局文件

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context=".MainActivity" >    <SurfaceView        android:id="@+id/videoSurfaceView"        android:layout_width="match_parent"        android:layout_height="match_parent" />        <LinearLayout        android:id="@+id/seekLayout"         android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_gravity="bottom|center_horizontal"        android:orientation="vertical"        android:visibility="visible">        <TextView         android:id="@+id/movieName"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginBottom="@dimen/movie_name_bottom"        android:layout_marginLeft="@dimen/movie_name_left"        android:textColor="@color/movie_name_color"        android:textSize="@dimen/movie_name_size"        android:shadowColor="@color/movie_name_back"        android:shadowDx="4"        android:shadowDy="4"        android:shadowRadius="2"/>    <include        android:id="@+id/vedioProgress"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginBottom="@dimen/progress_bottom"        android:layout_marginLeft="@dimen/movie_name_left"        android:layout_marginRight="@dimen/progress_right"        layout="@layout/video_progress_layout"        android:background="@color/progress_back"         />        </LinearLayout></FrameLayout>

进度条布局文件:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_gravity="center_horizontal"    android:background="@color/progress_back"    android:orientation="horizontal"    android:weightSum="1668" >    <ImageView        android:id="@+id/videoBack"        android:layout_width="@dimen/video_start_width"        android:layout_height="wrap_content"        android:layout_marginTop="@dimen/video_start_top"        android:layout_weight="60"        android:src="@drawable/video_back_style" />    <ImageView        android:id="@+id/videoStart"        android:layout_width="@dimen/video_start_width"        android:layout_height="wrap_content"        android:layout_marginLeft="@dimen/video_start_left"        android:layout_marginTop="@dimen/video_start_top"        android:layout_weight="60"        android:src="@drawable/video_start_style" />    <ImageView        android:id="@+id/videoForward"        android:layout_width="@dimen/video_start_width"        android:layout_height="wrap_content"        android:layout_marginLeft="@dimen/video_start_left"        android:layout_marginTop="@dimen/video_start_top"        android:layout_weight="60"        android:src="@drawable/video_forward_style" />    <ImageView        android:id="@+id/videoReqort"        android:layout_width="@dimen/video_start_width"        android:layout_height="wrap_content"        android:layout_marginLeft="@dimen/video_start_left"        android:layout_marginTop="@dimen/video_start_top"        android:layout_weight="60"        android:src="@drawable/video_report_style" />    <LinearLayout        android:layout_width="@dimen/video_start_width"        android:layout_height="wrap_content"        android:layout_weight="1268"        android:orientation="vertical" >        <com.luluvideo.view.TextMoveLayout            android:id="@+id/videoCurrent"            android:layout_width="match_parent"            android:layout_height="@dimen/text_move_height" />        <ProgressBar            android:id="@+id/videoProgress"            style="?android:attr/progressBarStyleHorizontal"            android:layout_width="match_parent"            android:layout_height="@dimen/progressbar_height"            android:layout_marginLeft="@dimen/progressbar_left"            android:layout_marginRight="@dimen/progressbar_right"            android:layout_marginTop="@dimen/progressbar_top"            android:max="100"            android:progressDrawable="@drawable/video_progress_style"             />    </LinearLayout>    <TextView        android:id="@+id/videoTotal"        android:layout_width="@dimen/video_start_width"        android:layout_height="wrap_content"        android:layout_gravity="center_vertical"        android:layout_marginLeft="@dimen/video_total_time_left"        android:layout_marginTop="@dimen/video_total_time_top"        android:layout_weight="100"        android:textColor="#ffffff"        android:textSize="@dimen/video_total_time_size"/></LinearLayout>

由于特殊原因不能上传项目,把视频文放到raw文件下(最好MP4,有的格式MediaPlayer不识别),有不懂的可以回复交流。


1 0
原创粉丝点击