Как я могу сделать SurfaceView больше экрана?

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

Как я могу реализовать цифровой зум? (Нет, я не могу использовать 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();
    }
}

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

Ответ 1

//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>

Ответ 2

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

Я обнаружил, что вы можете настроить размер холста в 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.

Надежда Это решает вашу проблему

Ответ 3

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

getWindow(). setLayout (1000, 1000);

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

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

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

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

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

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

Работа со всем уровнем API >= 8.

Ответ 4

Здесь my 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).