カメラのプレビューの上に動画を表示するアプリを作成しています。そして、私が行っている方法は、2 つの SurfaceView を追加することです。1 つはカメラ プレビューを保持し、もう 1 つは動画を保持し、メイン アクティビティ内のフレームレイアウトに追加します。したがって、基本的に 3 つの public クラスと、アニメーションを制御するための動画クラス内に 1 つの内部スレッド クラスがあります。
アプリの起動時に問題なく動作しました-カメラがプレビュー中で、画像が動いています。しかし、その後、ホーム画面に移動するか、写真をクリックして別のアクティビティにリダイレクトしてアクティビティを一時停止し、再開すると、カメラのプレビューが真っ暗になります。奇妙な部分は、電話を別のモード (横/縦) に回転させると、正常に戻ることです。
カメラが再開しないという投稿をいくつか読みましたが、解決策はすべてカメラを開くことでした。調べた後、私の問題はカメラインスタンスに関するものではないと確信しています。実際、ホーム画面に移動してアクティビティを一時停止すると、再開するとカメラが一瞬表示されてからブラックアウトします。
OnPause() でレイアウトからすべてのビューを削除したり、ビューを追加するときにインデックス番号を指定したりするなど、あらゆる種類のことを試してきました。しかし、少し進歩した唯一の方法は、次のブロックでキャンバスロックをコメントアウトしたときでした. ロックがなければ、写真はランダムに動きますが、カメラは再開できます。実際、スレッドに関するすべてを省略して静止画を表示するだけでも、カメラは問題なく動作します。ここのスレッドに何か問題があると感じていますが、わかりませんでした。
スレッドの run メソッドは次のとおりです。
public void run() {
Canvas canvas;
while (isRunning) { //When setRunning(false) occurs, isRunning is
canvas = null; //set to false and loop ends, stopping thread
try {
canvas = surfaceHolder.lockCanvas(null); //Lock
synchronized (surfaceHolder) {
//Insert methods to modify positions of items in onDraw()
animation();
postInvalidate();
}
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas); //Unlock
}
}
}
}
スレッドを開始する部分は次のとおりです。
public void surfaceCreated(SurfaceHolder arg0) {
setWillNotDraw(false); //Allows us to use invalidate() to call onDraw()
thread = new BubbleThread(getHolder(), this); //Start the thread that
thread.setRunning(true); //will make calls to
thread.start(); //onDraw()
}
スレッドを終了する部分は次のとおりです。
public void surfaceDestroyed(SurfaceHolder arg0) {
try {
thread.setRunning(false); //Tells thread to stop
thread.join(); //Removes thread from mem.
} catch (InterruptedException e) {}
}
[更新] 主な活動コード:
public class MainActivity extends Activity {
... // Declarations
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/* Adjust app settings */
...
//Load();
}
public void Load(){
/* Try to get the camera */
Camera c = getCameraInstance();
/* If the camera was received, create the app */
if (c != null){
// Create the parent layout to layer the
// camera preview and bubble layer
parentLayout = new FrameLayout(this);
parentLayout.setLayoutParams(new LayoutParams(
LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
// Create a new camera view and add it to the layout
cameraPreview = new CameraPreview(this, c);
parentLayout.addView(cameraPreview, 0);
// Create a new draw view and add it to the layout
bubbleLayer = new BubbleLayer(this);
parentLayout.addView(bubbleLayer, 1);
// Set the layout as the apps content view
setContentView(parentLayout);
}
/* If the camera was not received, close the app */
else {
Toast toast = Toast.makeText(getApplicationContext(),
"Unable to find camera. Closing.", Toast.LENGTH_SHORT);
toast.show();
finish();
}
}
/** A safe way to get an instance of the Camera object. */
/** This method is strait from the Android API */
public static Camera getCameraInstance(){
Camera c = null;
try {
// Attempt to get a Camera instance
c = Camera.open();
}
catch (Exception e){
// Camera is not available (in use or does not exist)
e.printStackTrace();
}
return c;
}
/* Override the onPause method so that we
* can release the camera when the app is closing.
*/
@Override
protected void onPause() {
super.onPause();
if (cameraPreview != null){
cameraPreview.onPause();
cameraPreview = null;
}
}
/* We call Load in our Resume method, because
* the app will close if we call it in onCreate
*/
@Override
protected void onResume(){
super.onResume();
Load();
}
}
[/アップデート]
[UPDATE2] カメラ プレビュー コード:
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
... // Declarations
public CameraPreview(Context context, Camera camera) {
super(context);
this.context = context;
mCamera = camera;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
Camera.Parameters parameters = mCamera.getParameters();
Size bestSize = getBestSize(parameters.getSupportedPreviewSizes(),
width,height);
parameters.setPreviewSize(bestSize.width, bestSize.height);
mCamera.setParameters(parameters);
// 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
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
setCameraDisplayOrientation();
mCamera.startPreview();
} catch (Exception e){
Log.d("CameraView", "Error starting camera preview: "
+ e.getMessage());
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the
// camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d("CameraView", "Error setting camera preview: "
+ e.getMessage());
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
/* Find the best size for camera */
private Size getBestSize(List<Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.05;
double targetRatio = (double) w / h;
if (sizes == null) return null;
Size bestSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
bestSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (bestSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
bestSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return bestSize;
}
private void setCameraDisplayOrientation() {
if (mCamera == null) return;
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(0, info);
WindowManager winManager = (WindowManager)
context.getSystemService(Context.WINDOW_SERVICE);
int rotation = winManager.getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0: degrees = 0; break;
case Surface.ROTATION_90: degrees = 90; break;
case Surface.ROTATION_180: degrees = 180; break;
case Surface.ROTATION_270: degrees = 270; break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
}
else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
mCamera.setDisplayOrientation(result);
}
public void onPause() {
if (mCamera == null) return;
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
[/UPDATE2]
もう 1 つわかったことは、アクティビティを一時停止すると、ロック解除がほとんど実行されないことでした。それが実行されたとき、カメラはまだ戻ってきませんでしたが、thread.join()が実行されたため、この動作は私には奇妙に思えたので、finallyブロックも実行されるべきだったと思います。
申し訳ありませんが、私の質問をより少ない言葉で説明できませんでしたが、何か手がかりを残してください. 前もって感謝します!