横竖屏切换SurfaceView 大小的调整

视频播放的实现大概有以下形式:

1.使用系统自带视频播放类VideoView

2.使用MediaPlayer+surfaceView

3.使用一些第三方框架如:vitamio 还有像新浪在github上开放的视频播放框架等...

使用场景:

第一种方法:简单,但是VideoView不支持自定义视频,也就是你只能使用系统给你提供的布局,这在很大时候是不符合我们项目需求的。

第二种方法:使用MediaPlayer+surfaceView的话支持我们自定义布局,使用起来稍微复杂,这种方法存在的一个缺点就是只是支持MP4 3gp这样的视频编码格式,其他格式的视频均不能播放。

第三种方法:使用第三方框架,不用多说,照着SDK来就行了,相对也简单,同时支持更多的播放格式,再是目前这样的框架好像收费的居多。


SurfaceView在横竖屏切换情况下应如何调整大小

首先先看效果图:

横竖屏切换SurfaceView 大小的调整_第1张图片    横竖屏切换SurfaceView 大小的调整_第2张图片


布局如下:




    

        
    

    

很简单,上面一个SurfaceView 下面一个Button 用于横竖屏切换。

在ManiFest.xml 中设置:

 
            
                

                
            
        

设置了一个ScreenOrientation 属性为纵向,同时设置了configChanges 属性,为了防止横竖屏切换导致Activity重新创建。

Main.java 代码:

public class MainActivity1 extends AppCompatActivity
        implements SurfaceHolder.Callback
        , MediaPlayer.OnPreparedListener {

    public static final float SHOW_SCALE = 16 * 1.0f / 9;

    private RelativeLayout mSurfaceLayout;
    private SurfaceView mSurfaceView;
    private SurfaceHolder mHolder;
    private MediaPlayer mMediaPlayer;

    //屏幕宽度
    private int mScreenWidth;
    //屏幕高度
    private int mScreenHeight;
    //记录现在的播放位置
    private int mCurrentPos;
    private boolean isLand;

    private DisplayMetrics displayMetrics;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main1);

        displayMetrics = new DisplayMetrics();
        this.getWindow().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        mScreenWidth = displayMetrics.widthPixels;
        mScreenHeight = displayMetrics.heightPixels;

        //后台异常终止,应当恢复原有位置播放
        if (savedInstanceState != null)
            mCurrentPos = savedInstanceState.getInt("currentPos", 0);

        initView();
    }

    private void initView() {

        mSurfaceLayout = (RelativeLayout) findViewById(R.id.layout_gesture);
        RelativeLayout.LayoutParams lp =
                (RelativeLayout.LayoutParams) mSurfaceLayout.getLayoutParams();
        lp.height = (int) (mScreenWidth * SHOW_SCALE);
        mSurfaceLayout.setLayoutParams(lp);

        mSurfaceView = (SurfaceView) findViewById(R.id.sv);
        mHolder = mSurfaceView.getHolder();
        mHolder.addCallback(this);
    }

    private void resetSize() {

        float areaWH = 0.0f;
        int height;

        if (!isLand) {
            // 竖屏16:9
            height = (int) (mScreenWidth / SHOW_SCALE);
            areaWH = SHOW_SCALE;
        } else {
            //横屏按照手机屏幕宽高计算比例
            height = mScreenHeight;
            areaWH = mScreenWidth / mScreenHeight;
        }

        RelativeLayout.LayoutParams layoutParams =
                new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height);
        mSurfaceLayout.setLayoutParams(layoutParams);

        int mediaWidth = mMediaPlayer.getVideoWidth();
        int mediaHeight = mMediaPlayer.getVideoHeight();


        float mediaWH = mediaWidth * 1.0f / mediaHeight;

        RelativeLayout.LayoutParams layoutParamsSV = null;


        if (areaWH > mediaWH) {
            //直接放会矮胖
            int svWidth = (int) (height * mediaWH);
            layoutParamsSV = new RelativeLayout.LayoutParams(svWidth, height);
            layoutParamsSV.addRule(RelativeLayout.CENTER_IN_PARENT);
            mSurfaceView.setLayoutParams(layoutParamsSV);
        }

        if (areaWH < mediaWH) {
            //直接放会瘦高。
            int svHeight = (int) (mScreenWidth / mediaWH);
            layoutParamsSV = new RelativeLayout.LayoutParams(mScreenWidth, svHeight);
            layoutParamsSV.addRule(RelativeLayout.CENTER_IN_PARENT);
            mSurfaceView.setLayoutParams(layoutParamsSV);
        }

    }

    private void initMediaPlayer() {

        if (mMediaPlayer != null) {//从Home 返回
            mMediaPlayer.setDisplay(mHolder);
            mMediaPlayer.start();
        } else {

            mMediaPlayer = new MediaPlayer();  //销毁返回重新初始化

            try {
                mMediaPlayer.setDataSource(this, Uri.parse(MediaPath.MEDIA_HENG));
                mMediaPlayer.setLooping(true);
                mMediaPlayer.setDisplay(mHolder);
                mMediaPlayer.setOnPreparedListener(this);
                mMediaPlayer.prepareAsync();
                mMediaPlayer.setScreenOnWhilePlaying(true);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    //只有在点击Home键或者程序发生异常时才会执行此方法
    @Override
    protected void onSaveInstanceState(Bundle outState) {

        outState.putInt("currentPos", mCurrentPos);
        super.onSaveInstanceState(outState);
    }


    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        //SV可见
        initMediaPlayer();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder
            , int format, int width, int height) {
        //SV状态变化
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (mMediaPlayer != null) {
            mMediaPlayer.pause();
            mCurrentPos = mMediaPlayer.getCurrentPosition();
        }
    }

    /**
     * 销毁掉MediaPlayer对象
     */
    private void releaseMP() {

        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
            mMediaPlayer.release();
            mMediaPlayer = null;
            System.gc();
        }

    }

    @Override
    public void onPrepared(MediaPlayer mp) {

        resetSize();
        if (mCurrentPos != 0) {
            mMediaPlayer.seekTo(mCurrentPos);
        }

        mMediaPlayer.start();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        isLand = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        mScreenWidth = displayMetrics.widthPixels;
        mScreenHeight = displayMetrics.heightPixels;

        resetSize();
    }

    public void changeOrientation(View view) {
        if (Configuration.ORIENTATION_LANDSCAPE == this.getResources()
                .getConfiguration().orientation) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        } else {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        }

    }


    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus && Build.VERSION.SDK_INT >= 19) {
            View decorView = getWindow().getDecorView();
            decorView.setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_FULLSCREEN
                            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
        }
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        releaseMP();

    }
}


