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用来得到 加载成功的回调,另外供外面代码调用得到视频的时长和当前时长。
相关文章
|
5月前
|
Android开发 UED 计算机视觉
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
本文介绍了一款受游戏“金铲铲之战”启发的Android自定义View——线条等待动画的实现过程。通过将布局分为10份,利用`onSizeChanged`测量最小长度,并借助画笔绘制动态线条,实现渐变伸缩效果。动画逻辑通过四个变量控制线条的增长与回退,最终形成流畅的等待动画。代码中详细展示了画笔初始化、线条绘制及动画更新的核心步骤,并提供完整源码供参考。此动画适用于加载场景,提升用户体验。
449 5
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
|
5月前
|
Android开发 开发者
Android设置View是否可用
在Android开发中,有时需要将布局设置为不可点击状态(失去焦点)。常见的解决方法是使用`setOnClickListener(null)`,但本文介绍一种更通用的方式:通过封装`setViewEnabled`方法实现。该方法可递归设置View及其子View的启用状态,支持传入目标View和布尔值(`true`为可用,`false`为禁用)。例如,调用`setViewEnabled(edittext, false)`即可禁用EditText。文章附有源码及示例动图,帮助开发者快速理解与应用。
107 1
|
5月前
|
Android开发
Android自定义view之利用PathEffect实现动态效果
本文介绍如何在Android自定义View中利用`PathEffect`实现动态效果。通过改变偏移量,结合`PathEffect`的子类(如`CornerPathEffect`、`DashPathEffect`、`PathDashPathEffect`等)实现路径绘制的动态变化。文章详细解析了各子类的功能与参数,并通过案例代码展示了如何使用`ComposePathEffect`组合效果,以及通过修改偏移量实现动画。最终效果为一个菱形图案沿路径运动,源码附于文末供参考。
|
5月前
|
Android开发 开发者
Android自定义view之利用drawArc方法实现动态效果
本文介绍了如何通过Android自定义View实现动态效果,重点使用`drawArc`方法完成圆弧动画。首先通过`onSizeChanged`进行测量,初始化画笔属性,设置圆弧相关参数。核心思路是不断改变圆弧扫过角度`sweepAngle`,并调用`invalidate()`刷新View以实现动态旋转效果。最后附上完整代码与效果图,帮助开发者快速理解并实践这一动画实现方式。
140 0
|
5月前
|
Android开发 数据安全/隐私保护 开发者
Android自定义view之模仿登录界面文本输入框(华为云APP)
本文介绍了一款自定义输入框的实现,包含静态效果、hint值浮动动画及功能扩展。通过组合多个控件完成界面布局,使用TranslateAnimation与AlphaAnimation实现hint文字上下浮动效果,支持密码加密解密显示、去除键盘回车空格输入、光标定位等功能。代码基于Android平台,提供完整源码与attrs配置,方便复用与定制。希望对开发者有所帮助。
|
5月前
|
XML Java Android开发
Android自定义view之网易云推荐歌单界面
本文详细介绍了如何通过自定义View实现网易云音乐推荐歌单界面的效果。首先,作者自定义了一个圆角图片控件`MellowImageView`,用于绘制圆角矩形图片。接着,通过将布局放入`HorizontalScrollView`中,实现了左右滑动功能,并使用`ViewFlipper`添加图片切换动画效果。文章提供了完整的代码示例,包括XML布局、动画文件和Java代码,最终展示了实现效果。此教程适合想了解自定义View和动画效果的开发者。
225 65
Android自定义view之网易云推荐歌单界面
|
5月前
|
XML 前端开发 Android开发
一篇文章带你走近Android自定义view
这是一篇关于Android自定义View的全面教程,涵盖从基础到进阶的知识点。文章首先讲解了自定义View的必要性及简单实现(如通过三个构造函数解决焦点问题),接着深入探讨Canvas绘图、自定义属性设置、动画实现等内容。还提供了具体案例,如跑马灯、折线图、太极图等。此外,文章详细解析了View绘制流程(measure、layout、draw)和事件分发机制。最后延伸至SurfaceView、GLSurfaceView、SVG动画等高级主题,并附带GitHub案例供实践。适合希望深入理解Android自定义View的开发者学习参考。
552 84
|
5月前
|
Android开发 开发者
Android自定义View之不得不知道的文件attrs.xml(自定义属性)
本文详细介绍了如何通过自定义 `attrs.xml` 文件实现 Android 自定义 View 的属性配置。以一个包含 TextView 和 ImageView 的 DemoView 为例,讲解了如何使用自定义属性动态改变文字内容和控制图片显示隐藏。同时,通过设置布尔值和点击事件,实现了图片状态的切换功能。代码中展示了如何在构造函数中解析自定义属性,并通过方法 `setSetting0n` 和 `setbackeguang` 实现功能逻辑的优化与封装。此示例帮助开发者更好地理解自定义 View 的开发流程与 attrs.xml 的实际应用。
Android自定义View之不得不知道的文件attrs.xml(自定义属性)
|
5月前
|
前端开发 Android开发 UED
讲讲Android为自定义view提供的SurfaceView
本文详细介绍了Android中自定义View时使用SurfaceView的必要性和实现方式。首先分析了在复杂绘制逻辑和高频界面更新场景下,传统View可能引发卡顿的问题,进而引出SurfaceView作为解决方案。文章通过Android官方Demo展示了SurfaceView的基本用法,包括实现`SurfaceHolder.Callback2`接口、与Activity生命周期绑定、子线程中使用`lockCanvas()`和`unlockCanvasAndPost()`方法完成绘图操作。
114 3
|
5月前
|
Android开发 开发者
Android自定义view之围棋动画(化繁为简)
本文介绍了Android自定义View的动画实现,通过两个案例拓展动态效果。第一个案例基于`drawArc`方法实现单次动画,借助布尔值控制动画流程。第二个案例以围棋动画为例,从简单的小球直线运动到双向变速运动,最终实现循环动画效果。代码结构清晰,逻辑简明,展示了如何化繁为简实现复杂动画,帮助读者拓展动态效果设计思路。文末提供完整源码,适合初学者和进阶开发者学习参考。
Android自定义view之围棋动画(化繁为简)

热门文章

最新文章