Android中使用SurfaceView+MediaPlayer+自定义的MediaController实现自定义的视屏播放器

效果图如下:

(PS本来是要给大家穿gif动态图的,无奈太大了,没法上传)

功能实现:暂停,播放,快进,快退,全屏,退出全屏,等基本功能

实现的思路:

在主布局中放置一个SurfaceView,在SurfaceView中放置一个MediaPlayer ,在其下方自定义一个MediaController,不过也不能称之为MediaController,使用的是PupupWindow来实现的,在PupupWindow布局中放置几个textView,Button,最重要的使我们的SeekBar控件,创建一个定时器,当用户触摸屏幕时,让其popupWindow显示5秒的时间,具体实现可以看代码(主程序的代码有点多,耐心点看啊)

主布局activity_main.xml文件

<RelativeLayout 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:layout_width="match_parent"
        android:layout_height="260dp"
        android:id="@+id/surfaceView_main"
        />

    <ImageView
        android:onClick="clickButton"
        android:id="@+id/imageView_main_play"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="100dp"
        android:src="@drawable/ic_launcher" />

</RelativeLayout>

  popupwindow.xml的布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/bottom_layout"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:gravity="center"
    android:orientation="horizontal"
    android:background="@drawable/voip_toast_bg"
    android:paddingLeft="5dp"
    android:paddingRight="5dp"
    android:weightSum="10" >

    <ImageView
        android:id="@+id/imageView_play"
        android:layout_width="0dp"
        android:layout_height="30dp"
        android:layout_weight="1"
        android:src="@drawable/video_btn_on" />

    <SeekBar
        android:id="@+id/seekbar"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="5.0"
        android:max="100"
        android:maxHeight="5dp"
        android:minHeight="5dp"
        android:layout_marginLeft="5dp"
        android:progress="0"
        android:thumbOffset="0dp" />

    <TextView
        android:id="@+id/textView_playtime"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1.3"
        android:gravity="center"
        android:text="00:00"
        android:textColor="@android:color/white"
        android:textSize="12sp" />

    <TextView
        android:id="@+id/textView_playtime"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="0.2"
        android:gravity="center"
        android:text="/"
        android:textColor="@android:color/white"
        android:textSize="12sp" />

    <TextView
        android:id="@+id/textView_totaltime"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1.3"
        android:gravity="center"
        android:text="00:00"
        android:textColor="@android:color/white"
        android:textSize="12sp" />

    <ImageView
        android:id="@+id/imageView_fullscreen"
        android:layout_width="0dp"
        android:layout_height="30dp"
        android:layout_weight="1"
        android:src="@drawable/video_full_screen" />

</LinearLayout>

  

主Activity中的代码:

package com.amy.day43_03_SurfaceViewMediaPlayer;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import android.R.integer;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.text.format.DateFormat;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
import android.widget.MediaController;
import android.widget.PopupWindow;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

public class MainActivity extends Activity {
	private final static String TAG = "MainActivity";
	private Context mContext = this;
	private SurfaceView surfaceView = null;
	private SurfaceHolder surfaceHolder = null;
	private MediaPlayer mediaPlayer = null;
	private ImageView imageView_main_show = null;

	// 自定义的控制条及其上的控件
	private View controllerView;
	private PopupWindow popupWindow;

	private ImageView imageView_play;
	private ImageView imageView_fullscreen;
	private SeekBar seekBar;
	private TextView textView_playTime;
	private TextView textView_duration;
	private String filePath = null;

	private float densityRatio = 1.0f; // 密度比值系数(密度比值:一英寸中像素点除以160)

	private Runnable r = new Runnable() {
		@Override
		public void run() {
			// 又回到了主线程
			showOrHiddenController();
		}
	};

	private MyVideoBroadcastReceiver receiver = null;

	// 设置定时器
	private Timer timer = null;
	private final static int WHAT = 0;
	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case WHAT:
				if (mediaPlayer != null) {
					int currentPlayer = mediaPlayer.getCurrentPosition();
					if (currentPlayer > 0) {
						mediaPlayer.getCurrentPosition();
						textView_playTime.setText(formatTime(currentPlayer));

						// 让seekBar也跟随改变
						int progress = (int) ((currentPlayer / (float) mediaPlayer
								.getDuration()) * 100);

						seekBar.setProgress(progress);
					} else {
						textView_playTime.setText("00:00");
						seekBar.setProgress(0);
					}
				}

				break;

