Как я могу сделать 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();
}
}
ОБНОВЛЕНИЕ: Основываясь на ответе @Pulkit Sethi, можно растянуть / увеличить SurfaceView по вертикали, но не по горизонтали. Чтобы увеличить SurfaceView по вертикали, просто замените display.getHeight()/2 на display.getHeight()*2 выше. Также обратите внимание, что изменение ширины не приводит к увеличению по горизонтали, ни в моем коде, ни в Pulkit.
4 ответа
//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>
Вы не можете сделать свой 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.
Надеюсь, что это решает вашу проблему
Что вы можете сделать, это получить окно и установить его высоту:
getWindow (). setLayout (1000, 1000);
Это делает ваше окно больше, чем экран, делающий ваш корневой вид, и, следовательно, ваш вид поверхности, вероятно, содержащийся внутри Framelayout больше, чем экран.
Это сработало для меня, дайте мне знать.
Вышеупомянутое работало бы несмотря ни на что. То, что вы хотели бы сделать, это прослушать событие onSurfaceCreated для вашего представления поверхности. Затем, после того, как вы запустили вид с камеры и сможете рассчитать размер вашего виджета с предварительным просмотром, вы захотите изменить размер виджета контейнера.
Идея в том, что ваш контейнерный виджет (вероятно, FrameLayout) хочет стать больше, чем screen. Сам экран ограничен активностью, поэтому сначала установите размер окна,
затем установите размер вашей структуры кадра (она всегда будет уменьшена до максимального размера окон, поэтому установите соответственно).
Я делаю всю эту логику после того, как мой onSurfaceCreated закончил, я начал предварительный просмотр. Я слушаю это событие в своей деятельности, реализуя небольшой интерфейс, так как мой предварительный просмотр камеры - это отдельный класс.
Работаем на всех уровнях API>= 8.
Вот мой TouchSurfaceView
"s 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
).