我希望在屏幕上显示2个视图 - 一个是相机预览,在顶部,而另一个将显示图像或谷歌地图 - 并且显示在屏幕的底部。
我希望它们之间有一个类似渐变的过渡 - 所以它们之间没有粗糙的边缘。这有可能产生这样的效果吗?
编辑: 我想要实现的效果应该是这样的(顶部来自相机预览,而底部应该是地图......):
在iOS上我得到了类似的效果,CameraOverlay显示地图并将图层masp设置为渐变:
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = self.map.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithWhite: 1.0 alpha: 0.0] CGColor], (id)[[UIColor colorWithWhite: 1.0 alpha: 1.0] CGColor], nil];
gradient.startPoint = CGPointMake(0.5f, 0.0f);
gradient.endPoint = CGPointMake(0.5f, 0.5f);
self.map.layer.mask = gradient;
答案 0 :(得分:4)
不幸的是,如果两个组件都必须是交互式/实时的,那么您无法在相机预览和地图之间交叉淡入淡出。如前面评论中所述,这与两个小部件的性质和Android合成的局限性有关。
相机预览需要SurfaceView
才能正常工作。来自官方文档:
SurfaceView在其窗口中打孔以允许其表面 显示。视图层次结构将正确处理 与Surface合成SurfaceView的任何兄弟姐妹 通常会出现在它上面。这可用于放置叠加层 比如Surface顶部的按钮,但是请注意它可以 自完全α混合复合材料以来对性能产生影响 每次Surface更改时都会执行。
谷歌地图v2也使用SurfaceView
(看here),所以基本上你有两个SurfaceView
实例一个在另一个上面,你根本无法应用渐变蒙版为了实现你想要的,你无法控制每个小部件的绘制方式:
SurfaceView
接收相机缓冲区并原生渲染SurfaceView
在另一个流程中呈现。此外,非常不鼓励使用SurfaceView
的两个实例,如here所述:
实现表面视图的方式是单独的表面 在其包含的窗口后面创建并且Z-ordered,并且透明 像素被绘制到SurfaceView所在的矩形中 看到背后的表面。我们从不打算允许多个 表面视图。
我认为你唯一的选择就是只选择其中一个进行实时/互动,然后将另一个绘制为静态图像渐变。
的修改
为了进一步验证我之前的陈述,请参阅官方文档about Camera usage的引用:
重要提示:将完全初始化 SurfaceHolder传递给 setPreviewDisplay(SurfaceHolder)。 如果没有表面,相机将无法开始预览。
因此,您必须使用SurfaceView
才能从Camera
获取预览。始终。
只是重复一遍:你无法控制这些像素的呈现方式,因为Camera
使用预览SurfaceHolder
直接写入帧缓冲区。
总之,你有两个完全不透明的 SurfaceView
个实例,你根本无法对其内容应用任何花哨的渲染,所以我认为这样的效果在Android中是不切实际的。< / p>
答案 1 :(得分:0)
这可能,但也许有点复杂。为了简单起见,我将核心代码放在答案中。正如已经指出的那样,你需要两个视图才能做到这一点,一个视图在另一个视图之上。 “lower”应该是SurfaceView,由maps API驱动。 “更高”的应该显示相机图像逐渐消失。
编辑:正如mr_archano指出的那样,API(现在)定义为没有SurfaceView,相机不会发送预览数据。哼哼,这就是进步的本质,然而,这也是可以克服的。代码显示:
核心代码因此在“相机预览”上提供了“相机预览”,并且上部图片被故意扭曲,因此它在顶部清晰可见,在中间褪色并在底部消失。
我可以建议使用此代码的最佳方法是自己实现前四个步骤,看看它是否正常工作,然后添加最后两个步骤,看看它是否有效,然后将关键概念插入另一个,无疑更大更复杂的代码。
前四个步骤:
创建自定义视图以显示到顶部,相机,视图。此类在其下面的任何内容上呈现位图。位图中每个像素的alpha值将决定下部视图的通过量。
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知道传入图像数据的大小。第二个接收预览图像数据,将其转换为位图,沿着途径扭曲它以便可见性和演示alpha淡入淡出。
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's 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();
}
关于第二个例程的说明:
该代码显示了基本思想。然后进入下一阶段:
将相机的Surface视图设置得足够小,以隐藏在顶部视图的非褪色部分后面。即,将android:layout_height
更改为60dp
。
设置中间SurfaceView以接收地图信息。