2

我正在实现一个可以在使用 SurfaceView 对象播放 RTMP 流时放大或缩小的功能。我做了一些研究,我决定先计算大小和位置,然后使用 onMeasure() 方法重新构建视图。

现在我有一个问题,即对象并没有真正遵循我在缩放期间设置的位置。我想要实现的效果就像这个视频(它显示了我的应用程序的 iOS 版本的行为),我录制了另一个视频来展示我到目前为止所做的事情。如果我使用与 iOS 版本应用程序相同的位置,对象会更频繁地移动。它根本不流畅,可能会导致糟糕的用户体验。

我对这种行为的观察是 SurfaceView 的对象会在触发缩放功能时使用固定位置 x 和 y 重新调整其宽度和高度。然后,对象将向后移动,这可能是由于遵循其父级的布局。我打印了日志消息,发现当 onMeasure() 被触发时位置 x 和 y 会发生变化。虽然我已经在函数中设置了位置,但是当进程转到onLayout()时它仍然会改变。由于 onLayout() 在 onMeasure() 完成后立即被调用,所以我看不到任何可能的时间来设置位置以避免移位。

以下是我所做的: 布局:

<RelativeLayout
    android:id="@+id/RelaytiveView"
    android:layout_width="fill_parent"
    android:layout_height="200dp">
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_centerInParent="true"
        android:background="#000000"
        android:gravity="center"
        android:orientation="vertical" >
        <com.player.adapter.RtmpLiveSurfaceview
            android:id="@+id/goLiveView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>
</RelativeLayout>

和代码:

public class RtmpLiveSurfaceview extends SurfaceView 
{
    private static final int DEFAULT_PIXEL_WIDTH = 1280;
    private static final int DEFAULT_PIXEL_HEIGHT = 720;
    private float INIT_WIDTH;
    private float INIT_HEIGHT;
    private float WIDTH = 0;
    private float HEIGHT = 0;
    private float SCALER = (float) 1.0;
    private float TAG_POINT_X = 0;
    private float TAG_POINT_Y = 0;

    private float INIT_DIST = 0;
    SurfaceHolder mholder;

    static final int ZOOM = 2;

    @Override
    protected void onSizeChanged (int w, int h, int oldw, int oldh)
    {
    }
    @Override
    protected void onLayout (boolean changed, int left, int top, int right, int bottom)
    {
        setX(TAG_POINT_X);
        setY(TAG_POINT_Y);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
    {
        int width = getDefaultSize(DEFAULT_PIXEL_WIDTH, widthMeasureSpec);
        int height = getDefaultSize(DEFAULT_PIXEL_HEIGHT, heightMeasureSpec);
        if (DEFAULT_PIXEL_WIDTH > 0 && DEFAULT_PIXEL_HEIGHT > 0)
        {
            if ( DEFAULT_PIXEL_WIDTH * height  > width * DEFAULT_PIXEL_HEIGHT )
            {
                height = width * DEFAULT_PIXEL_HEIGHT / DEFAULT_PIXEL_WIDTH;
            } 
            else if ( DEFAULT_PIXEL_WIDTH * height  < width * DEFAULT_PIXEL_HEIGHT )
            {
                width = height * DEFAULT_PIXEL_WIDTH / DEFAULT_PIXEL_HEIGHT;
            }
            else
            {
            }
        }
        INIT_WIDTH = width;
        INIT_HEIGHT = height;
        if(WIDTH == 0)
        {
            WIDTH = INIT_WIDTH;
            HEIGHT = INIT_HEIGHT;
        }
        int wmode = MeasureSpec.getMode(widthMeasureSpec);
        int hmode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSpec = MeasureSpec.makeMeasureSpec((int)WIDTH, wmode);
        int heightSpec = MeasureSpec.makeMeasureSpec((int)HEIGHT, hmode);
        setX(TAG_POINT_X);
        setY(TAG_POINT_Y);
        super.onMeasure(widthSpec, heightSpec);
    }
    public void init() 
    {
        mholder = getHolder();
        mholder.setSizeFromLayout();
        mholder.addCallback(new Callback() 
        {
            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
            {
            }
        });
    }

    public boolean onTouchEvent(MotionEvent MEvent) 
    {
        switch (MEvent.getAction() & MotionEvent.ACTION_MASK) 
        {
            case MotionEvent.ACTION_POINTER_DOWN:
            {
                if(MEvent.getPointerCount() >= 2)
                {
                    mode = ZOOM;
                    INIT_DIST = spacing(MEvent);
                    break;
                }
            }
            case MotionEvent.ACTION_MOVE:
            {
                if (mode == ZOOM) 
                {
                    if(MEvent.getPointerCount() >= 2)
                    {
                        float newDist = spacing(MEvent);
                        float tmp_scaler = (float) (((newDist - INIT_DIST) * 0.1 + INIT_DIST) / INIT_DIST);
                        if((tmp_scaler * WIDTH) > (2.0 * INIT_WIDTH))
                            tmp_scaler = (float) (((newDist - INIT_DIST) * 0.01 + INIT_DIST) / INIT_DIST);
                        WIDTH = getWidth();
                        HEIGHT = getHeight();
                        if (((tmp_scaler * WIDTH) >= (1.0 * INIT_WIDTH)) && ((tmp_scaler * WIDTH) <= (4.0 * INIT_WIDTH)))
                        {
                            SCALER = tmp_scaler;
                            TAG_POINT_X = getX();
                            if((TAG_POINT_X + WIDTH * SCALER) < INIT_WIDTH)
                                TAG_POINT_X = INIT_WIDTH - WIDTH * SCALER;
                            else if(TAG_POINT_X > 0)
                                TAG_POINT_X = 0;
                            TAG_POINT_Y = getY();
                            if((TAG_POINT_Y + HEIGHT * SCALER) < INIT_HEIGHT)
                                TAG_POINT_Y = INIT_HEIGHT - HEIGHT * SCALER;
                            else if(TAG_POINT_Y > 0)
                                TAG_POINT_Y = 0;
                            WIDTH = WIDTH * SCALER;
                            HEIGHT = HEIGHT * SCALER;
                            requestLayout();
                        }
                    }
                }
                break;
            }

        }

        return true;
    }
}

谁能帮我?如果 SurfaceView 无法实现放大缩小功能,有什么建议的解决方案吗?谢谢。

4

1 回答 1

1

移动和调整 SurfaceView 的大小可能不是正确的答案。SurfaceView 的 Surface 部分是一个单独的层,其大小和位置由窗口管理器管理,而不是应用程序的视图层次结构,因此为 SurfaceView 的属性设置动画往往看起来很笨拙。

一种可能的方法是从 SurfaceView 切换到 TextureView。您可以摆脱布局代码,只使用 TextureView 的转换矩阵。例如,请参阅 Grafika 电影播放类中处理视频纵横比的方式的差异—— PlayMoveSurfaceActivity .clickPlayStop() 使用 AspectFrameLayout,而基于TextureView 的PlayMovieActivity .clickPlayStop() 只保留 View 并调整矩阵.

另一种方法是保留 SurfaceView,但将相机预览发送到 SurfaceTexture 并将输出渲染到 OpenGL ES 纹理。有关示例,请参见TextureFromCameraActivity 。注意缩放功能是如何通过更新纹理坐标来实现的。这更加灵活,但您需要合并 GLES 设置代码。

于 2015-02-03T17:07:04.890 回答