您的位置:首页 > 其它

Surface+MediaPlayer显示视频

2015-05-05 22:00 302 查看
[code]/**
 * 该实例中使用MediaPlayer完成播放,同时界面使用SurfaceView来实现
 * 
 * 这里我们实现MediaPlayer中很多状态变化时的监听器
 * 
 * 使用Mediaplayer时,也可以使用MediaController类,但是需要实现MediaController.mediaController接口
 * 实现一些控制方法。
 * 
 * 然后,设置controller.setMediaPlayer(),setAnchorView(),setEnabled(),show()就可以了,这里不再实现
 * @author Administrator
 *
 */ 
public class VideoSurfaceDemo extends Activity implements OnCompletionListener,OnErrorListener,OnInfoListener, 
    OnPreparedListener, OnSeekCompleteListener,OnVideoSizeChangedListener,SurfaceHolder.Callback{ 
    private Display currDisplay; 
    private SurfaceView surfaceView; 
    private SurfaceHolder holder; 
    private MediaPlayer player; 
    private int vWidth,vHeight; 
    //private boolean readyToPlay = false; 

    public void onCreate(Bundle savedInstanceState){ 
        super.onCreate(savedInstanceState); 
        this.setContentView(R.layout.video_surface); 

        surfaceView = (SurfaceView)this.findViewById(R.id.video_surface); 
        //给SurfaceView添加CallBack监听 
        holder = surfaceView.getHolder(); 
        holder.addCallback(this); 
        //为了可以播放视频或者使用Camera预览,我们需要指定其Buffer类型 
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 

        //下面开始实例化MediaPlayer对象 
        player = new MediaPlayer(); 
        player.setOnCompletionListener(this); 
        player.setOnErrorListener(this); 
        player.setOnInfoListener(this); 
        player.setOnPreparedListener(this); 
        player.setOnSeekCompleteListener(this); 
        player.setOnVideoSizeChangedListener(this); 
        Log.v("Begin:::", "surfaceDestroyed called"); 
        //然后指定需要播放文件的路径,初始化MediaPlayer 
        String dataPath = Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.m4v"; 
        try { 
            player.setDataSource(dataPath); 
            Log.v("Next:::", "surfaceDestroyed called"); 
        } catch (IllegalArgumentException e) { 
            e.printStackTrace(); 
        } catch (IllegalStateException e) { 
            e.printStackTrace(); 
        } catch (IOException e) { 
            e.printStackTrace(); 
        } 
        //然后,我们取得当前Display对象 
        currDisplay = this.getWindowManager().getDefaultDisplay(); 
    } 

    @Override 
    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { 
        // 当Surface尺寸等参数改变时触发 
        Log.v("Surface Change:::", "surfaceChanged called"); 
    } 
    @Override 
    public void surfaceCreated(SurfaceHolder holder) { 
        // 当SurfaceView中的Surface被创建的时候被调用 
        //在这里我们指定MediaPlayer在当前的Surface中进行播放 
        player.setDisplay(holder); 
        //在指定了MediaPlayer播放的容器后,我们就可以使用prepare或者prepareAsync来准备播放了 
        player.prepareAsync(); 

    } 
    @Override 
    public void surfaceDestroyed(SurfaceHolder holder) { 

        Log.v("Surface Destory:::", "surfaceDestroyed called"); 
    } 
    @Override 
    public void onVideoSizeChanged(MediaPlayer arg0, int arg1, int arg2) { 
        // 当video大小改变时触发 
        //这个方法在设置player的source后至少触发一次 
        Log.v("Video Size Change", "onVideoSizeChanged called"); 

    } 
    @Override 
    public void onSeekComplete(MediaPlayer arg0) { 
        // seek操作完成时触发 
        Log.v("Seek Completion", "onSeekComplete called"); 

    } 
    @Override 
    public void onPrepared(MediaPlayer player) { 
        // 当prepare完成后,该方法触发,在这里我们播放视频 

        //首先取得video的宽和高 
        vWidth = player.getVideoWidth(); 
        vHeight = player.getVideoHeight(); 

        if(vWidth > currDisplay.getWidth() || vHeight > currDisplay.getHeight()){ 
            //如果video的宽或者高超出了当前屏幕的大小,则要进行缩放 
            float wRatio = (float)vWidth/(float)currDisplay.getWidth(); 
            float hRatio = (float)vHeight/(float)currDisplay.getHeight(); 

            //选择大的一个进行缩放 
            float ratio = Math.max(wRatio, hRatio); 

            vWidth = (int)Math.ceil((float)vWidth/ratio); 
            vHeight = (int)Math.ceil((float)vHeight/ratio); 

            //设置surfaceView的布局参数 
            surfaceView.setLayoutParams(new LinearLayout.LayoutParams(vWidth, vHeight)); 

            //然后开始播放视频 

            player.start(); 
        } 
    } 
    @Override 
    public boolean onInfo(MediaPlayer player, int whatInfo, int extra) { 
        // 当一些特定信息出现或者警告时触发 
        switch(whatInfo){ 
        case MediaPlayer.MEDIA_INFO_BAD_INTERLE***ING: 
            break; 
        case MediaPlayer.MEDIA_INFO_METADATA_UPDATE:   
            break; 
        case MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING: 
            break; 
        case MediaPlayer.MEDIA_INFO_NOT_SEEKABLE:  
            break; 
        } 
        return false; 
    } 
    @Override 
    public boolean onError(MediaPlayer player, int whatError, int extra) { 
        Log.v("Play Error:::", "onError called"); 
        switch (whatError) { 
        case MediaPlayer.MEDIA_ERROR_SERVER_DIED: 
            Log.v("Play Error:::", "MEDIA_ERROR_SERVER_DIED"); 
            break; 
        case MediaPlayer.MEDIA_ERROR_UNKNOWN: 
            Log.v("Play Error:::", "MEDIA_ERROR_UNKNOWN"); 
            break; 
        default: 
            break; 
        } 
        return false; 
    } 
    @Override 
    public void onCompletion(MediaPlayer player) { 
        // 当MediaPlayer播放完成后触发 
        Log.v("Play Over:::", "onComletion called"); 
        this.finish(); 
    } 
}


