android之VideoView和视频播放View的扩展

来源:互联网 发布:爱淘宝怎么返利多少 编辑:程序博客网 时间:2024/06/03 14:51

1.概念及扩展

  VideoView 是android 系统提供的一个媒体播放显示和控制的控件。其结构层次如下:

  原型:VideoView extends SurfaceView implements MediaController.MediaPlayerControl

  类结构:

      java.lang.Object
        ↳ android.view.View
          ↳ android.view.SurfaceView
            ↳ android.widget.VideoView

  通过VideoView 的原型可知:如果构建更为复杂和有特色个性的视频View,需要继承SurfaceView 和实现MediaPlayerControl接口其中SurfaceView 为显示提供支持,MediaPlayerControl则为媒体控制提供了支持。

2.案例

1)VideoView案例

(我们没有管理MediaPalyer的各种状态,这些状态都让VideoView给封装了,并且,当VideoView创建的时候,MediaPalyer对象将会创建,当VideoView对象销毁的时候,MediaPlayer对象将会释放。)

布局文件

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent"    android:layout_height="fill_parent"><VideoView android:id="@+id/video_view" android:layout_width="match_parent" android:layout_height="match_parent"    android:layout_centerInParent="true" /></LinearLayout>

主程序:

public class VideoPlayer extends Activity implements MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {    public static final String TAG = "VideoPlayer";    private VideoView mVideoView;    private Uri mUri;    private int mPositionWhenPaused = -1;    private MediaController mMediaController;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        //Set the screen to landscape.        this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);        mVideoView = (VideoView)findViewById(R.id.video_view);        //Video file        mUri = Uri.parse(Environment.getExternalStorageDirectory() + "/1.3gp");        //Create media controller,组件可以控制视频的播放,暂停,回复,seek等操作,不需要你实现        mMediaController = new MediaController(this);        mVideoView.setMediaController(mMediaController);    }    public void onStart() {        // Play Video        mVideoView.setVideoURI(mUri);        mVideoView.start();        super.onStart();    }    public void onPause() {        // Stop video when the activity is pause.        mPositionWhenPaused = mVideoView.getCurrentPosition();        mVideoView.stopPlayback();        super.onPause();    }    public void onResume() {        // Resume video player        if(mPositionWhenPaused >= 0) {            mVideoView.seekTo(mPositionWhenPaused);            mPositionWhenPaused = -1;        }        super.onResume();    }    public boolean onError(MediaPlayer player, int arg1, int arg2) {        return false;    }    public void onCompletion(MediaPlayer mp) {        this.finish();    }}

2)自定义VideoView

和VideoView实现类似,继承了SurfaceView并且实现了MediaPlayerControl。

