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

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用来得到 加载成功的回调,另外供外面代码调用得到视频的时长和当前时长。

时间: 2024-09-10 10:52:13

android之VideoView和视频播放View的扩展的相关文章

Android通过VideoView实现视频播放

在Android系统中,是通过MediaPalyer类播放媒体文件的(包括视频和音频).虽然这个类已经比较简单了,但是还需要控制各种状态,对于视频还需要设置输出窗口,还是需要仔细研究的.为了避免这些麻烦事儿,Android框架提供了VideoView类来封装MediaPalyer,这个VideoView类非常好用.Android自带的程序Gallery也是用VideoView实现的.本文以实例介绍怎样用VideoView来实现VideoPlayer,本文也参考了Android自带程序Galler

Android下VideoView的研究

    VideoView继承自SurfaceView,实现了MediaController.MediaPlayerControl的接口.在android系统中的包名为android.widget.VideoView.     VideoView的主要功能是显示一个指定的视频文件.VideoView可以从多个地方载入图片(比如资源或者content provider中).     VideoView继承了Android.view.View类中的XML属性.常量.和field(我们常翻译成属性).

Android 通过onDraw实现在View中绘图操作的示例

以下是对Android通过onDraw实现在View中绘图操作的示例代码进行了详细的分析介绍,需要的朋友可以过来参考下   Android绘图操作,通过继承View实现,在onDraw函数中实现绘图.下面是一个简单的例子: 复制代码 代码如下: public class AndroidTest extends Activity {     /** Called when the activity is first created. */     @Override     public void

ontochevent事件冲突-Android 自定义布局和自定义view的onTochEvent时间冲突

问题描述 Android 自定义布局和自定义view的onTochEvent时间冲突 我自定义了一个布局和一个view,自定义view被布局引用.view和布局都重写了他的onTochEvent方法,然后发现布局的onTochEvent方法无法被执行被view的onTochEvent 拦截了,请问怎么解决. 解决方案 使自定义view来拦截touch事件即可 onInterceptTouchEvent 解决方案二: 刚好最近写了一篇关于View事件冲突的文章,你看看http://blog.csd

《Android App开发入门:使用Android Studio 2.X开发环境》——第 3章 Android App界面设计 3-1 View 与 ViewGroup(Layout):组件与布局

第 3章 Android App界面设计 3-1 View与 ViewGroup(Layout):组件与布局 3-2 使用 LinearLayout建立界面布局 3-3 使用 weight属性控制组件的宽 /高 3-4 通过属性美化外观 3-5 用程序设置组件的外观属性 3-6 使用 ConstraintLayout 提升设计与执行的性能 3-7 使用 Gmail 将程序寄 3-1 View 与 ViewGroup(Layout):组件与布局

android 关于VideoView倍数播放的问题

问题描述 android 关于VideoView倍数播放的问题 VideoView播放视频能实现倍数播放吗? 好纠结啊!谁知道的 麻烦告知 解决方案 http://blog.csdn.net/sno_guo/article/details/7769887

Android使用VideoView播放网络视频

Android支持播放网络上的视频.在播放网络上的视频时,牵涉到视频流的传输,往往有两种协议,一种是HTTP,一种是RTSP.这 两种协议最大的不同是,HTTP协议,不支持实时流媒体的播放,而RTSP协议就支持. Android中自带的播放器,以及VideoView等都支持上述两种协议,因此,可以直接播放网络上的视频,唯一不同的就是URI. 代码如下: Java代码  package demo.camera;   import android.app.Activity;   import and

android开发-Android 如何实现在线视频播放器并缓存本地,下次没有联网的情况,可离线播放缓存

问题描述 Android 如何实现在线视频播放器并缓存本地,下次没有联网的情况,可离线播放缓存 Android 如何实现在线视频播放器并缓存本地,下次没有联网的情况,可离线播放缓存 解决方案 android中如何实现离线缓存android中如何实现离线缓存android中如何实现离线缓存 解决方案二: http://www.cnblogs.com/doorsky/p/3218043.html 你可以参考一下

Android中我继承了View,为什么访问不了View中的protected属性,例如Scrollx什么的~

问题描述 Android中我继承了View,为什么访问不了View中的protected属性,例如Scrollx什么的~ 问题补充:renpeng301 写道 解决方案 BubbleTextView.Java 这个文件出错么?调试launcher的文章[url]http://gqdy365.iteye.com/blog/763543[/url]解决方案二:引用主要我是从code.google上下的系统Launcher的源代码~那我怎么才能把它弄好~~ 用Scrollx的get方法.解决方案三:查