这是MediaPlayer+Surface组合播放视频,VideoView就是基于这个实现的。

SurfaceView负责显示,MediaPlayer负责处理视频数据,当Surface创建出来之后就设置MediaPlayer以SurfaceView作为播放平台,然后MediaPlayer就可以准备去加载资源,因为是异步加载,所以setOnPrepared()来处理当加载完毕之后的事情->MediaPlayer.start();

上次讨论VideoView的时候发现限制多多,例如它会自动调整VideoView的大小来适应按比例调整大小的视频,还有就是MediaController的问题,要解决这些问题因为VideoView的封装的缘故而显得好难解决,但是MediaPlayer+Surface就明显开放得多,例如你指定了XML中Surface的大小,那么播放的时候视频就会自动填满Surface了,第一个问题就解决了。

第二个问题,MediaController就没那么好解决了,之前也提到了解决的想法,就是自己写FrameLayout铺在视频上面,Touch视频的时候就显示出来,平时就隐藏起来,自己写的FrameLayout想要什么按钮就写什么按钮,但是这个FrameLayout也不是那么好写的,不然SDK也不会专门为我们写好MediaController类了,特别是看过源码之后就知道进度条,按钮的状态,缓冲进度,自动消失等都不是那么好写的,也不是说一定写不出,但是至少得花上一定时间吧。

下面提供一个外国的博客来解决这个问题,其实就是他已经帮我们写好了这个FrameLayout,而且代码就写在那里,想怎么改怎么改,这篇博客也是主要探究怎改的

http://www.brightec.co.uk/blog/custom-android-media-controller

先来张效果图:



代码和图标都在stackoverflow里面

http://stackoverflow.com/questions/12482203/how-to-create-custom-ui-for-android-mediacontroller/14323144#14323144

1 - Download VideoControllerView.java into your project then make the following changes:

Change the package name to match your project

Either change the last import line to match your project or delete the line if it’s not necessary

