Как я могу плавно увеличить и уменьшить масштаб в Android? Относится к SurfaceView с потоковой передачей RTMP
Я реализую функцию, которая может увеличивать или уменьшать масштаб при воспроизведении потокового RTMP с объектом SurfaceView. Я провел некоторое исследование и решил сначала вычислить размер и положение и использовать метод onMeasure() для пересмотра представления.
Теперь у меня проблема в том, что объект не соответствует положению, которое я установил во время масштабирования. Эффект, который я хотел бы реализовать, похож на это видео (которое показывает поведение версии моего приложения для iOS), и я записал другое видео, чтобы показать, что я сделал до сих пор. Объект будет смещаться чаще, если я использую ту же позицию, что и приложение версии iOS. Это не совсем гладко и может привести к ухудшению работы пользователя.
Мое наблюдение за поведением состоит в том, что объект SurfaceView изменил бы размеры своей ширины и высоты с фиксированным положением x и y при срабатывании функции масштабирования. Затем объект будет сдвигаться назад, что может быть результатом следования макету его родителя. Я распечатал сообщения журнала и обнаружил, что положение x и y изменилось бы при запуске onMeasure(). Хотя я установил позицию в функции, она все равно будет изменена, когда процесс перейдет к 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, есть ли какое-либо предлагаемое решение? Благодарю.
1 ответ
Перемещение и изменение размера SurfaceView, вероятно, не правильный ответ. Часть Surface в SurfaceView - это отдельный слой, размером и положением которого управляет диспетчер окон, а не иерархия View приложения, поэтому анимация атрибутов SurfaceView выглядит неопрятно.
Одним из возможных подходов было бы переключиться с SurfaceView на TextureView. Вы можете избавиться от кода макета и просто использовать матрицу преобразования TextureView. Посмотрите, например, разницу в том, как обрабатывается соотношение сторон видео в классах воспроизведения фильмов Grafika - PlayMoveSurfaceActivity.clickPlayStop () использует AspectFrameLayout, в то время как PlayMovieActivity.clickPlayStop () на основе TextureView оставляет один только View и настраивает матрицу,
Другой подход заключается в том, чтобы сохранить SurfaceView, но отправить предварительный просмотр камеры в SurfaceTexture и визуализировать вывод в текстуру OpenGL ES. Посмотрите TextureFromCameraActivity для примера. Обратите внимание, как реализована функция масштабирования путем обновления координат текстуры. Это гораздо более гибко, но вам нужно будет включить установочный код GLES.