Как я могу сделать SurfaceView больше, чем экран? - PullRequest
13 голосов
/ 19 мая 2011

Я хотел бы эффективно сделать простой цифровой зум для предварительного просмотра камеры, поэтому я подумал, что просто изменим размер моего SurfaceView, чтобы он был больше экрана. Другие вопросы (такие как 3813049 ), кажется, указывают на то, что это легко, поэтому я создал пример кода, ниже которого я ожидаю, что я смогу видеть только половину изображения по горизонтали (поскольку SurfaceView в два раза шире, чем экран), и изображение занимает только половину экрана по горизонтали. Тем не менее, запуск его (когда он предназначен для SDK версии 4 на моем Thunderbolt с Android 2.2.1) приводит к возможности видеть все изображение по горизонтали при заполнении экрана по горизонтали. Поверхностный вид, кажется, ведет себя как задумано вертикально (когда я делаю его меньше, чем экран), но Android не позволяет мне сделать Поверхность больше, чем экран.

Как я могу реализовать цифровой зум? (Нет, я не могу использовать Camera.Parameters.setZoom; не только это не поддерживается Android 1.6, но разные камеры поддерживают и реализуют это по-разному)

public class MagnifyTestActivity extends Activity implements SurfaceHolder.Callback {
    private MagnificationView mPreview;
    private SurfaceHolder mHolder;
    private Camera mCamera = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPreview = new MagnificationView(this);
        setContentView(mPreview);
        mHolder = mPreview.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public class MagnificationView extends SurfaceView {
        public MagnificationView(Context context) {
            super(context);
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            Display display = getWindowManager().getDefaultDisplay();
            int width = display.getWidth()*2;
            int height = display.getHeight()/2;
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    public void surfaceCreated(SurfaceHolder holder) {
        mCamera = Camera.open();
        try {
            mCamera.setPreviewDisplay(holder);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        mHolder.setFixedSize(w, h);
        mCamera.startPreview();
    }
}

ОБНОВЛЕНИЕ: Основываясь на ответе @Pulkit Sethi, можно растянуть / увеличить SurfaceView по вертикали, но не по горизонтали. Чтобы увеличить SurfaceView по вертикали, просто замените display.getHeight () / 2 на display.getHeight () * 2 выше. Также обратите внимание, что изменение ширины не приводит к увеличению по горизонтали, ни в моем коде, ни в Pulkit.

Ответы [ 4 ]

3 голосов
/ 25 марта 2013
//Activity class

public class CameraActivity extends Activity implements SurfaceListener {

    private static final String TAG = "CameraActivity";

    Camera mCamera;
    CameraPreview mPreview;
    private FrameLayout mCameraPreview;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);

        setContentView(R.layout.activity_camera);

        mCamera = getCameraInstance();
        mPreview = new CameraPreview(this, mCamera);

        mCameraPreview = (FrameLayout) findViewById(R.id.camera_preview);
        mCameraPreview.addView(mPreview);


    }

    @Override
    protected void onPause() {
        super.onPause();

        releaseCamera();
    }

    private Camera getCameraInstance() {
        Camera camera = null;
        try {
            camera = Camera.open();
        } catch (Exception e) {

        }
        return camera;
    }

    private void releaseCamera() {
        if (null != mCamera) {
            mCamera.release();
        }

        mCamera = null;
    }

    @Override
    public void surfaceCreated() {

        //Change these mate
        int width = 1000;
        int height = 1000;
        // Set parent window params
        getWindow().setLayout(width, height);

        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
                width, height);
        mCameraPreview.setLayoutParams(params);
        mCameraPreview.requestLayout();
    }
}

// Preview class