If your project’s minSdkVersion is less than 14, delete the onInitializeAccessibilityEvent and onInitializeAccessibilityNodeInfo methods on lines 561-571.

2 - Download media_controller.xml into your project’s layout folder. This file references a string called description so either create a string with this name (see 2a) or delete the android:contentDescription references in the layout.

2a - To create a description string, open strings.xml and add a line like Media Controls

3 - Download the 4 images into your project’s drawable-xhdpi folder and name them as ic_media_play.png, ic_media_pause.png, ic_media_fullscreen_shrink.png and ic_media_fullscreen_stretch.png

至于怎样使用这个拿来的VideoControllerView

首先就是在Activity设置XML:

[code]<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/video_container" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:gravity="center_horizontal|center_vertical" 
    android:orientation="vertical" > 
    <FrameLayout 
        android:id="@+id/videoSurfaceContainer" 
        android:layout_width="match_parent" 
        android:layout_height="200dp" > 
        <SurfaceView 
            android:id="@+id/videoSurface" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" /> 
    </FrameLayout> 
</LinearLayout>


然后Activity的代码如下:

[code]package com.example.test;

import java.io.File;
import java.io.IOException;

import android.app.Activity;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.FrameLayout;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;

public class VideoPlayerActivity extends Activity implements SurfaceHolder.Callback,
MediaPlayer.OnPreparedListener, VideoControllerView.MediaPlayerControl
{
    SurfaceView videoSurface; 
    MediaPlayer player; 
    VideoControllerView controller; 

    int bufferPercent=0;
    @Override 
    protected void onCreate(Bundle savedInstanceState) 
    { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_video_player); 

        videoSurface = (SurfaceView) findViewById(R.id.videoSurface); 
        SurfaceHolder videoHolder = videoSurface.getHolder(); 
        videoHolder.addCallback(this); 
        player = new MediaPlayer(); 
        player.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener()
        {

            @Override
            public void onBufferingUpdate(MediaPlayer mp, int percent)
            {
                // TODO Auto-generated method stub
                bufferPercent=percent;
            }
        });
        controller = new VideoControllerView(this);

        try { 
            player.setAudioStreamType(AudioManager.STREAM_MUSIC); 
            player.setDataSource(this, Uri.fromFile(new File(MainActivity.path))); 
            player.setOnPreparedListener(this); } 
        catch (IllegalArgumentException e) { e.printStackTrace(); } 
        catch (SecurityException e) { e.printStackTrace(); } 
        catch (IllegalStateException e) { e.printStackTrace(); } 
        catch (IOException e) { e.printStackTrace(); }
    }

    @Override 
    public boolean onTouchEvent(MotionEvent event) 
    { 
        controller.show(); 
        return false; 
    }

    // Implement SurfaceHolder.Callback 
    @Override 
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) 
    { 

    } 
    @Override 
    public void surfaceCreated(SurfaceHolder holder) 
    { 
        player.setDisplay(holder); 
        player.prepareAsync(); 
    } 
    @Override 
    public void surfaceDestroyed(SurfaceHolder holder) 
    { 

    } 
    // End SurfaceHolder.Callback

    // Implement MediaPlayer.OnPreparedListener 
    @Override 
    public void onPrepared(MediaPlayer mp) 
    { 
        controller.setMediaPlayer(this); 
        controller.setAnchorView((FrameLayout) findViewById(R.id.videoSurfaceContainer));
        player.start(); 
    } 
    // End MediaPlayer.OnPreparedListener

    // Implement VideoMediaController.MediaPlayerControl 
    @Override 
    public boolean canPause() { return true; } 
    @Override 
    public boolean canSeekBackward() { return true; } 
    @Override 
    public boolean canSeekForward() { return true; } 
    @Override 
    public int getBufferPercentage() { return bufferPercent; } 
    @Override 
    public int getCurrentPosition() { return player.getCurrentPosition(); } 
    @Override 
    public int getDuration() { return player.getDuration(); } 
    @Override 
    public boolean isPlaying() { return player.isPlaying(); } 
    @Override 
    public void pause() { player.pause(); } 
    @Override 
    public void seekTo(int i) { player.seekTo(i); } 
    @Override 
    public void start() { player.start(); } 
    @Override 
    public boolean isFullScreen() { return false; } 
    @Override 
    public void toggleFullScreen() { } 
    // End VideoMediaController.MediaPlayerControl
}


