android - 如何在 Android 中平滑地实现放大缩小?与带有 RTMP 流的 SurfaceView 相关

标签 android android-layout surfaceview zooming onmeasure

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

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

我对该行为的观察是,当缩放功能触发时,SurfaceView 的对象将使用固定位置 x 和 y 重新调整其宽度和高度。然后,该对象将向后移动,这可能是由于遵循其父级的布局而导致的。我打印了日志消息,发现当 onMeasure() 被触发时,位置 x 和 y 会发生变化。虽然我在函数中设置了position,但是到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 实现放大缩小功能,是否有任何建议的解决方案?谢谢。

最佳答案

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

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

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

关于android - 如何在 Android 中平滑地实现放大缩小?与带有 RTMP 流的 SurfaceView 相关,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28291514/

相关文章:

android - 库不可导入

java - 异步任务 : How to return a HashMap from doInBackground?

android - 在 VM 中 Dockerize android 模拟器

android - Android 中的相对布局

java - 通过 surfaceview 使用 zxing 条形码阅读器

java - 该类型的方法和构造函数未定义

java - 适用于 Android 穿戴的 Moto 360 模拟器

android - 将 android 布局转换到另一个位置

android - 如何在 SurfaceView 上显示相机预览?

android - 如何重绘surfaceview android的一部分