Ответ 1
Это возможно, но, возможно, немного сложно. Чтобы это было просто, я поставил основной код для достижения этого в ответе. Как уже отмечалось, для этого вам нужно два вида, один "поверх другого". "Нижний" должен быть SurfaceView, управляемый API карт. "Высший" должен показать, что изображение камеры исчезло над ним.
EDIT: Как указывает mr_archano, API (теперь) определен таким образом, что без SurfaceView камера не будет отправлять данные предварительного просмотра. Ho hum, такова природа прогресса, но это также невозможно преодолеть.
В коде представлен:
- "Нижний" SurfaceView управляется непосредственно механизмом предварительного просмотра камеры.
- "Средний" SurfaceView предназначен для API MAPS.
- "Верхний" вид - это то, где данные камеры отображаются для достижения желаемого эффекта.
Таким образом, основной код дает "предварительный просмотр камеры" над "предварительным просмотром камеры", а верхняя часть изображения была преднамеренно искажена, поэтому она четко видна в верхней части, затухающей в середине и снижающейся внизу.
Могу ли я предложить, чтобы лучший способ использования этого кода состоял в том, чтобы реализовать эти первые четыре шага самостоятельно и увидеть, как он работает, а затем добавить два последних шага и увидеть, что они работают, прежде чем вставлять ключевые понятия в другие, несомненно более сложный и сложный фрагмент кода.
Первые четыре шага:
-
Создайте собственное представление для отображения сверху, камеры, просмотра. Этот класс отображает растровое изображение над всем, что находится под ним. Значение альфа в каждом пикселе в растровом изображении определит, сколько из нижнего представления пропускается.
public class CameraOverlayView extends View { private Paint paint; private Size incomingSize; private Bitmap bitmap = null; public CameraOverlayView(Context context) { super(context); init(); } public CameraOverlayView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { paint = new Paint(); paint.setStyle(Style.FILL_AND_STROKE); paint.setColor(0xffffffff); paint.setTextSize((float) 20.0); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int width = canvas.getWidth(); int height = canvas.getHeight(); canvas.drawBitmap(bitmap, 0.0f, 0.0f, paint); } }
-
Поместите три представления в кадр, и все они будут установлены в
fill_parent
в обоих направлениях. Первый будет "снизу" (SurfaceView, так что просмотр камеры работает). Второй "посередине" (вид поверхности для Карт или что-то еще). Третий "сверху" (вид для исчезнувшего изображения камеры).<SurfaceView android:id="@+id/beneathSurfaceView" android:layout_width="fill_parent" android:layout_height="fill_parent" /> <SurfaceView android:id="@+id/middleSurfaceView" android:layout_width="fill_parent" android:layout_height="fill_parent" /> <com.blah.blah.blah.CameraOverlayView android:id="@+id/aboveCameraView" android:layout_width="fill_parent" android:layout_height="fill_parent" />
-
Урезанная основная активность, которая будет настраивать камеру, и отправить автоматическое изображение предварительного просмотра в (снизу) SurfaceView и данные предварительного просмотра в процедуру обработки. Он устанавливает обратный вызов для захвата данных предварительного просмотра. Эти два запускаются параллельно.
public class CameraOverlay extends Activity implements SurfaceHolder.Callback2 { private SurfaceView backSV; private CameraOverlayView cameraV; private SurfaceHolder cameraH; private Camera camera=null; private Camera.PreviewCallback cameraCPCB; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.camera_overlay); // Get the two views backSV = (SurfaceView) findViewById(R.id.beneathSurfaceView); cameraV = (CameraOverlayView) findViewById(R.id.aboveCameraView); // BACK: Putting the camera on the back SV (replace with whatever is driving that SV) cameraH = backSV.getHolder(); cameraH.addCallback(this); // FRONT: For getting the data from the camera (for the front view) cameraCPCB = new Camera.PreviewCallback () { @Override public void onPreviewFrame(byte[] data, Camera camera) { cameraV.acceptCameraData(data, camera); } }; } // Making the camera run and stop with state changes @Override public void onResume() { super.onResume(); camera = Camera.open(); camera.startPreview(); } @Override public void onPause() { super.onPause(); camera.setPreviewCallback(null); camera.stopPreview(); camera.release(); camera=null; } private void cameraImageToViewOn() { // FRONT cameraV.setIncomingSize(camera.getParameters().getPreviewSize()); camera.setPreviewCallback(cameraCPCB); } private void cameraImageToViewOff() { // FRONT camera.setPreviewCallback(null); } // The callbacks which mean that the Camera does stuff ... @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // If your preview can change or rotate, take care of those events here. // Make sure to stop the preview before resizing or reformatting it. if (holder == null) return; // stop preview before making changes try { cameraImageToViewOff(); // FRONT camera.stopPreview(); } catch (Exception e){ // ignore: tried to stop a non-existent preview } // set preview size and make any resize, rotate or reformatting changes here // start preview with new settings try { camera.setPreviewDisplay(holder); //BACK camera.startPreview(); cameraImageToViewOn(); // FRONT } catch (Exception e){ } } @Override public void surfaceCreated(SurfaceHolder holder) { try { camera.setPreviewDisplay(holder); //BACK camera.startPreview(); cameraImageToViewOn(); // FRONT } catch (IOException e) { } } @Override public void surfaceDestroyed(SurfaceHolder holder) { } @Override public void surfaceRedrawNeeded(SurfaceHolder holder) { } }
Некоторые вещи отсутствуют:
- Обеспечение правильной ориентации изображения камеры
- Обеспечение оптимального размера изображения предварительного просмотра камеры
-
Теперь добавьте две функции в представление, созданные на первом шаге. Первое гарантирует, что View знает размер входящих изображений. Второй получает данные предварительного просмотра, превращает его в растровое изображение, искажая его по пути как для видимости, так и для демонстрации альфа-затухания.
public void setIncomingSize(Size size) { incomingSize = size; if (bitmap != null) bitmap.recycle(); bitmap = Bitmap.createBitmap(size.width, size.height, Bitmap.Config.ARGB_8888); } public void acceptCameraData(byte[] data, Camera camera) { int width = incomingSize.width; int height = incomingSize.height; // the bitmap we want to fill with the image int numPixels = width*height; // the buffer we fill up which we then fill the bitmap with IntBuffer intBuffer = IntBuffer.allocate(width*height); // If you're reusing a buffer, next line imperative to refill from the start, - if not good practice intBuffer.position(0); // Get each pixel, one at a time int Y; int xby2, yby2; int R, G, B, alpha; float U, V, Yf; for (int y = 0; y < height; y++) { // Set the transparency based on how far down the image we are: if (y<200) alpha = 255; // This image only at the top else if (y<455) alpha = 455-y; // Fade over the next 255 lines else alpha = 0; // nothing after that // For speed sake, you should probably break out of this loop once alpha is zero ... for (int x = 0; x < width; x++) { // Get the Y value, stored in the first block of data // The logical "AND 0xff" is needed to deal with the signed issue Y = data[y*width + x] & 0xff; // Get U and V values, stored after Y values, one per 2x2 block // of pixels, interleaved. Prepare them as floats with correct range // ready for calculation later. xby2 = x/2; yby2 = y/2; U = (float)(data[numPixels + 2*xby2 + yby2*width] & 0xff) - 128.0f; V = (float)(data[numPixels + 2*xby2 + 1 + yby2*width] & 0xff) - 128.0f; // Do the YUV -> RGB conversion Yf = 1.164f*((float)Y) - 16.0f; R = (int)(Yf + 1.596f*V); G = 2*(int)(Yf - 0.813f*V - 0.391f*U); // Distorted to show effect B = (int)(Yf + 2.018f*U); // Clip rgb values to 0-255 R = R < 0 ? 0 : R > 255 ? 255 : R; G = G < 0 ? 0 : G > 255 ? 255 : G; B = B < 0 ? 0 : B > 255 ? 255 : B; // Put that pixel in the buffer intBuffer.put(Color.argb(alpha, R, G, B)); } } // Get buffer ready to be read intBuffer.flip(); // Push the pixel information from the buffer onto the bitmap. bitmap.copyPixelsFromBuffer(intBuffer); this.invalidate(); }
Примечания по второй процедуре:
- Он принимает формат входящей камеры NV21. Другие могут быть доступны, этот гарантированно будет там, даже если это боль. См. Преобразование YUV- > RGB (обработка изображений) → YUV во время onPreviewFrame в android?.
- Возможно, это может быть сделано быстрее или лучше, с более поздними версиями Android и некоторой оптимизацией кода.
Этот код показывает основную идею. Затем перейдите к следующему этапу:
-
Установите вид поверхности камеры, чтобы быть достаточно маленьким, чтобы спрятаться за не выцветшим участком верхнего вида. т.е. измените
android:layout_height
на это, скажем, на60dp
. -
Установите средний SurfaceView для получения информации о карте.