下面分析下VideoControllerView的代码:

[code]/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0  *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.test;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

import java.lang.ref.WeakReference;
import java.util.Formatter;
import java.util.Locale;

//import your.resource.path;

/**
 * A view containing controls for a MediaPlayer. Typically contains the
 * buttons like "Play/Pause", "Rewind", "Fast Forward" and a progress
 * slider. It takes care of synchronizing the controls with the state
 * of the MediaPlayer.
 * <p>
 * The way to use this class is to instantiate it programatically.
 * The MediaController will create a default set of controls
 * and put them in a window floating above your application. Specifically,
 * the controls will float above the view specified with setAnchorView().
 * The window will disappear if left idle for three seconds and reappear
 * when the user touches the anchor view.
 * <p>
 * Functions like show() and hide() have no effect when MediaController
 * is created in an xml layout.
 *
 * MediaController will hide and
 * show the buttons according to these rules:
 * <ul>
 * <li> The "previous" and "next" buttons are hidden until setPrevNextListeners()
 *   has been called
 * <li> The "previous" and "next" buttons are visible but disabled if
 *   setPrevNextListeners() was called with null listeners
 * <li> The "rewind" and "fastforward" buttons are shown unless requested
 *   otherwise by using the MediaController(Context, boolean) constructor
 *   with the boolean set to false
 * </ul>
 */
public class VideoControllerView extends FrameLayout {
    private static final String TAG = "VideoControllerView";

    private MediaPlayerControl  mPlayer;
    private Context             mContext;
    private ViewGroup           mAnchor;
    private View                mRoot;
    private ProgressBar         mProgress;
    private TextView            mEndTime, mCurrentTime;
    private boolean             mShowing;
    private boolean             mDragging;
    private static final int    sDefaultTimeout = 3000;
    private static final int    FADE_OUT = 1;
    private static final int    SHOW_PROGRESS = 2;
    private boolean             mUseFastForward;
    private boolean             mFromXml;
    private boolean             mListenersSet;
    private View.OnClickListener mNextListener, mPrevListener;
    StringBuilder               mFormatBuilder;
    Formatter                   mFormatter;
    private ImageButton         mPauseButton;
    private ImageButton         mFfwdButton;
    private ImageButton         mRewButton;
    private ImageButton         mNextButton;
    private ImageButton         mPrevButton;
    private ImageButton         mFullscreenButton;
    private Handler             mHandler = new MessageHandler(this);

    public VideoControllerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mRoot = null;
        mContext = context;
        mUseFastForward = true;
        mFromXml = true;

