summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorWu-cheng Li <wuchengli@google.com>2012-05-15 09:30:27 -0700
committerWu-cheng Li <wuchengli@google.com>2012-05-17 23:24:09 -0700
commitcce0e94832a2c9471a47bc7da6a95d5d76b98f29 (patch)
tree6c2393fbd7a2130d7a0df74347599aabb6bae2de /src
parentec118ff08b7cc515ecab533b42c6e2f0581107d4 (diff)
downloadCamera-cce0e94832a2c9471a47bc7da6a95d5d76b98f29.tar.gz
Do not wait for camera open and preview start in onCreate/onResume.
This reduces the start-up time. bug:6191393 Change-Id: Ica22be12867bccddde484ce0f01222644495e539
Diffstat (limited to 'src')
-rw-r--r--src/com/android/camera/Camera.java268
-rw-r--r--src/com/android/camera/FocusManager.java10
-rw-r--r--src/com/android/camera/ui/IndicatorControlWheel.java10
3 files changed, 170 insertions, 118 deletions
diff --git a/src/com/android/camera/Camera.java b/src/com/android/camera/Camera.java
index fb56d6a6..e8a56a73 100644
--- a/src/com/android/camera/Camera.java
+++ b/src/com/android/camera/Camera.java
@@ -97,6 +97,10 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
private static final int SHOW_TAP_TO_FOCUS_TOAST = 6;
private static final int UPDATE_THUMBNAIL = 7;
private static final int SWITCH_CAMERA = 8;
+ private static final int CAMERA_OPEN_DONE = 9;
+ private static final int START_PREVIEW_DONE = 10;
+ private static final int OPEN_CAMERA_FAIL = 11;
+ private static final int CAMERA_DISABLED = 12;
// The subset of parameters we need to update in setCameraParameters().
private static final int UPDATE_PARAM_INITIALIZE = 1;
@@ -120,7 +124,6 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
private boolean mAeLockSupported;
private boolean mAwbLockSupported;
private boolean mContinousFocusSupported;
- private String[] mDefaultFocusModes;
private MyOrientationEventListener mOrientationListener;
// The degrees of the device rotated clockwise from its natural orientation.
@@ -256,13 +259,33 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
private boolean mQuickCapture;
- ConditionVariable mParametersSetCondition = new ConditionVariable();
- Thread mCameraPreviewThread = new Thread() {
+ CameraStartUpThread mCameraStartUpThread;
+ ConditionVariable mStartPreviewPrerequisiteReady = new ConditionVariable();
+
+ // The purpose is not to block the main thread in onCreate and onResume.
+ private class CameraStartUpThread extends Thread {
@Override
public void run() {
- startPreview();
+ try {
+ mCameraDevice = Util.openCamera(Camera.this, mCameraId);
+ mParameters = mCameraDevice.getParameters();
+ // Wait until all the initialization needed by startPreview are
+ // done.
+ mStartPreviewPrerequisiteReady.block();
+
+ initializeCapabilities();
+ if (mFocusManager == null) initializeFocusManager();
+ setCameraParameters(UPDATE_PARAM_ALL);
+ mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
+ startPreview();
+ mHandler.sendEmptyMessage(START_PREVIEW_DONE);
+ } catch (CameraHardwareException e) {
+ mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL);
+ } catch (CameraDisabledException e) {
+ mHandler.sendEmptyMessage(CAMERA_DISABLED);
+ }
}
- };
+ }
/**
* This Handler is used to post message back onto the main thread of the
@@ -296,7 +319,6 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
// wrong. Framework does not have a callback for this now.
if (Util.getDisplayRotation(Camera.this) != mDisplayRotation) {
setDisplayOrientation();
- mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
}
if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
@@ -318,10 +340,51 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
switchCamera();
break;
}
+
+ case CAMERA_OPEN_DONE: {
+ initializeAfterCameraOpen();
+ break;
+ }
+
+ case START_PREVIEW_DONE: {
+ mCameraStartUpThread = null;
+ setCameraState(IDLE);
+ break;
+ }
+
+ case OPEN_CAMERA_FAIL: {
+ mCameraStartUpThread = null;
+ mOpenCameraFail = true;
+ Util.showErrorAndFinish(Camera.this,
+ R.string.cannot_connect_camera);
+ break;
+ }
+
+ case CAMERA_DISABLED: {
+ mCameraStartUpThread = null;
+ mCameraDisabled = true;
+ Util.showErrorAndFinish(Camera.this,
+ R.string.camera_disabled);
+ break;
+ }
}
}
}
+ private void initializeAfterCameraOpen() {
+ // These depend on camera parameters.
+ setPreviewFrameLayoutAspectRatio();
+ mFocusManager.setPreviewSize(mPreviewFrameLayout.getWidth(),
+ mPreviewFrameLayout.getHeight());
+ if (mIndicatorControlContainer == null) {
+ initializeIndicatorControl();
+ }
+ initializeZoom();
+ updateOnScreenIndicators();
+ startFaceDetection();
+ showTapToFocusToastIfNeeded();
+ }
+
private void resetExposureCompensation() {
String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
CameraSettings.EXPOSURE_DEFAULT_VALUE);
@@ -329,9 +392,6 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
Editor editor = mPreferences.edit();
editor.putString(CameraSettings.KEY_EXPOSURE, "0");
editor.apply();
- if (mIndicatorControlContainer != null) {
- mIndicatorControlContainer.reloadPreferences();
- }
}
}
@@ -359,7 +419,6 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
// Initialize location service.
boolean recordLocation = RecordLocationPreference.get(
mPreferences, mContentResolver);
- initOnScreenIndicator();
mLocationManager.recordLocation(recordLocation);
keepMediaProviderInstance();
@@ -373,10 +432,6 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
mImageSaver = new ImageSaver();
mImageNamer = new ImageNamer();
installIntentFilter();
- initializeZoom();
- updateOnScreenIndicators();
- startFaceDetection();
- showTapToFocusToastIfNeeded();
mFirstTimeInitialized = true;
addIdleHandler();
@@ -425,6 +480,9 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
if (!mIsImageCaptureIntent) {
mModePicker.setCurrentMode(ModePicker.MODE_CAMERA);
}
+ if (mIndicatorControlContainer != null) {
+ mIndicatorControlContainer.reloadPreferences();
+ }
}
private class ZoomChangeListener implements ZoomControl.OnZoomChangedListener {
@@ -491,16 +549,19 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
if ((mModePicker != null) && !Util.pointInView(x, y, mModePicker)) {
mModePicker.dismissModeSelection();
}
- // Check if the popup window is visible.
- View popup = mIndicatorControlContainer.getActiveSettingPopup();
- if (popup != null) {
- // Let popup window, indicator control or preview frame handle the
- // event by themselves. Dismiss the popup window if users touch on
- // other areas.
- if (!Util.pointInView(x, y, popup)
- && !Util.pointInView(x, y, mIndicatorControlContainer)
- && !Util.pointInView(x, y, mPreviewFrameLayout)) {
- mIndicatorControlContainer.dismissSettingPopup();
+ // Check if the popup window is visible. Indicator control can be
+ // null if camera is not opened yet.
+ if (mIndicatorControlContainer != null) {
+ View popup = mIndicatorControlContainer.getActiveSettingPopup();
+ if (popup != null) {
+ // Let popup window, indicator control or preview frame
+ // handle the event by themselves. Dismiss the popup window
+ // if users touch on other areas.
+ if (!Util.pointInView(x, y, popup)
+ && !Util.pointInView(x, y, mIndicatorControlContainer)
+ && !Util.pointInView(x, y, mPreviewFrameLayout)) {
+ mIndicatorControlContainer.dismissSettingPopup();
+ }
}
}
}
@@ -718,6 +779,7 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
if (!mIsImageCaptureIntent) {
startPreview();
+ setCameraState(IDLE);
startFaceDetection();
}
@@ -1054,13 +1116,13 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
private void setCameraState(int state) {
mCameraState = state;
switch (state) {
+ case PREVIEW_STOPPED:
case SNAPSHOT_IN_PROGRESS:
case FOCUSING:
case SWITCHING_CAMERA:
enableCameraControls(false);
break;
case IDLE:
- case PREVIEW_STOPPED:
enableCameraControls(true);
break;
}
@@ -1134,69 +1196,37 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
mContentResolver = getContentResolver();
- /*
- * To reduce startup time, we start the camera open and preview threads.
- * We make sure the preview is started at the end of onCreate.
- */
- CameraOpenThread cameraOpenThread = new CameraOpenThread();
- cameraOpenThread.start();
+ // To reduce startup time, open the camera and start the preview in
+ // another thread.
+ mCameraStartUpThread = new CameraStartUpThread();
+ mCameraStartUpThread.start();
setContentView(R.layout.camera);
+
// Surface texture is from camera screen nail and startPreview needs it.
// This must be done before startPreview.
mIsImageCaptureIntent = isImageCaptureIntent();
createCameraScreenNail(!mIsImageCaptureIntent);
- initializeControlByIntent();
-
- mRotateDialog = new RotateDialogController(this, R.layout.rotate_dialog);
mPreferences.setLocalId(this, mCameraId);
CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
-
- mNumberOfCameras = CameraHolder.instance().getNumberOfCameras();
- mQuickCapture = getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
-
+ mFocusAreaIndicator = (RotateLayout) findViewById(
+ R.id.focus_indicator_rotate_layout);
// we need to reset exposure for the preview
resetExposureCompensation();
+ // Starting the preview needs preferences, camera screen nail, and
+ // focus area indicator.
+ mStartPreviewPrerequisiteReady.open();
- // Make sure camera device is opened.
- try {
- cameraOpenThread.join();
- if (mOpenCameraFail) {
- Util.showErrorAndFinish(this, R.string.cannot_connect_camera);
- return;
- } else if (mCameraDisabled) {
- Util.showErrorAndFinish(this, R.string.camera_disabled);
- return;
- }
- } catch (InterruptedException ex) {
- // ignore
- }
-
- initializeCapabilities();
- mDefaultFocusModes = getResources().getStringArray(
- R.array.pref_camera_focusmode_default_array);
- initializeFocusManager();
+ initializeControlByIntent();
+ mRotateDialog = new RotateDialogController(this, R.layout.rotate_dialog);
+ mNumberOfCameras = CameraHolder.instance().getNumberOfCameras();
+ mQuickCapture = getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
initializeMiscControls();
mLocationManager = new LocationManager(this, this);
-
- mCameraPreviewThread.start();
-
- // Wait until the camera settings are retrieved.
- mParametersSetCondition.block();
-
- // Do this after starting preview because it depends on camera
- // parameters.
- initializeIndicatorControl();
-
- // Make sure preview is started.
- try {
- mCameraPreviewThread.join();
- } catch (InterruptedException ex) {
- // ignore
- }
- mCameraPreviewThread = null;
- mParametersSetCondition = null;
+ initOnScreenIndicator();
+ // Make sure all views are disabled before camera is open.
+ enableCameraControls(false);
}
private void overrideCameraSettings(final String flashMode,
@@ -1232,7 +1262,6 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
// setting the indicator buttons.
mIndicatorControlContainer =
(IndicatorControlContainer) findViewById(R.id.indicator_control);
- if (mIndicatorControlContainer == null) return;
loadCameraPreferences();
final String[] SETTING_KEYS = {
CameraSettings.KEY_FLASH_MODE,
@@ -1344,8 +1373,11 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
// onClick handler for R.id.btn_retake
@OnClickAttr
public void onReviewRetakeClicked(View v) {
+ if (mPaused) return;
+
hidePostCaptureAlert();
startPreview();
+ setCameraState(IDLE);
startFaceDetection();
}
@@ -1444,7 +1476,9 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
@Override
public void onShutterButtonFocus(boolean pressed) {
- if (mPaused || collapseCameraControls() || mCameraState == SNAPSHOT_IN_PROGRESS) return;
+ if (mPaused || collapseCameraControls()
+ || (mCameraState == SNAPSHOT_IN_PROGRESS)
+ || (mCameraState == PREVIEW_STOPPED)) return;
// Do not do focus if there is not enough storage.
if (pressed && !canTakePicture()) return;
@@ -1459,7 +1493,8 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
@Override
public void onShutterButtonClick() {
if (mPaused || collapseCameraControls()
- || mCameraState == SWITCHING_CAMERA) return;
+ || (mCameraState == SWITCHING_CAMERA)
+ || (mCameraState == PREVIEW_STOPPED)) return;
// Do not take the picture if there is not enough storage.
if (mStorageSpace <= Storage.LOW_STORAGE_THRESHOLD) {
@@ -1505,25 +1540,10 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
mZoomValue = 0;
// Start the preview if it is not started.
- if (mCameraState == PREVIEW_STOPPED) {
- CameraOpenThread cameraOpenThread = new CameraOpenThread();
- cameraOpenThread.start();
- try {
- cameraOpenThread.join();
- if (mOpenCameraFail) {
- Util.showErrorAndFinish(this, R.string.cannot_connect_camera);
- return;
- } else if (mCameraDisabled) {
- Util.showErrorAndFinish(this, R.string.camera_disabled);
- return;
- }
- } catch (InterruptedException ex) {
- // ignore
- }
- initializeCapabilities();
+ if (mCameraState == PREVIEW_STOPPED && mCameraStartUpThread == null) {
resetExposureCompensation();
- startPreview();
- startFaceDetection();
+ mCameraStartUpThread = new CameraStartUpThread();
+ mCameraStartUpThread.start();
}
if (!mIsImageCaptureIntent) getLastThumbnail();
@@ -1537,10 +1557,9 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
}
keepScreenOnAwhile();
- if (mCameraState == IDLE) {
- mOnResumeTime = SystemClock.uptimeMillis();
- mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
- }
+ mOnResumeTime = SystemClock.uptimeMillis();
+ mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
+
// Dismiss open menu if exists.
PopupManager.getInstance(this).notifyShowPopup(null);
@@ -1551,11 +1570,26 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
}
}
+ void waitCameraStartUpThread() {
+ try {
+ if (mCameraStartUpThread != null) {
+ mCameraStartUpThread.join();
+ mCameraStartUpThread = null;
+ setCameraState(IDLE);
+ }
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+
@Override
protected void onPause() {
mPaused = true;
super.onPause();
+ // Wait the camera start up thread to finish.
+ waitCameraStartUpThread();
+
stopPreview();
// Close the camera now because other activities may need to use it.
closeCamera();
@@ -1598,6 +1632,11 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
mHandler.removeMessages(FIRST_TIME_INIT);
mHandler.removeMessages(CHECK_DISPLAY_ROTATION);
mHandler.removeMessages(SWITCH_CAMERA);
+ mHandler.removeMessages(CAMERA_OPEN_DONE);
+ mHandler.removeMessages(START_PREVIEW_DONE);
+ mHandler.removeMessages(OPEN_CAMERA_FAIL);
+ mHandler.removeMessages(CAMERA_DISABLED);
+
mPendingSwitchCameraId = -1;
if (mFocusManager != null) mFocusManager.removeMessages();
}
@@ -1635,11 +1674,13 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
private void initializeFocusManager() {
// Create FocusManager object. startPreview needs it.
- mFocusAreaIndicator = (RotateLayout) findViewById(R.id.focus_indicator_rotate_layout);
CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
- mFocusManager = new FocusManager(mPreferences, mDefaultFocusModes,
- mFocusAreaIndicator, mInitialParams, this, mirror);
+ String[] defaultFocusModes = getResources().getStringArray(
+ R.array.pref_camera_focusmode_default_array);
+ mFocusManager = new FocusManager(mPreferences, defaultFocusModes,
+ mFocusAreaIndicator, mInitialParams, this, mirror,
+ getMainLooper());
}
private void initializeMiscControls() {
@@ -1836,8 +1877,6 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
}
private void startPreview() {
- if (mPaused || isFinishing()) return;
-
mFocusManager.resetTouchFocus();
mCameraDevice.setErrorCallback(mErrorCallback);
@@ -1859,9 +1898,6 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
}
setCameraParameters(UPDATE_PARAM_ALL);
- // Inform the main thread to go on the UI initialization.
- if (mParametersSetCondition != null) mParametersSetCondition.open();
-
if (mSurfaceTexture == null) {
Size size = mParameters.getPreviewSize();
if (mCameraDisplayOrientation % 180 == 0) {
@@ -1869,6 +1905,7 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
} else {
mCameraScreenNail.setSize(size.height, size.width);
}
+ notifyScreenNailChanged();
mCameraScreenNail.acquireSurfaceTexture();
mSurfaceTexture = mCameraScreenNail.getSurfaceTexture();
}
@@ -1877,7 +1914,6 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
Log.v(TAG, "startPreview");
mCameraDevice.startPreviewAsync();
- setCameraState(IDLE);
mFocusManager.onPreviewStarted();
if (mSnapshotOnIdle) {
@@ -1955,10 +1991,7 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
CameraSettings.setCameraPictureSize(
pictureSize, supported, mParameters);
}
-
- // Set the preview frame aspect ratio according to the picture size.
Size size = mParameters.getPictureSize();
- mPreviewFrameLayout.setAspectRatio((double) size.width / size.height);
// Set a preview size that is closest to the viewfinder height and has
// the right aspect ratio.
@@ -2109,7 +2142,8 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
private boolean isCameraIdle() {
return (mCameraState == IDLE) ||
- (mFocusManager.isFocusCompleted() && (mCameraState != SWITCHING_CAMERA));
+ ((mFocusManager != null) && mFocusManager.isFocusCompleted()
+ && (mCameraState != SWITCHING_CAMERA));
}
private boolean isImageCaptureIntent() {
@@ -2169,6 +2203,7 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
mLocationManager.recordLocation(recordLocation);
setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
+ setPreviewFrameLayoutAspectRatio();
updateOnScreenIndicators();
}
@@ -2215,6 +2250,7 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
mFocusManager.setMirror(mirror);
mFocusManager.setParameters(mInitialParams);
startPreview();
+ setCameraState(IDLE);
initializeIndicatorControl();
// from onResume
@@ -2324,6 +2360,12 @@ public class Camera extends ActivityBase implements FocusManager.Listener,
// PreviewFrameLayout size has changed.
@Override
public void onSizeChanged(int width, int height) {
- mFocusManager.setPreviewSize(width, height);
+ if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
+ }
+
+ void setPreviewFrameLayoutAspectRatio() {
+ // Set the preview frame aspect ratio according to the picture size.
+ Size size = mParameters.getPictureSize();
+ mPreviewFrameLayout.setAspectRatio((double) size.width / size.height);
}
}
diff --git a/src/com/android/camera/FocusManager.java b/src/com/android/camera/FocusManager.java
index 8eded32e..3d95af2d 100644
--- a/src/com/android/camera/FocusManager.java
+++ b/src/com/android/camera/FocusManager.java
@@ -23,9 +23,9 @@ import android.hardware.Camera.Area;
import android.hardware.Camera.Parameters;
import android.media.MediaActionSound;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.util.Log;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
@@ -109,6 +109,10 @@ public class FocusManager {
}
private class MainHandler extends Handler {
+ public MainHandler(Looper looper) {
+ super(looper);
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -123,8 +127,8 @@ public class FocusManager {
public FocusManager(ComboPreferences preferences, String[] defaultFocusModes,
View focusIndicatorRotate, Parameters parameters, Listener listener,
- boolean mirror) {
- mHandler = new MainHandler();
+ boolean mirror, Looper looper) {
+ mHandler = new MainHandler(looper);
mMatrix = new Matrix();
mPreferences = preferences;
diff --git a/src/com/android/camera/ui/IndicatorControlWheel.java b/src/com/android/camera/ui/IndicatorControlWheel.java
index 066b2858..caa2a7be 100644
--- a/src/com/android/camera/ui/IndicatorControlWheel.java
+++ b/src/com/android/camera/ui/IndicatorControlWheel.java
@@ -250,6 +250,7 @@ public class IndicatorControlWheel extends IndicatorControl implements
// Do not grey out the icons when taking a picture.
setupFilter(mCurrentMode != MODE_CAMERA);
mInitialized = true;
+ requestLayout();
}
private ImageView addImageButton(Context context, int resourceId, boolean rotatable) {
@@ -483,7 +484,10 @@ public class IndicatorControlWheel extends IndicatorControl implements
double endVisibleRadians = mInAnimation
? mEndVisibleRadians[1]
: mEndVisibleRadians[mCurrentLevel];
- if ((!view.isEnabled() && (mCurrentLevel == 0))
+ // Only hide the views in video mode. Hiding the views when taking
+ // a picture is disturbing.
+ if ((!view.isEnabled() && (mCurrentLevel == 0)
+ && (mCurrentMode == MODE_VIDEO))
|| (radian < (startVisibleRadians - HIGHLIGHT_RADIANS / 2))
|| (radian > (endVisibleRadians + HIGHLIGHT_RADIANS / 2))) {
view.setVisibility(View.GONE);
@@ -648,13 +652,15 @@ public class IndicatorControlWheel extends IndicatorControl implements
if (mCurrentMode == MODE_VIDEO) {
mSecondLevelIcon.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
mCloseIcon.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
- requestLayout();
} else {
// We also disable the zoom button during snapshot.
enableZoom(enabled);
}
mSecondLevelIcon.setEnabled(enabled);
mCloseIcon.setEnabled(enabled);
+ // Request layout because the visibility of children may be decided by
+ // isEnabled in onLayout.
+ requestLayout();
}
public void enableZoom(boolean enabled) {