如何以编程方式管理自定义视频播放器放大/缩小?

时间:2013-12-19 08:41:44

标签: android video android-video-player

我们可以通过两种方式实现CUSTOM视频播放器:

  1. VideoView

  2. SurfaceView并使用MediaPlayer执行此操作。

  3. 我阅读了一篇与Custom Video Player相关的教程,其中使用了SurfaceView并使用MediaPlayer来实现此功能 - link here

    我的需求是什么:

    如何制作自定义视图,例如"When you touch on the video screen such as zoom out/zoom in, video view goes to the full screen and after zoom in gesture back to fix size what earlier "

    有人可以建议我怎么做这件事吗?

1 个答案:

答案 0 :(得分:0)

我与你分享我的代码我正在使用纹理视图使用两个手指放大/缩小(捏缩放)

    package com.app.sampletextureview;

import android.app.Activity;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.SurfaceTexture;
import android.media.MediaActionSound;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;

import android.util.FloatMath;
import android.util.Log;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;

import java.io.IOException;


public class MainActivity extends Activity implements TextureView.SurfaceTextureListener,View.OnTouchListener {
    private static final String TAG = MainActivity.class.getName();
    private static final String FILE_NAME = "Vid.mp4";

    private MediaPlayer mMediaPlayer;
    private TextureView mTextureView;
    private RelativeLayout mrRelativeLayout;

    private float mVideoWidth;
    private float mVideoHeight;

    DisplayMetrics dm;

    RelativeLayout.LayoutParams params;

    private ScaleGestureDetector scaleGestureDetector;
    private Matrix matrix = new Matrix();

    @SuppressWarnings("unused")
    private static final float MIN_ZOOM = 1f,MAX_ZOOM = 1f;

    // These matrices will be used to scale points of the image

    Matrix savedMatrix = new Matrix();

    // The 3 states (events) which the user is trying to perform
    static final int NONE = 0;
    static final int DRAG = 1;
    static final int ZOOM = 2;
    int mode = NONE;

    // these PointF objects are used to record the point(s) the user is touching
    PointF start = new PointF();
    PointF mid = new PointF();
    float oldDist = 1f;

    private float mVideoWidth;
private float mVideoHeight;



int viewWidth=0,viewHeight=0,xoff=0,yoff=0;;




    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        calculateVideoSize();