        Log.i(TAG, TAG);
    }

    public VideoControllerView(Context context, boolean useFastForward) {
        super(context);
        mContext = context;
        mUseFastForward = useFastForward;

        Log.i(TAG, TAG);
    }

    public VideoControllerView(Context context) {
        this(context, true);

        Log.i(TAG, TAG);
    }

    @Override
    public void onFinishInflate() {
        if (mRoot != null)
            initControllerView(mRoot);
    }

    public void setMediaPlayer(MediaPlayerControl player) {
        mPlayer = player;
        updatePausePlay();
        updateFullScreen();
    }

    /**
     * Set the view that acts as the anchor for the control view.
     * This can for example be a VideoView, or your Activity's main view.
     * @param view The view to which to anchor the controller when it is visible.
     */
    public void setAnchorView(ViewGroup view) {
        mAnchor = view;

        FrameLayout.LayoutParams frameParams = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
        );

        removeAllViews();
        View v = makeControllerView();
        addView(v, frameParams);
    }

    /**
     * Create the view that holds the widgets that control playback.
     * Derived classes can override this to create their own.
     * @return The controller view.
     * @hide This doesn't work as advertised
     */
    protected View makeControllerView() {
        LayoutInflater inflate = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //mRoot = inflate.inflate(R.layout.media_controller, null);
        mRoot = inflate.inflate(R.layout.my_media_controller, null);
 //这里可以变相加载自己的XML作为makeControllerView的返回值
        initControllerView(mRoot);

        return mRoot;
    }

    private void initControllerView(View v) {
        mPauseButton = (ImageButton) v.findViewById(R.id.pause);
        if (mPauseButton != null) {
            mPauseButton.requestFocus();
            mPauseButton.setOnClickListener(mPauseListener);
        }

        mFullscreenButton = (ImageButton) v.findViewById(R.id.fullscreen);
        if (mFullscreenButton != null) {
            mFullscreenButton.requestFocus();
            mFullscreenButton.setOnClickListener(mFullscreenListener);
        }

        mFfwdButton = (ImageButton) v.findViewById(R.id.ffwd);
        if (mFfwdButton != null) {
            mFfwdButton.setOnClickListener(mFfwdListener);
            if (!mFromXml) {
                mFfwdButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE);
            }
        }

        mRewButton = (ImageButton) v.findViewById(R.id.rew);
        if (mRewButton != null) {
            mRewButton.setOnClickListener(mRewListener);
            if (!mFromXml) {
                mRewButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE);
            }
        }

        // By default these are hidden. They will be enabled when setPrevNextListeners() is called
        mNextButton = (ImageButton) v.findViewById(R.id.next);
        if (mNextButton != null && !mFromXml && !mListenersSet) {
            mNextButton.setVisibility(View.GONE);
        }
        mPrevButton = (ImageButton) v.findViewById(R.id.prev);
        if (mPrevButton != null && !mFromXml && !mListenersSet) {
            mPrevButton.setVisibility(View.GONE);
        }

        mProgress = (ProgressBar) v.findViewById(R.id.mediacontroller_progress);
        if (mProgress != null) {
            if (mProgress instanceof SeekBar) {
                SeekBar seeker = (SeekBar) mProgress;
                seeker.setOnSeekBarChangeListener(mSeekListener);
            }
            mProgress.setMax(1000);
        }

        mEndTime = (TextView) v.findViewById(R.id.time);
        mCurrentTime = (TextView) v.findViewById(R.id.time_current);
        mFormatBuilder = new StringBuilder();
        mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());

        installPrevNextListeners();
    }

    /**
     * Show the controller on screen. It will go away
     * automatically after 3 seconds of inactivity.
     */
    public void show() {
        show(sDefaultTimeout);
    }

    /**
     * Disable pause or seek buttons if the stream cannot be paused or seeked.
     * This requires the control interface to be a MediaPlayerControlExt
     */
    private void disableUnsupportedButtons() {
        if (mPlayer == null) {
            return;
        }

        try {
            if (mPauseButton != null && !mPlayer.canPause()) {
                mPauseButton.setEnabled(false);
            }
            if (mRewButton != null && !mPlayer.canSeekBackward()) {
                mRewButton.setEnabled(false);
            }
            if (mFfwdButton != null && !mPlayer.canSeekForward()) {
                mFfwdButton.setEnabled(false);
            }
        } catch (IncompatibleClassChangeError ex) {
            // We were given an old version of the interface, that doesn't have
            // the canPause/canSeekXYZ methods. This is OK, it just means we
            // assume the media can be paused and seeked, and so we don't disable
            // the buttons.
        }
    }

    /**
     * Show the controller on screen. It will go away
     * automatically after 'timeout' milliseconds of inactivity.
     * @param timeout The timeout in milliseconds. Use 0 to show
     * the controller until hide() is called.
     */
    public void show(int timeout) {
        if (!mShowing && mAnchor != null) {
            setProgress();
            if (mPauseButton != null) {
                mPauseButton.requestFocus();
            }
            disableUnsupportedButtons();

            FrameLayout.LayoutParams tlp = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                //ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.MATCH_PARENT,
                Gravity.BOTTOM
            );
           //因为我是上下2条横条,所以要修改这个高度的参数,令其占满FrameLayout
            mAnchor.addView(this, tlp);
            mShowing = true;
        }
        updatePausePlay();
        updateFullScreen();

        // cause the progress bar to be updated even if mShowing
        // was already true.  This happens, for example, if we're
        // paused with the progress bar showing the user hits play.
        mHandler.sendEmptyMessage(SHOW_PROGRESS);

        Message msg = mHandler.obtainMessage(FADE_OUT);
        if (timeout != 0) {
            mHandler.removeMessages(FADE_OUT);
            mHandler.sendMessageDelayed(msg, timeout);
        }
    }

    public boolean isShowing() {
        return mShowing;
    }

    /**
     * Remove the controller from the screen.
     */
    public void hide() {
        if (mAnchor == null) {
            return;
        }

        try {
            mAnchor.removeView(this);
            mHandler.removeMessages(SHOW_PROGRESS);
        } catch (IllegalArgumentException ex) {
            Log.w("MediaController", "already removed");
        }
        mShowing = false;
    }

    private String stringForTime(int timeMs) {
        int totalSeconds = timeMs / 1000;

        int seconds = totalSeconds % 60;
        int minutes = (totalSeconds / 60) % 60;
        int hours   = totalSeconds / 3600;

        mFormatBuilder.setLength(0);
        if (hours > 0) {
            return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
        } else {
            return mFormatter.format("%02d:%02d", minutes, seconds).toString();
        }
    }

    private int setProgress() {
        if (mPlayer == null || mDragging) {
            return 0;
        }

        int position = mPlayer.getCurrentPosition();
        int duration = mPlayer.getDuration();
        if (mProgress != null) {
            if (duration > 0) {
                // use long to avoid overflow
                long pos = 1000L * position / duration;
                mProgress.setProgress( (int) pos);
            }
            int percent = mPlayer.getBufferPercentage();
            Log.e("Lin", ""+percent);
            mProgress.setSecondaryProgress(percent * 10);
        }

        if (mEndTime != null)
            mEndTime.setText(stringForTime(duration));
        if (mCurrentTime != null)
            mCurrentTime.setText(stringForTime(position));

        return position;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //show(sDefaultTimeout);
        hide();
        return true;
    }

    @Override
    public boolean onTrackballEvent(MotionEvent ev) {
        show(sDefaultTimeout);
        return false;
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (mPlayer == null) {
            return true;
        }

        int keyCode = event.getKeyCode();
        final boolean uniqueDown = event.getRepeatCount() == 0
                && event.getAction() == KeyEvent.ACTION_DOWN;
        if (keyCode ==  KeyEvent.KEYCODE_HEADSETHOOK
                || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
                || keyCode == KeyEvent.KEYCODE_SPACE) {
            if (uniqueDown) {
                doPauseResume();
                show(sDefaultTimeout);
                if (mPauseButton != null) {
                    mPauseButton.requestFocus();
                }
            }
            return true;
        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
            if (uniqueDown && !mPlayer.isPlaying()) {
                mPlayer.start();
                updatePausePlay();
                show(sDefaultTimeout);
            }
            return true;
        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
                || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
            if (uniqueDown && mPlayer.isPlaying()) {
                mPlayer.pause();
                updatePausePlay();
                show(sDefaultTimeout);
            }
            return true;
        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
                || keyCode == KeyEvent.KEYCODE_VOLUME_UP
                || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
            // don't show the controls for volume adjustment
            return super.dispatchKeyEvent(event);
        } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {
            if (uniqueDown) {
                hide();
            }
            return true;
        }

        show(sDefaultTimeout);
        return super.dispatchKeyEvent(event);
    }

    private View.OnClickListener mPauseListener = new View.OnClickListener() {
        public void onClick(View v) {
            doPauseResume();
            show(sDefaultTimeout);
        }
    };

    private View.OnClickListener mFullscreenListener = new View.OnClickListener() {
        public void onClick(View v) {
            doToggleFullscreen();
            show(sDefaultTimeout);
        }
    };

    public void updatePausePlay() {
        if (mRoot == null || mPauseButton == null || mPlayer == null) {
            return;
        }

        if (mPlayer.isPlaying()) {
            mPauseButton.setImageResource(R.drawable.ic_media_pause);
        } else {
            mPauseButton.setImageResource(R.drawable.ic_media_play);
        }
    }

    public void updateFullScreen() {
        if (mRoot == null || mFullscreenButton == null || mPlayer == null) {
            return;
        }

        if (mPlayer.isFullScreen()) {
            mFullscreenButton.setImageResource(R.drawable.ic_media_fullscreen_shrink);
        }
        else {
            mFullscreenButton.setImageResource(R.drawable.ic_media_fullscreen_stretch);
        }
    }

    private void doPauseResume() {
        if (mPlayer == null) {
            return;
        }

        if (mPlayer.isPlaying()) {
            mPlayer.pause();
        } else {
            mPlayer.start();
        }
        updatePausePlay();
    }

    private void doToggleFullscreen() {
        if (mPlayer == null) {
            return;
        }

        mPlayer.toggleFullScreen();
    }

    // There are two scenarios that can trigger the seekbar listener to trigger:
    //
    // The first is the user using the touchpad to adjust the posititon of the
    // seekbar's thumb. In this case onStartTrackingTouch is called followed by
    // a number of onProgressChanged notifications, concluded by onStopTrackingTouch.
    // We're setting the field "mDragging" to true for the duration of the dragging
    // session to avoid jumps in the position in case of ongoing playback.
    //
    // The second scenario involves the user operating the scroll ball, in this
    // case there WON'T BE onStartTrackingTouch/onStopTrackingTouch notifications,
    // we will simply apply the updated position without suspending regular updates.
    private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
        public void onStartTrackingTouch(SeekBar bar) {
            show(3600000);

            mDragging = true;

            // By removing these pending progress messages we make sure
            // that a) we won't update the progress while the user adjusts
            // the seekbar and b) once the user is done dragging the thumb
            // we will post one of these messages to the queue again and
            // this ensures that there will be exactly one message queued up.
            mHandler.removeMessages(SHOW_PROGRESS);
        }

        public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {
            if (mPlayer == null) {
                return;
            }

            if (!fromuser) {
                // We're not interested in programmatically generated changes to
                // the progress bar's position.
                return;
            }

            long duration = mPlayer.getDuration();
            long newposition = (duration * progress) / 1000L;
            mPlayer.seekTo( (int) newposition);
            if (mCurrentTime != null)
                mCurrentTime.setText(stringForTime( (int) newposition));
        }

        public void onStopTrackingTouch(SeekBar bar) {
            mDragging = false;
            setProgress();
            updatePausePlay();
            show(sDefaultTimeout);

            // Ensure that progress is properly updated in the future,
            // the call to show() does not guarantee this because it is a
            // no-op if we are already showing.
            mHandler.sendEmptyMessage(SHOW_PROGRESS);
        }
    };

    @Override
    public void setEnabled(boolean enabled) {
        if (mPauseButton != null) {
            mPauseButton.setEnabled(enabled);
        }
        if (mFfwdButton != null) {
            mFfwdButton.setEnabled(enabled);
        }
        if (mRewButton != null) {
            mRewButton.setEnabled(enabled);
        }
        if (mNextButton != null) {
            mNextButton.setEnabled(enabled && mNextListener != null);
        }
        if (mPrevButton != null) {
            mPrevButton.setEnabled(enabled && mPrevListener != null);
        }
        if (mProgress != null) {
            mProgress.setEnabled(enabled);
        }
        disableUnsupportedButtons();
        super.setEnabled(enabled);
    }

    @Override
    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
        super.onInitializeAccessibilityEvent(event);
        event.setClassName(VideoControllerView.class.getName());
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
        super.onInitializeAccessibilityNodeInfo(info);
        info.setClassName(VideoControllerView.class.getName());
    }

    private View.OnClickListener mRewListener = new View.OnClickListener() {
        public void onClick(View v) {
            if (mPlayer == null) {
                return;
            }

            int pos = mPlayer.getCurrentPosition();
            pos -= 5000; // milliseconds
            mPlayer.seekTo(pos);
            setProgress();

            show(sDefaultTimeout);
        }
    };

    private View.OnClickListener mFfwdListener = new View.OnClickListener() {
        public void onClick(View v) {
            if (mPlayer == null) {
                return;
            }

            int pos = mPlayer.getCurrentPosition();
            pos += 15000; // milliseconds
            mPlayer.seekTo(pos);
            setProgress();

            show(sDefaultTimeout);
        }
    };

    private void installPrevNextListeners() {
        if (mNextButton != null) {
            mNextButton.setOnClickListener(mNextListener);
            mNextButton.setEnabled(mNextListener != null);
        }

        if (mPrevButton != null) {
            mPrevButton.setOnClickListener(mPrevListener);
            mPrevButton.setEnabled(mPrevListener != null);
        }
    }

    public void setPrevNextListeners(View.OnClickListener next, View.OnClickListener prev) {
        mNextListener = next;
        mPrevListener = prev;
        mListenersSet = true;

        if (mRoot != null) {
            installPrevNextListeners();

            if (mNextButton != null && !mFromXml) {
                mNextButton.setVisibility(View.VISIBLE);
            }
            if (mPrevButton != null && !mFromXml) {
                mPrevButton.setVisibility(View.VISIBLE);
            }
        }
    }

    public interface MediaPlayerControl {
        void    start();
        void    pause();
        int     getDuration();
        int     getCurrentPosition();
        void    seekTo(int pos);
        boolean isPlaying();
        int     getBufferPercentage();
        boolean canPause();
        boolean canSeekBackward();
        boolean canSeekForward();
        boolean isFullScreen();
        void    toggleFullScreen();
    }

    private static class MessageHandler extends Handler {
        private final WeakReference<VideoControllerView> mView;

        MessageHandler(VideoControllerView view) {
            mView = new WeakReference<VideoControllerView>(view);
        }
        @Override
        public void handleMessage(Message msg) {
            VideoControllerView view = mView.get();
            if (view == null || view.mPlayer == null) {
                return;
            }

            int pos;
            switch (msg.what) {
                case FADE_OUT:
                    view.hide();
                    break;
                case SHOW_PROGRESS:
                    pos = view.setProgress();
                    if (!view.mDragging && view.mShowing && view.mPlayer.isPlaying()) {
                        msg = obtainMessage(SHOW_PROGRESS);
                        sendMessageDelayed(msg, 1000 - (pos % 1000));
                    }
                    break;
            }
        }
    }
}