public class CameraPreview extends SurfaceView implements
        SurfaceHolder.Callback {

    private static final String TAG = "CameraPreview";

    Context mContext;
    Camera mCamera;
    SurfaceHolder mHolder;

    public interface SurfaceListener{
        public void surfaceCreated();
    }
    SurfaceListener listener;


    public CameraPreview(Context context, Camera camera) {
        super(context);

        mContext = context;
        listener = (SurfaceListener)mContext;
        mCamera = camera;

        mHolder = getHolder();
        mHolder.addCallback(this);

        // Required prior 3.0 HC
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

        try {
            mCamera.setPreviewDisplay(holder);

            Parameters params = mCamera.getParameters();
            //Change parameters here
            mCamera.setParameters(params);

            mCamera.startPreview();

            listener.surfaceCreated();

        } catch (Exception e) {
            // TODO: handle exception
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.

        Log.i(TAG, "Surface changed called");
        if (mHolder.getSurface() == null) {
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here
        mCamera.setDisplayOrientation(90);

        // start preview with new settings
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

        } catch (Exception e) {
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }

}

//Layout file 
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <FrameLayout
        android:id="@+id/camera_preview"
        android:layout_width="300dp"
        android:layout_height="400dp"
        android:layout_centerHorizontal="true"
        android:paddingTop="10dp" >
    </FrameLayout>

</RelativeLayout>
1 голос
/ 21 марта 2013

Что вы можете сделать, это получить окно и установить его высоту:

getWindow().setLayout(1000, 1000);

Это делает ваше окно больше, чем экран, делая ваш корневой вид, и, следовательно, ваше представление поверхности, возможно, содержится внутри Framelayout больше, чем экран.

Это сработало для меня, дайте мне знать.

Вышесказанное сработает, несмотря ни на что. То, что вы хотели бы сделать, это прослушать событие onSurfaceCreated для вашего вида поверхности. Затем, после того как вы запустили вид с камеры и сможете рассчитать размер вашего виджета с предварительным просмотром, вы захотите изменить размер виджета контейнера.

Идея в том, что ваш контейнерный виджет (вероятно, FrameLayout) хочет быть больше экрана. Сам экран ограничен активностью, поэтому сначала установите размер окна,

затем установите размер вашей структуры кадра (она всегда будет уменьшена до максимального размера окон, поэтому установите соответственно).

Я делаю всю эту логику после того, как мой onSurfaceCreated закончен, я запустил предварительный просмотр. Я слушаю это событие в своей деятельности, реализуя небольшой интерфейс, так как мой предварительный просмотр камеры - это отдельный класс.

Работа на всех уровнях API> = 8

1 голос
/ 27 декабря 2012

Вы не можете сделать свой SurfaceView больше, чем экран. Это говорит о том, что есть способы обойти это.

Я обнаружил, что вы можете отрегулировать размер холста в SurfaceView, что позволит изменять масштаб.

public class DrawingThread extends Thread {
private MagnificationView mainPanel;
private SurfaceHolder surfaceHolder;
private boolean run;

public DrawingThread(SurfaceHolder surface, MagnificationView panel){
    surfaceHolder = surface;
    mainPanel = panel;
}

public SurfaceHolder getSurfaceHolder(){
    return surfaceHolder;
}

public void setRunning (boolean run){
    this.run = run;
}

public void run(){
    Canvas c;
    while (run){
        c = null;
        try {
            c = surfaceHolder.lockCanvas(null);
            synchronized (surfaceHolder){
                mainPanel.OnDraw(c);
            }
        } finally {
            if (c != null){
                surfaceHolder.unlockCanvasAndPost(c);
            }
        }
    }
}

}

В классе MagnificationView добавьте метод:

public void OnDraw(Canvas canvas){
    if (canvas!=null){
        canvas.save();
        canvas.scale(scaleX,scaleY);      

        canvas.restore();
    }
}

DrawingThread будет темой, в которой вы начинаете свою деятельность. Кроме того, в вашем классе MagnificationView переопределите OnTouchEvent, чтобы обработать ваше собственное масштабирование (что изменит scaleX и scaleY.

Надеюсь, что это решит вашу проблему

0 голосов
/ 25 ноября 2014

Вот мои TouchSurfaceView onMeasure, которые выполняют масштабирование:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension((int) (width * scaleFactor), (int) (height * scaleFactor));
    }

Это правильно увеличивает и уменьшает масштаб в зависимости от scaleFactor.

Я не проверял это с камерой, но он работает правильно с MediaPlayer (ведет себя как VideoView).

...