        initView();

    }
    private void initView() {

        mrRelativeLayout= (RelativeLayout) findViewById(R.id.rootView);
        mTextureView = (TextureView) findViewById(R.id.textureView);

        mTextureView.setOnTouchListener(this);


        /*Log.d("Height-WidthOnCreate",""+mVideoHeight+"-"+mVideoWidth);
        mTextureView.setMinimumWidth((int)mVideoWidth);

        mTextureView.setMinimumHeight((int)mVideoHeight);

        mTextureView.setSurfaceTextureListener(this);


    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i2) {
        Surface surface = new Surface(surfaceTexture);

        try {
            AssetFileDescriptor afd = getAssets().openFd(FILE_NAME);
            mMediaPlayer = new MediaPlayer();

            mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());

            mMediaPlayer.setSurface(surface);

            mMediaPlayer.setLooping(true);

            // don't forget to call MediaPlayer.prepareAsync() method when you use constructor for
            // creating MediaPlayer
            mMediaPlayer.prepareAsync();

            adjustAspectRatio((int) mVideoWidth, (int)mVideoHeight);
                Log.d("SurfaceCreate",""+mVideoWidth+""+mVideoHeight);



            // Play video when the media source is ready for playback.
            mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mediaPlayer) {
                    mediaPlayer.start();
                }
            });

        } catch (IllegalArgumentException e) {
            Log.d(TAG, e.getMessage());
        } catch (SecurityException e) {
            Log.d(TAG, e.getMessage());
        } catch (IllegalStateException e) {
            Log.d(TAG, e.getMessage());
        } catch (IOException e) {
            Log.d(TAG, e.getMessage());
        }




    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i2) {

    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {



        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {

    }

    private void calculateVideoSize() {
        try {
            AssetFileDescriptor afd = getAssets().openFd(FILE_NAME);
            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);

            Log.d("Height-Width",""+mVideoHeight+"-"+mVideoWidth);

        } catch (IOException e) {
            Log.d(TAG, e.getMessage());
        } catch (NumberFormatException e) {
            Log.d(TAG, e.getMessage());
        }
    }


    private void updateTextureViewSize(int viewWidth, int viewHeight) {
        float scaleX = 1.0f;
        float scaleY = 1.0f;

        if (mVideoWidth > viewWidth && mVideoHeight > viewHeight) {
            scaleX = mVideoWidth / viewWidth;
            scaleY = mVideoHeight / viewHeight;
        } else if (mVideoWidth < viewWidth && mVideoHeight < viewHeight) {
            scaleY = viewWidth / mVideoWidth;
            scaleX = viewHeight / mVideoHeight;
        } else if (viewWidth > mVideoWidth) {
            scaleY = (viewWidth / mVideoWidth) / (viewHeight / mVideoHeight);
        } else if (viewHeight > mVideoHeight) {
            scaleX = (viewHeight / mVideoHeight) / (viewWidth / mVideoWidth);
        }

        // Calculate pivot points, in our case crop from center
        int pivotPointX = viewWidth / 2;
        int pivotPointY = viewHeight / 2;

        Matrix matrix = new Matrix();
        matrix.setScale(scaleX, scaleY, pivotPointX, pivotPointY);

        mTextureView.setTransform(matrix);

    }


    private void adjustAspectRatio(int videoWidth, int videoHeight) {
        int viewWidth = mTextureView.getWidth();
        int viewHeight = mTextureView.getHeight();
        double aspectRatio = (double) videoHeight / videoWidth;

        int newWidth, newHeight;
        if (viewHeight > (int) (viewWidth * aspectRatio)) {
            // limited by narrow width; restrict height
            newWidth = viewWidth;
            newHeight = (int) (viewWidth * aspectRatio);
        } else {
            // limited by short height; restrict width
            newWidth = (int) (viewHeight / aspectRatio);
            newHeight = viewHeight;
        }
        int xoff = (viewWidth - newWidth) / 2;
        int yoff = (viewHeight - newHeight) / 2;
        Log.v(TAG, "video=" + videoWidth + "x" + videoHeight +
                " view=" + viewWidth + "x" + viewHeight +
                " newView=" + newWidth + "x" + newHeight +
                " off=" + xoff + "," + yoff);

        Matrix txform = new Matrix();
        mTextureView.getTransform(txform);
        txform.setScale((float) newWidth / viewWidth, (float) newHeight / viewHeight);
        //txform.postRotate(10);          // just for fun
        txform.postTranslate(xoff, yoff);
        mTextureView.setTransform(txform);
    }


  /*  @Override
    public boolean onTouchEvent(MotionEvent ev) {
//        scaleGestureDetector.onTouchEvent(ev);
        return true;
    }
*/



    private class ScaleListener extends ScaleGestureDetector.
            SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            float scaleFactor = detector.getScaleFactor();
            scaleFactor = Math.max(0.1f, Math.min(scaleFactor, 5.0f));
            matrix.setScale(scaleFactor, scaleFactor);
            Log.d("Matrix", "" + scaleFactor);
            mTextureView.setTransform(matrix);

            return true;
        }
    }


 @Override