至于我的2条横条布局文件:

[code]<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <LinearLayout 
        android:id="@+id/title_line"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#3f000000"
        android:orientation="horizontal">
        <TextView 
            android:text="Title"
            android:textSize="14sp"
            android:textColor="@android:color/white"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/control_line"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:background="#3f000000"
        android:gravity="center_vertical"
        android:orientation="horizontal" >

        <ImageButton
            android:id="@+id/pause"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent"
            android:src="@drawable/ic_media_play" />

        <SeekBar
            android:id="@+id/mediacontroller_progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:maxHeight="4dp"
            android:minHeight="4dp"
            android:layout_weight="1"
            android:progressDrawable="@drawable/playback_seekbar2"
            android:thumb="@drawable/playback_seekbar2_thumb" />

        <TextView
            android:id="@+id/time_current"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:paddingLeft="4dip"
            android:paddingRight="4dip"
            android:paddingTop="4dip"
            android:textSize="14sp"
            android:textStyle="bold" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="/"
            android:textSize="14sp"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:paddingLeft="4dip"
            android:paddingRight="4dip"
            android:paddingTop="4dip"
            android:textSize="14sp"
            android:textStyle="bold" />

        <ImageButton
            android:id="@+id/fullscreen"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent"
            android:src="@drawable/ic_media_fullscreen_stretch" />
    </LinearLayout>

</RelativeLayout>


一个相对布局,上面一条LinearLayout,下面一条LinearLayout,整个RelativeLayout就当然要占满我们的AnchorView–包裹着Surface的FrameLayout,然后那2个横条才会相对位于上下位置,而不是黏在一起,如果是一个横条倒不用修改那个位置。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: