如何动画更改 VideoView 的大小?

How Can I Animate Changing the Size of a VideoView?

我有一个 VideoView,我想在我的应用程序中动态更改其大小。通过扩展 VideoView class,我已经成功地让视频和 videoView 正确地改变大小。但是,我希望能够在两种尺寸之间逐渐过渡。我怎样才能做到这一点?我试过缩放动画,但虽然这会改变 VideoView 布局的大小,但视频本身不会缩放。想法?

这是我的视频class:

public class MyVideoView extends VideoView {

    private int mForceHeight,mForceWidth;
    private int mOrigWidth, mOrigHeight, mMinWidth, mMinHeight;


    public MyVideoView(Context context) {
        super(context);
    }

    /* Will cause inflator errors if not present */
    public MyVideoView(Context context, AttributeSet attrs){
        super(context, attrs);
    }

    public void setDimensions(int w, int h) {
        this.mForceHeight = h;
        this.mForceWidth = w;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        if (mForceHeight != 0)
            setMeasuredDimension(mForceWidth, mForceHeight);
        else
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }


    public void setOrigDimens(int width, int height) {
        mOrigWidth = width;
        mOrigHeight = height;

        mMinWidth = width/4;    // My own decision for the small size
        mMinHeight = height/4;
    }


    public void setSmallView() {
        setNewViewSize(mMinWidth, mMinHeight);
    }


    public void setNormalView() {
        setNewViewSize(mOrigWidth, mOrigHeight);
    }


    /* RESIZES THE VIEW */
    public void setNewViewSize(int width, int height) {
        mForceWidth = width;
        mForceHeight = height;
        setDimensions(width, height);
        getHolder().setFixedSize(width, height);
    }
}

这是我试过的缩放代码:

Animation scaling = new ScaleAnimation(1.0f, 0.2f, 1.0f, 0.2f);
scaling.setDuration(2000);
startAnimation(scaling);

非常感谢任何帮助!

我找到的一个解决方案是使用 ValueAnimator 更改大小应有的比例,然后使用更新侦听器强制视频实际更改大小。然而,它非常紧张:

ValueAnimator scaleDown = ValueAnimator.ofFloat(1, 0.25f);
    scaleDown.setDuration(1000);

    scaleDown.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        public void onAnimationUpdate(ValueAnimator animation) {
            Float value = (Float) animation.getAnimatedValue();
            setNewViewSize(mOrigWidth*value, mOrigHeight*value);
        }
    });

    scaleDown.start();

最佳答案是简单地使用 TextureView 而不是 SurfaceView(VideoView 默认继承自 SurfaceView)。为此,请使用如下代码:

activity_main.xml 中定义您的 TextureView:

<TextureView
    android:id="@+id/videoTexture"
    android:layout_width="match_parent"
    android:layout_height="match_parent />

在您的 MainActivity.java 中,声明以下变量:

private MediaPlayer mMediaPlayer;
private TextureView mVideoTextureView;
private float mDisplayWidth;
private float mDisplayHeight;

然后在您的 onCreate() 方法中,按如下方式初始化它们:

mVideoTextureView =(TextureView) rootview.findViewById(R.id.videoTexture);
mVideoTextureView.setSurfaceTextureListener(this);

mMediaPlayer = new MediaPlayer();
loadNewMovie();

要将电影加载到 MediaPlayer 中,请使用以下代码:

private void loadNewMovie() {

    AssetFileDescriptor afd = this.getResources().openRawResourceFd(ID_OF_YOUR_MOVIE);

    try {

        // Set source

        mMediaPlayer.reset();
        mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength());
        mMediaPlayer.prepare();

        /*
        // Gets the Height/Width of the video but does NOT include space
        // taken up by black bars if the dimensions don't exactly fit the screen.

        MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();
        metaRetriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
        String height = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
        String width = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
        mVideoHeight = Float.parseFloat(height);
        mVideoWidth = Float.parseFloat(width);
        */

        // Gets the size of the display in pixels (used for scaling)

        Display display = getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);

        mDisplayWidth = size.x;
        mDisplayHeight = size.y;

        // Play movie

        if (currState == State.PLAYING)
            mMediaPlayer.start();

        afd.close();
    } 
    catch (Exception e) {
        Log.e("ERROR", "loadNewMovie: " + e.getMessage(), e);
    }

    mMediaPlayer.seekTo(DEFAULT_VIDEO_INIT_POSITION);
}

最后,您可以使用以下代码即时调整您的视频:

private void updateTextureViewSize(int viewWidth, int viewHeight) {

    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(viewWidth,viewHeight);

    // ANY OTHER RULES...EXAMPLE:
    // params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);

    mVideoTextureView.setLayoutParams(params);
}

或者您可以像这样为变化设置动画:

private void animateTextureViewScaling(final float startScaleX, final float startScaleY, 
                                       final float endScaleX, final float endScaleY, 
                                       int duration) {

    // Note: Can't just use .scaleX and .scaleY directly because it will only scale 
    // the video, not it's holder

    mVideoTextureView.animate().setDuration(duration)
        .setUpdateListener(new AnimatorUpdateListener() {

            float value, scalingX, scalingY;
            float changeXScale = startScaleX - endScaleX;
            float changeYScale = startScaleY - endScaleY;

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                value = (Float) animation.getAnimatedValue();

                scalingX = (float) (startScaleX - changeXScale*value);
                scalingY = (float) (startScaleX - changeYScale*value);

                RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
                        (int) (mDisplayWidth*scalingX), (int) (mDisplayHeight*scalingY));

                // ANY OTHER RULES...EXAMPLE:
                // params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);

                mVideoTextureView.setLayoutParams(params);
            }

        }).start();
}

在上面的代码中,我使用mDisplayWidth(在loadNewVideo()中设置)作为我视频的原始尺寸。想怎么用就怎么用。

好处 TextureView 可以动画、变换和缩放(SurfaceView 不能)。

缺点 是 TextureView 只能用于硬件加速 window 并且会比 SurfaceView 使用更多的内存(大约 30%)。它还可能会遇到 1 到 3 帧延迟。