public boolean onTouch(View v, MotionEvent event)
{



    dumpEvent(event);
    // Handle touch events here...


    switch (event.getAction() & MotionEvent.ACTION_MASK)
    {
        case MotionEvent.ACTION_DOWN:   // first finger down only

            if (!istouch) {
                istouch=true;
                ll_bottombar.setVisibility(View.VISIBLE);
                ll_topheader.setVisibility(View.VISIBLE);
                hideSystemUI(getWindow());

            }else{
                String tag = btnLock.getTag().toString();
                if (tag.equals("false")) {
                    istouch = false;
                    ll_bottombar.setVisibility(View.GONE);
                    ll_topheader.setVisibility(View.GONE);
                    hideSystemUI(getWindow());

                }
            }
            break;

        case MotionEvent.ACTION_UP: // first finger lifted

            mode = NONE;
            Log.d(TAG, "mode=NONE");
            break;

        case MotionEvent.ACTION_POINTER_UP: // second finger lifted

            mode = NONE;
            Log.d(TAG, "mode=NONE");
            break;

        case MotionEvent.ACTION_POINTER_DOWN: // first and second finger down
       //case MotionEvent.ACTION_POINTER_2_DOWN:

            oldDist = spacing(event);
            Log.d(TAG, "oldDist=" + oldDist);

            if (oldDist > 5f) {
               savedMatrix.set(matrix);
                midPoint(mid, event);
              //  matrix.setScale( mid.y,(float) newWidth / viewWidth,mid.x, (float) newHeight / viewHeight);
               // matrix.setScale(mid.x,mid.y);
                if(event.getPointerCount() >= 2) {
                    event.setAction(MotionEvent.ACTION_MOVE);
                    mode = ZOOM;

                    Log.d(TAG, "mode=ZOOM");
                }

            }
           break;


        case MotionEvent.ACTION_MOVE:
            if (mode == NONE)
        {
            mode = NONE;
            Log.d(TAG, "mode=NONE");

        }
            if (event.getPointerCount() >= 2) {
           /* if (mode == DRAG)
            {

                // matrix.set(savedMatrix);
                //  matrix.postTranslate(event.getX() - start.x, event.getY() - start.y); // create the transformation in the matrix  of points
            }else if (mode == NONE)
        {
            mode = NONE;
            Log.d(TAG, "mode=NONE");

        }*/
             if (mode == ZOOM) {

                    // pinch zooming
                    float newDist = spacing(event);

                    Log.d(TAG, "newDist=" + newDist);
                    if (newDist > 5f) {
                        matrix.set(savedMatrix);
                        Log.d(TAG, "NEW="+newDist+"--"+oldDist);
                        Log.d(TAG, "NEW Mid Point="+mid.x+"--"+mid.y);
                        scale = newDist / oldDist;
                       // matrix.setScale();
                       // matrix.setScale(scale,scale);
                        matrix.postScale(scale,scale,mid.x, mid.y);


                        Log.d(TAG,"New Hight Width"+mTextureView.getWidth()+"--"+mTextureView.getHeight());

                    }

                }

                mTextureView.setTransform(matrix);
            }


            break;
    }

  // display the transformation on screen

    return true; // indicate event was handled
}
  /*  * --------------------------------------------------------------------------
     * Method: spacing Parameters: MotionEvent Returns: float Description:
     * checks the spacing between the two fingers on touch
     * ----------------------------------------------------
*/


    private float spacing(MotionEvent event)
    {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float)Math.sqrt(x * x + y * y);
    }

   /*  * --------------------------------------------------------------------------
     * Method: midPoint Parameters: PointF object, MotionEvent Returns: void
     * Description: calculates the midpoint between the two fingers
     * ------------------------------------------------------------

    private void midPoint(PointF point, MotionEvent event)
    {
        float x = mrRelativeLayout.getX()+mrRelativeLayout.getWidth();
        float y = mrRelativeLayout.getY()+mrRelativeLayout.getHeight();
        point.set(x / 2, y / 2);
    }


    private void dumpEvent(MotionEvent event)
    {
        String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE","POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };
        StringBuilder sb = new StringBuilder();
        int action = event.getAction();
        int actionCode = action & MotionEvent.ACTION_MASK;
        sb.append("event ACTION_").append(names[actionCode]);

        if (actionCode == MotionEvent.ACTION_POINTER_DOWN || actionCode == MotionEvent.ACTION_POINTER_UP)
        {
            sb.append("(pid ").append(action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
            sb.append(")");
        }

        sb.append("[");
        for (int i = 0; i < event.getPointerCount(); i++)
        {
            sb.append("#").append(i);
            sb.append("(pid ").append(event.getPointerId(i));
            sb.append(")=").append((int) event.getX(i));
            sb.append(",").append((int) event.getY(i));
            if (i + 1 < event.getPointerCount())
                sb.append(";");
        }

        sb.append("]");
    }



private void adjustAspectRatio(int videoWidth, int videoHeight) {

 viewWidth = mTextureView.getWidth();
     viewHeight = mTextureView.getHeight();

    double aspectRatio = (double) videoHeight / videoWidth;


    if (viewHeight > (int) (viewWidth * aspectRatio)) {
        // limited by narrow width; restrict height
        newWidth = viewWidth;
        newHeight = (int) (viewWidth * aspectRatio);
    } else {
        // limited by short height; restrict width
        newWidth = (int) (viewHeight / aspectRatio);
        newHeight = viewHeight;
    }

    xoff = (viewWidth - newWidth) / 2;

    yoff = (viewHeight - newHeight) / 2;


    Log.d(TAG, "video=" + videoWidth + "x" + videoHeight +
            " view=" + viewWidth + "x" + viewHeight +
            " newView=" + newWidth + "x" + newHeight +
            " off=" + xoff + "," + yoff);

    Log.d(TAG, "video="+mTextureView.getHeight()+"--"+mTextureView.getWidth());
    Log.d(TAG, "video="+newHeight+"--"+newWidth);
    mTextureView.setMinimumWidth(newWidth);
    mTextureView.setMinimumHeight(newHeight);

   // Matrix txform = new Matrix();
    mTextureView.getTransform(matrix);
    matrix.setScale((float) newWidth / viewWidth, (float) newHeight / viewHeight);
    //txform.postRotate(10);          // just for fun
    matrix.postTranslate(xoff, yoff);
    mTextureView.setTransform(matrix);
}

}     }