public class CustomerVideoView extends SurfaceView implements         MediaPlayerControl {     private static String TAG = "customer.videoplayer";     private boolean pause;     private boolean seekBackward;     private boolean seekForward;     private Uri videoUri;     private MediaPlayer mediaPlayer;     private Context context;     private OnPreparedListener onPreparedListener;     private int videoWidth;     private int videoHeight;     private MediaController mediaController;     protected SurfaceHolder surfaceHolder;     private Callback surfaceHolderCallback = new SurfaceHolder.Callback() {         public void surfaceChanged(SurfaceHolder holder, int format, int w,                 int h) {         }         public void surfaceCreated(SurfaceHolder holder) {             surfaceHolder = holder;             if (mediaPlayer != null) {                 mediaPlayer.setDisplay(surfaceHolder);                 resume();             } else {                 openVideo();             }         }         public void surfaceDestroyed(SurfaceHolder holder) {             surfaceHolder = null;             if (mediaController != null) {                 mediaController.hide();             }             release(true);         }     };     private void release(boolean cleartargetstate) {         if (mediaPlayer != null) {             mediaPlayer.reset();             mediaPlayer.release();             mediaPlayer = null;         }     }     public void resume() {         if (surfaceHolder == null) {             return;         }         if (mediaPlayer != null) {             return;         }         openVideo();     }     public CustomerVideoView(Context context, AttributeSet attrs, int defStyle) {         super(context, attrs, defStyle);         this.context = context;         this.initVideoView();     }     public CustomerVideoView(Context context, AttributeSet attrs) {         super(context, attrs);         this.context = context;         this.initVideoView();     }     public CustomerVideoView(Context context) {         super(context);         this.context = context;         this.initVideoView();     }     @Override     public boolean canPause() {         return this.pause;     }     @Override     public boolean canSeekBackward() {         return this.seekBackward;     }     @Override     public boolean canSeekForward() {         return this.seekForward;     }     @Override     public int getBufferPercentage() {         return 0;     }     @Override     public int getCurrentPosition() {         return mediaPlayer!=null?mediaPlayer.getCurrentPosition():0;     }     @Override     public int getDuration() {         return mediaPlayer!=null?mediaPlayer.getDuration():0;     }     @Override     public boolean isPlaying() {         return false;     }     @Override     public void pause() {     }     @Override     public void seekTo(int mSec) {     }     @Override     public void start() {     }     public void setVideoURI(Uri uri) {         this.videoUri = uri;         openVideo();         requestLayout();         invalidate();     }     private void openVideo() {         this.mediaPlayer = new MediaPlayer();         try {             this.mediaPlayer.setDataSource(this.context, this.videoUri);         } catch (Exception e) {             Log.e(TAG, e.getMessage());             throw new RuntimeException(e);         }         this.mediaPlayer.prepareAsync();         this.mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);         this.mediaPlayer.setOnPreparedListener(onPreparedListener);         attachMediaController();     }     private void attachMediaController() {         if (mediaPlayer != null && mediaController != null) {             mediaController.setMediaPlayer(this);             View anchorView = this.getParent() instanceof View ? (View) this                     .getParent() : this;             mediaController.setAnchorView(anchorView);             mediaController.setEnabled(true);         }     }     public void setMediaController(MediaController controller) {         if (mediaController != null) {             mediaController.hide();         }         mediaController = controller;         attachMediaController();     }     public void setOnPreparedListener(OnPreparedListener onPreparedListener) {         this.onPreparedListener = onPreparedListener;     }     @Override     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {         int width = getDefaultSize(videoWidth, widthMeasureSpec);         int height = getDefaultSize(videoHeight, heightMeasureSpec);         if (videoWidth > 0 && videoHeight > 0) {             if (videoWidth * height > width * videoHeight) {                 height = width * videoHeight / videoWidth;             } else if (videoWidth * height < width * videoHeight) {                 width = height * videoWidth / videoHeight;             }         }         Log.i(TAG, "setting size: " + width + ‘x’ + height);         setMeasuredDimension(width, height);     }     private void initVideoView() {         videoWidth = 0;         videoHeight = 0;         getHolder().addCallback(surfaceHolderCallback);         getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);         setFocusable(true);         setFocusableInTouchMode(true);         requestFocus();     } }

  一般情况下,android界面的绘制和更新,要交给主ui线程来操作,通过Handler机制。但是播放视频,需要比较优先和实时的改变和绘制界面。android提供了使用单独线程绘制UI的机制,就是SurfaceView。使用SurfaceView,需要实现SurfaceHolder.Callback接口:

  • surfaceCreated,在Surface(SurfaceView内部包含一个Surface实例)创建后,会立即调用该方法,可在该方法中做绘制界面相关的初始化工作;
  • surfaceChanged,当Surface的状态发生变化,比如大小,会调用该方法,在surfaceCreated方法调用过至少会调用一次该方法;
  • surfaceDestroyed,当销毁Surface的时候调用。

  开发者不能直接操作Surface实例,要通过SurfaceHandler,在SurfaceView中可以通过getHandler方法获取到SurfaceHandler实例。
SurfaceHander有一些类型,用来标识Surface实例界面数据来源,可以通过setType来操作:

  • SURFACE_TYPE_NORMAL:RAM缓存的原生数据
  • SURFACE_TYPE_HARDWARE:通过DMA,direct memory access,就是直接写屏技术获取到的数据,或者其他硬件加速的数据
  • SURFACE_TYPE_GPU:通过GPU加速的数据
  • SURFACE_TYPE_PUSH_BUFFERS:标识数据来源于其他对象,比如照相机,比如视频播放服务器(android内部有视频播放的服务器,所有播放视频相当于客户端)


  CustomerVideoView的构造方法,使用超类的构造方法。都会执行initVideoView()方法用来初始化界面和参数。另外一个主要的内容是openVideo()方法:

  • mediaPlayer.prepareAsync(),用来异步准备播放,另外还有个prepare()方法,是同步的,也就是全部下载完毕才能播放,显然,在播放网上视频的时候需要用前者;
  • 通过attachMediaController()方法,把控制条附加到播放视频的SurfaceView上,这里实现的不完全,因此还不能使用,仅仅是把MediaPlayerControl实例通过setMediaPlayer方法设置一下,供OnPreparedListener用来得到加载成功的回调,另外供外面代码调用得到视频的时长和当前时长。
绿色通道:好文要顶关注我收藏该文与我联系