因为主要是讲解SurfaceView大小的调整问题,所以并没有去定制控制MediaPlayer的UI布局,简单的分析一下代码:

先来看initView()方法:

1. 目前纵向视频播放一般接受的尺寸为16:9,所以在这我也是设置了16:9的尺寸,视频的宽(RelativeLayout的宽)为手机的宽,视频高的话,根据比例换算。


2.SurfaceView的产生和销毁,都是通过SurfaceHolder进行控制的,添加上SurfaceView的监听事件,这个的话原因想必大家也都知道,一旦Activity切换到后台或者跳转到其他的Activity界面,SurfaceView 就会被销毁,当切换回来Activity界面的时候SurfaceView 又会自动创建,这个时候我们需要重新绑定上新的SurfaceHolder,不然就会出现只是播放声音而不播放画面的问题,代码中每次执行SurfaceCreate()都会执行initMediaPlayer()这个方法,在这个方法中重新绑定了一次SurfaceHoder.

3.播放完毕或者是停止都需要将MediaPlayer资源释放掉。

Ok,主要来 分析一下resetSize()这个方法:

resetSize()这个方法,没别在onPrepare()和onConfigurationChanged中调用到了,一个是视频准备完成,另外一个是横竖屏切换。

53行:


if (!isLand) {
    // 竖屏16:9
    height = (int) (mScreenWidth / SHOW_SCALE);
    areaWH = SHOW_SCALE;
} else {
    //横屏按照手机屏幕宽高计算比例
    height = mScreenHeight;
    areaWH = mScreenWidth / mScreenHeight;
}


isLand 是否横屏,不是的话根据16:9比例计算出视频框高,是横屏的话,则手机屏幕的高作为视频框的高,比例当然是视频的宽/高,需要注意的是手机横竖屏切换会导致屏幕的宽高颠倒,在onConfigChanged()中已重新获取了手机宽高。




int mediaWidth = mMediaPlayer.getVideoWidth();
int mediaHeight = mMediaPlayer.getVideoHeight();

float mediaWH = mediaWidth * 1.0f / mediaHeight;



获取视频大小的宽高比




if (areaWH > mediaWH) {
    //直接放会矮胖
    int svWidth = (int) (height * mediaWH);
    layoutParamsSV = new RelativeLayout.LayoutParams(svWidth, height);
    layoutParamsSV.addRule(RelativeLayout.CENTER_IN_PARENT);
    mSurfaceView.setLayoutParams(layoutParamsSV);
}

if (areaWH < mediaWH) {
    //直接放会瘦高。
    int svHeight = (int) (mScreenWidth / mediaWH);
    layoutParamsSV = new RelativeLayout.LayoutParams(mScreenWidth, svHeight);
    layoutParamsSV.addRule(RelativeLayout.CENTER_IN_PARENT);
    mSurfaceView.setLayoutParams(layoutParamsSV);
}

如果是播放区域宽高比大于视频宽高比的话,这个时候的情况就是视频的高要大一些,那么需要缩放到视频播放框以内,也就是让视频的高等于播放区的高度,
同时按照视频的宽高比进行等比例缩放即可。如果是播放区的宽高比小于视频的宽高比的话,这个时候说明视频的宽高更大一些,我们需要将播放框的宽作为视
频的宽,然后按照等比例缩放计算出视频的高度。计算出宽高之后SurfaceView重新布局即可。SurfaceView 布局在RleativeLayout中设置了居中对其.

你可能感兴趣的:(多媒体)