			default:
				break;
			}
		};
	};

	// 自动隐藏自定义播放器控制条的时间
	private static final int HIDDEN_TIME = 5000;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		initView();

		initMediaPlayer();

		initController();

		// 动态注册广播接受者
		receiver = new MyVideoBroadcastReceiver();
		registerReceiver(receiver, new IntentFilter(
				"com.amy.day43_03_SurfaceViewMediaPlayer"));
	}

	private String formatTime(long time) {
		SimpleDateFormat formatter = new SimpleDateFormat("mm:ss");
		return formatter.format(new Date(time));
	}

	private void initController() {

		controllerView = getLayoutInflater().inflate(
				R.layout.popupwindow_mediacontroller, null);

		// 初始化popopWindow
		popupWindow = new PopupWindow(controllerView,
				LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, true);

		imageView_play = (ImageView) controllerView
				.findViewById(R.id.imageView_play);
		imageView_fullscreen = (ImageView) controllerView
				.findViewById(R.id.imageView_fullscreen);

		seekBar = (SeekBar) controllerView.findViewById(R.id.seekbar);

		textView_playTime = (TextView) controllerView
				.findViewById(R.id.textView_playtime);
		textView_duration = (TextView) controllerView
				.findViewById(R.id.textView_totaltime);

		seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

			// 表示手指拖动seekbar完毕,手指离开屏幕会触发以下方法
			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {
				// 让计时器延时执行
				handler.postDelayed(r, HIDDEN_TIME);
			}

			// 在手指正在拖动seekBar,而手指未离开屏幕触发的方法
			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {
				// 让计时器取消计时
				handler.removeCallbacks(r);
			}

			@Override
			public void onProgressChanged(SeekBar seekBar, int progress,
					boolean fromUser) {
				if (fromUser) {
					int playtime = progress * mediaPlayer.getDuration() / 100;
					mediaPlayer.seekTo(playtime);
				}

			}
		});

		// 点击播放的时候,判断是播放还是暂停
		imageView_play.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				if (imageView_main_show.getVisibility() == View.VISIBLE) {
					imageView_main_show.setVisibility(View.GONE);
				}

				if (mediaPlayer.isPlaying()) {
					mediaPlayer.pause();
					imageView_play.setImageResource(R.drawable.video_btn_down);
				} else {
					mediaPlayer.start();
					imageView_play.setImageResource(R.drawable.video_btn_on);

				}

			}
		});

		// 实现全屏和退出全屏(内容物横竖屏,不是屏幕的横竖屏)
		imageView_fullscreen.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
					setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
					imageView_fullscreen
							.setImageResource(R.drawable.video_full_screen);

					// 重新设置surfaceView的高度和宽度
					surfaceView.getLayoutParams().width = LayoutParams.MATCH_PARENT;
					surfaceView.getLayoutParams().height = (int) (260 * densityRatio);
				} else if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
					setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
					imageView_fullscreen
							.setImageResource(R.drawable.video_inner_screen);

					surfaceView.getLayoutParams().width = LayoutParams.MATCH_PARENT;
					surfaceView.getLayoutParams().height = LayoutParams.MATCH_PARENT;
				}

				surfaceView.setLayoutParams(surfaceView.getLayoutParams());
			}
		});
	}

	private void showOrHiddenController() {
		if (popupWindow.isShowing()) {
			popupWindow.dismiss();
		} else {
			// 将dp转换为px
			int controllerHeightPixel = (int) (densityRatio * 50);
			popupWindow.showAsDropDown(surfaceView, 0, -controllerHeightPixel);
			// 延时执行
			handler.postDelayed(r, HIDDEN_TIME);
		}
	}

	private void initMediaPlayer() {
		filePath = Environment.getExternalStoragePublicDirectory(
				Environment.DIRECTORY_DOWNLOADS).getAbsolutePath()
				+ File.separator + "myabc.mp4";

		Log.i("==main==",
				"==========="
						+ Environment.getExternalStoragePublicDirectory(
								Environment.DIRECTORY_DOWNLOADS)
								.getAbsolutePath());
		if (mediaPlayer == null) {
			// 1,创建MediaPlay对象
			mediaPlayer = new MediaPlayer();
			mediaPlayer.reset();
			try {
				mediaPlayer.setDataSource(filePath);
				mediaPlayer.prepare();
				// mediaPlayer.start();
				mediaPlayer.setLooping(false);
			} catch (IllegalStateException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}

		}

		mediaPlayer.setOnPreparedListener(new OnPreparedListener() {

			@Override
			public void onPrepared(MediaPlayer mp) {
				// 表示准备完成,设置总的时长,使用时间格式化工具

				// String duration = mediaPlayer.getDuration() ;
				textView_duration.setText(formatTime(mediaPlayer.getDuration()));
				// 初始化定时器
				timer = new Timer();
				timer.schedule(new TimerTask() {

					@Override
					public void run() {
						handler.sendEmptyMessage(WHAT);
					}
				}, 0, 1000);
			}
		});

		mediaPlayer.setOnErrorListener(new OnErrorListener() {

			@Override
			public boolean onError(MediaPlayer mp, int what, int extra) {
				mp.reset();

				return false;
			}
		});

		mediaPlayer.setOnCompletionListener(new OnCompletionListener() {

			@Override
			public void onCompletion(MediaPlayer mp) {
				// 发送广播,播放下一首歌曲

				Intent intent = new Intent();
				intent.setAction("com.amy.day43_03_SurfaceViewMediaPlayer");
				sendBroadcast(intent);
			}
		});
	}

	private void initView() {
		// TODO Auto-generated method stub
		densityRatio = getResources().getDisplayMetrics().density; // 表示获取真正的密度

		imageView_main_show = (ImageView) findViewById(R.id.imageView_main_play);
		surfaceView = (SurfaceView) findViewById(R.id.surfaceView_main);
		surfaceHolder = surfaceView.getHolder();

		surfaceHolder.addCallback(new Callback() {

			@Override
			public void surfaceDestroyed(SurfaceHolder holder) {
				// TODO Auto-generated method stub
				if (mediaPlayer != null) {
					mediaPlayer.stop();
					mediaPlayer.release();
				}
			}

			@Override
			public void surfaceCreated(SurfaceHolder holder) {
				// TODO Auto-generated method stub
				if (mediaPlayer != null) {

					mediaPlayer.setDisplay(surfaceHolder);
					// mediaPlayer.start() ;

				}

			}

			@Override
			public void surfaceChanged(SurfaceHolder holder, int format,
					int width, int height) {
				// TODO Auto-generated method stub

			}
		});

		// 设置屏幕的触摸监听
		surfaceView.setOnTouchListener(new OnTouchListener() {

			@Override
			public boolean onTouch(View v, MotionEvent event) {
				// 表示在点击的瞬间就显示控制条
				switch (event.getAction()) {
				case MotionEvent.ACTION_DOWN:
					showOrHiddenController();
					break;

				default:
					break;
				}
				return true;
			}
		});
	}

	/**
	 * 设置控件的监听事件
	 *
	 * @param v
	 */
	public void clickButton(View v) {
		switch (v.getId()) {
		case R.id.imageView_main_play:

			imageView_main_show.setVisibility(View.GONE);
			mediaPlayer.start();

			break;

		default:
			break;
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();

		unregisterReceiver(receiver);
		timer.cancel();

		if (mediaPlayer != null) {
			mediaPlayer.release();
			mediaPlayer = null;
		}

		handler.removeCallbacksAndMessages(null);
	}

	class MyVideoBroadcastReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context context, Intent intent) {
			if (intent.getAction().equals(
					"com.amy.day43_03_SurfaceViewMediaPlayer")) {
				AlertDialog.Builder builder = new AlertDialog.Builder(context);
				builder.setIcon(R.drawable.ic_launcher)
						.setTitle("提示")
						.setMessage("视屏播放完毕,是否播放")
						.setNegativeButton("取消", null)
						.setPositiveButton("确定",
								new DialogInterface.OnClickListener() {

									@Override
									public void onClick(DialogInterface dialog,
											int which) {
										mediaPlayer.reset();
										try {
											mediaPlayer.setDataSource(filePath);
											mediaPlayer.prepare();
										} catch (Exception e) {
											// TODO Auto-generated catch block
											e.printStackTrace();
										}
										mediaPlayer.setLooping(false);

										mediaPlayer.start();
									}
								}).show();

			}
		}

	}

}

  

时间: 2024-08-02 02:49:49

Android中使用SurfaceView+MediaPlayer+自定义的MediaController实现自定义的视屏播放器的相关文章

Android中使用SurfaceView和Canvas来绘制动画

其实每个View中都有Canvas可以用来绘制动画,只需要在这个View中重载onDraw()方法就可以,但是SurfaceView类是一个专门用来制动动画的类. Canvas(中文叫做"画布")就和HTML5中的canvas标签一样可以在一定区域内自由绘制图形.Canvas+SurfaceView制作的动画与View Animation和Property Animation这类动画比起来更加适合大量的集中播放的动画,比如游戏画面.相机的图像显示等. 因为SurfaceView通常会在

android 下的一段视屏播放的逻辑

android 下的一段视屏播放的逻辑只是Activity的一部分逻辑源码 DemoActivity: public class DemoActivity extends Activity implements OnClickListener { EditText et_path; Button bt_start; Button bt_pause; Button bt_restart; Button bt_stop; SurfaceView sv; SurfaceHolder holder; M

android中的surfaceview控件

SurfaceView是继承于View类的,有绘制动态图片,播放视频的作用.View负责在主线程中更新动画,而在SurfaceView是在一个新线程中更新动画,并且它是一个2D的画布效果,将自己放置于屏幕的最低层进行图画的显示, SurfaceView的使用方法: 1.当单独建立一个SurfaceView的工程 需要将这个工程继承SurfaceView 并接口于SurfaceHolder的Callback和Runnable  其中SurfaceHolder是用来监听画布,Runnable则是用来

【Android】利用安卓的数据接口、多媒体处理编写内存卡Mp3播放器app

通过调用安卓的MediaPlayer可以直接完成Mp3等主流音频的播放,同时利用ContentResolver与Cursor可以直接读取安卓内在数据库的信息,直接获取当前sdcard中所有音频的列表,无须像<[Android]内存卡图片读取器,图库app>(点击打开链接)一样利用原始的Java代码去遍历整个sdcard卡,直接调用安卓固有的类既便捷又快速.最后,读取出来的Mp3可以通过适配器直接加载到ListView列表,做出如下所示的内存卡Mp3播放器app效果.本app在自己的真实的16G

android中surface,surfaceview,sufaceholder以及surface客户端的关系

这里以照相机camera功能的实现来解释surface,surfaceview,sufaceholder以及surface客户端(本例子中指的是camera)的关系,surface及其client(客户端,本例子中指的是camera)之间的纽带就是surfaceholder,它控制着surface及其client(客户端,本例子中指的是camera)之间的连接or断开,surfaceView座位视图层级结构层面的组件,可以认为是它承载了surface,而surface是实际用来接收图像原始像素绘

Android中Canvas绘图基础详解(附源码下载)

Android中,如果我们想绘制复杂的自定义View或游戏,我们就需要熟悉绘图API.Android通过Canvas类暴露了很多drawXXX方法,我们可以通过这些方法绘制各种各样的图形.Canvas绘图有三个基本要素:Canvas.绘图坐标系以及Paint.Canvas是画布,我们通过Canvas的各种drawXXX方法将图形绘制到Canvas上面,在drawXXX方法中我们需要传入要绘制的图形的坐标形状,还要传入一个画笔Paint.drawXXX方法以及传入其中的坐标决定了要绘制的图形的形状

Android中的soundpool小结

之前只知道android中可以用mediaplayer播放音乐,原来今天才发现 可以用soundpool,用soundpool可以播一些短的反应速度要求高的声音, 比如游戏中的爆破声,而mediaplayer适合播放长点的. 1. SoundPool载入音乐文件使用了独立的线程,不会阻塞UI主线程的操作.但是这里如果音效文件过大没有载入完成,我们调用play方法时可能产生严重的后果,这里Android SDK提供了一个SoundPool.OnLoadCompleteListener类来帮助我们了

Android中Canvas绘图基础详解

原文:http://blog.csdn.net/iispring/article/details/49770651 Android中,如果我们想绘制复杂的自定义View或游戏,我们就需要熟悉绘图API.Android通过Canvas类暴露了很多drawXXX方法,我们可以通过这些方法绘制各种各样的图形.Canvas绘图有三个基本要素:Canvas.绘图坐标系以及Paint.Canvas是画布,我们通过Canvas的各种drawXXX方法将图形绘制到Canvas上面,在drawXXX方法中我们需要

Android中多线程下载列表的封装实现(含进度反馈)

来源:http://blog.csdn.net/u011638883/article/details/17347015 实现了一下Android中的文件多线程下载模块,支持自定义线程数.断点续传.下载任务的删除,添加等功能,这里封装了一下,功能已全部实现.不过由于使用的是最简单的手动线程数组及消息通知实现,可能还存在某些小问题.笔者会在后面的使用过程中再进行优化完善.先看一下程序测试效果,这里指定了5个下载任务,以及2个下载线程,具体如下: 要运行以上Demo需要自己搭建服务器,和简单,只需要把