最近项目中要用到视频播放器,就先写了个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;// 放置当前时间控件的ViewGroup
private 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() {
@Override
public 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();
}
}
}
};
@Override
protected 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 stub
Intent intent = getIntent();
}
private void initData() {
// TODO Auto-generated method stub
screenWidth = 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();// 得到AssetManager
roboto = Typeface.createFromAsset(mgr, "fonts/roboto.ttf");// 根据路径得到Typeface
isPlaying = 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);
}
@Override
public 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 {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public 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 block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
@Override
protected void onPause() {
if (mediaPlayer.isPlaying()) {
// 保存当前播放的位置
postion = mediaPlayer.getCurrentPosition();
mediaPlayer.stop();
}
super.onPause();
}
@Override
protected void onDestroy() {
if (thread != null) {
threadRunning = false;
thread = null;
}
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
mediaPlayer.release();
super.onDestroy();
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (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;
}
}
// 定义遥控按键
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
// TODO Auto-generated method stub
if (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() {
@Override
public 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 block
e.printStackTrace();
}
}
}
};
class MyTimerTask extends TimerTask {
@Override
public 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就可以改变子布局位置
*/
@Override
protected 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不识别),有不懂的可以回复交流。