在Android4.4上面,Camera和Gallery2的关系在代码上是分开的。但是在在实际代码上,Camera和Gallery的关系很密切。
下面以实际开发过程中遇到的一个问题来展开分析:
需求:客户要求做假对焦,进入到preview界面的时候,自动对焦一次。
我的做法是在PhotoActor.java中的onPreviewStartDone预览完成的函数中模拟onSingleTapUp触摸点击效果的自动对焦过程。
mCamera.getFocusManager().onSingleTapUp(mCamera.getPreviewFrameWidth() / 2, mCamera.getPreviewFrameHeight() / 2);
FocusManager.java
public void onSingleTapUp(int x, int y) {
Log.i(TAG,"onSingleTapUp x = " + x + " y = " + y);
if (!mInitialized || mState == STATE_FOCUSING_SNAP_ON_FINISH || mState == STATE_UNKNOWN) { return; }
………………
// Disable "center" rule because we no longer want to put it in the center.
int[] rules = p.getRules();
rules[RelativeLayout.CENTER_IN_PARENT] = 0;
mFocusIndicatorRotateLayout.requestLayout();
// Stop face detection because we want to specify focus and metering area.
mListener.stopFaceDetection();
// Set the focus area and metering area.
mListener.setFocusParameters();
if (mFocusAreaSupported) {
autoFocus();
} else { // Just show the indicator in all other cases.
updateFocusUI();
// Reset the metering area in 3 seconds.
mHandler.removeMessages(RESET_TOUCH_FOCUS);
mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY);
//add
mHandler.removeMessages(MSG_SHOW_FOCUS_END);
mHandler.sendEmptyMessageDelayed(MSG_SHOW_FOCUS_END, MSG_SHOW_FOCUS_END_DELAY);
}
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case RESET_TOUCH_FOCUS:
cancelAutoFocus();
mListener.startFaceDetection();
break;
case MSG_SHOW_FOCUS_END:
mFocusIndicatorRotateLayout.showSuccess(true);
mListener.playSound(MediaActionSound.FOCUS_COMPLETE);
break;
default:
break;
}
}
这样,简单的功能是实现了,但是首先遇到的第一个问题是:由于Preview的SurfaceView界面是先绘制出来,然后取景的,取景需要时间,这样对焦就经常发生在SurfaceView还是灰色界面的时候就开始对焦,特别是在切换前后摄像头的时候特别明显。真实对焦是不会发生这样的问题的,因为它是根据Sensor捕获到的信息上传后产生效果的,取景没完成是不会对焦的。假对焦那就采取野蛮的办法,增加一个延时,演变成下面的代码:
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mCamera.getFocusManager().onSingleTapUp(mCamera.getPreviewFrameWidth() / 2, mCamera.getPreviewFrameHeight() / 2);
}
}, 1000);
这样基本上能接受了。
很快,测试找到了第二个问题点:Camera Activity启动后,进入到图片预览界面,然后OnPause,再OnResume的时候,进入到的还是图片预览界面,但是这时候会有对焦行为。通过log分析,OnResume的时候也是有startPreview行为的(不管你是在preview界面,还是在图片预览界面),我的理解是,这时候不管在哪个界面,Preview都已经需要Init,只是在图片预览时,Camera preview的行为是在另外开辟的一个缓存中进行的,只是没有在当前屏幕显示出来而已。
自然而然,我们就必须增加一个判断条件来判断Camera Preview是否在当先的显示画面。很快在PhotoPage.java就找到了相关的代码。重点问题来的,我要怎么在Camera和Gallery之间来传递消息呢?
Camera.java和GalleryActivity.java都集成自AbstractGalleryActivity.java,通过分析在ActivityBase.java中找到AppBridge,顾名思义,可以看出来,这就是桥梁。在AppBridge抽象类中有一个接口:
public interface Server {
// Set the camera frame relative to GLRootView.
public void setCameraRelativeFrame(Rect frame);
// Switch to the previous or next picture using the capture animation.
// The offset is -1 to switch to the previous picture, 1 to switch to
// the next picture.
public boolean switchWithCaptureAnimation(int offset);
// Enable or disable the swiping gestures (the default is enabled).
public void setSwipingEnabled(boolean enabled);
// Notify that the ScreenNail is changed.
public void notifyScreenNailChanged();
// Add a new media item to the secure album.
public void addSecureAlbumItem(boolean isVideo, int id);
/// M: for camera gesture feature @
public Listener setGestureListener(Listener listener);
/// @}
// M: Notify PhotoView to render FullPicture only to boost performance
public void renderFullPictureOnly(boolean fullPictureOnly);
public void setOritationTag(boolean lock, int oritationNum);
///M:add for set camera path
public void setCameraPath(String setPath);
public int getSecureAlbumCount();
public boolean isCameraScreen(int offset);
}
而PhotoPage实现了该接口:
public class PhotoPage extends ActivityState implements
PhotoView.Listener, OrientationManager.Listener, ShareActionProvider.OnShareTargetSelectedListener,AppBridge.Server,
PhotoPageBottomControls.Delegate, GalleryActionBar.OnAlbumModeSelectedListener,
AbstractGalleryActivity.GyroPositionListener,
AbstractGalleryActivity.HotknotCompleteListener{
……
public boolean isCameraScreen(int offset) {
return mModel.isCamera(offset);
}
……
}
找到问题点,实现起来就非常简单了,中间还有两层的封装就不多说了,最终的代码变为:
if(mCamera.isCameraScreen(0)) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mCamera.getFocusManager().onSingleTapUp(mCamera.getPreviewFrameWidth() / 2, mCamera.getPreviewFrameHeight() / 2);
}
}, 1000);
}
整个过程还是花了我1.5天的时间,主要是没有找到好的可以查看整个app UML结构的工具,所以一点点找起来麻烦,浪费时间,而且代码量大,很容易就迷失了。
有好的工具,请留言推荐,谢谢!