Camera 中的配置更改后,返回到具有空 ImageView 的应用程序
Posted
技术标签:
【中文标题】Camera 中的配置更改后,返回到具有空 ImageView 的应用程序【英文标题】:After configuration change in Camera, returns to app with empty ImageView 【发布时间】:2015-06-09 19:44:44 【问题描述】:我没有在网上找到任何可以解决这个特定问题的东西。我有一个使用 android 相机应用程序拍摄照片或视频的活动。然后它从相机中返回图像或视频,并将其显示在视图中。当我处于相同配置时,我的视频和图像、捕获和显示都可以正常工作。 但是,如果我从我的应用程序启动相机,比如纵向,那么当我在相机应用程序中时,我决定将其更改为横向,然后一旦我从相机应用程序返回,我的图像显示为空白 @987654321 @. 如果我保持相同的配置,它可以正常工作。就像我从纵向开始,并在纵向使用相机应用程序时一样,然后我的ImageView
在返回时显示图像。由于我无法为相机应用编写代码,因此不知道如何处理。
但是,如果我在使用相机视频的过程中改变方向,我的视频没有问题。无论我在应用程序或相机应用程序中的哪个方向,它返回到我的应用程序时都显示良好。
但是为什么图片有问题呢?我试图在onSaveInstanceState
和onRestoreInstanceState
中保存状态,但这不会影响任何事情。当我记录它们时,我在这些方法中的所有变量都得到了空值(对于视频也是如此)。我想是因为它可能与我的 Intent 数据有关吗?但这只是一个猜测。我查看了Intent
并没有发现任何遗漏。图像意图与视频意图有点不同,所以也许我遗漏了一些东西。
我确实有 2 个不同的 xml
文件用于纵向和横向布局,但它们是相同的并且位于正确的文件夹中。当我进行配置更改时,Android 会自动选择它们。
如果有人有提示,非常感谢。
更新:
我尝试在清单中添加一行代码:android:configChanges="keyboardHidden|orientation|screenSize"
我覆盖了onConfigurationChanged()
,但现在我的图像返回为黑屏。这两个布局文件都在我的layout
文件夹中。
@Override
public void onConfigurationChanged(Configuration newConfig)
super.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
setContentView(R.layout.activity_make_photo_video_land);
else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT)
setContentView(R.layout.activity_make_photo_video);
更新 2:
我撤消了我在 UPDATE 1 中尝试的所有操作。因为那给了我一个黑屏和其他无法修复的错误。
这次我在onCreate
中添加了一个配置选择器来手动改变视图:
// Checks the orientation of the screen
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
setContentView(R.layout.activity_make_photo_video_land);
mImageView = (ImageView) findViewById(R.id.taken_photo_land);
else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
setContentView(R.layout.activity_make_photo_video);
mImageView = (ImageView) findViewById(R.id.taken_photo);
经过更多的记录,我意识到当我在相机应用程序中更改配置时,以下方法按此顺序执行(从第一个活动开始列出):
onCreate()
onSavedInstanceState()
- 我离开我的应用程序去摄像头
onCreate()
- 我从带有照片的应用返回,mImageBitmap 上的日志为空。
onRestoreInstanceState()
inActivityResult()
- 然后它会处理从相机拍摄的照片。
我在onSavedInstanceState()
中唯一坚持的是mCurrentPhotoPath
,这是在inActivityResult()
中运行setPic()
方法所必需的。
在onSavedInstanceState()
:
outState.putString("FILE_PATH", mCurrentPhotoPath);
在onCreate()
:
if (savedInstanceState != null)
mCurrentPhotoPath = savedInstanceState.getString("FILE_PATH");
这个过程很好,并且位图值的日志不为空。那为什么图片还没有发布呢??
MakePhotoVideo.java
package org.azurespot.makecute;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.VideoView;
import org.azurespot.R;
import org.azurespot.cutecollection.CuteCollection;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Random;
public class MakePhotoVideo extends ActionBarActivity
private static final int ACTION_TAKE_PHOTO = 1;
private static final int ACTION_TAKE_VIDEO = 2;
private static final String BITMAP_STORAGE_KEY = "viewbitmap";
private static final String IMAGEVIEW_VISIBILITY_STORAGE_KEY = "imageviewvisibility";
private ImageView mImageView;
private Bitmap mImageBitmap;
private static final String VIDEO_STORAGE_KEY = "viewvideo";
private static final String VIDEOVIEW_VISIBILITY_STORAGE_KEY = "videoviewvisibility";
private VideoView mVideoView;
private Uri mVideoUri;
private File fileVideo;
private String mCurrentPhotoPath;
String videoPath;
private int position = 0;
private static final String JPEG_FILE_PREFIX = "IMG_";
private static final String JPEG_FILE_SUFFIX = ".jpg";
private PhotoStorageDirFactory mPhotoStorageDirFactory = null;
/* Photo album for this application */
private String getAlbumName()
return getString(R.string.album_name);
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_make_photo_video);
mImageView = (ImageView) findViewById(R.id.taken_photo);
mVideoView = (VideoView) findViewById(R.id.video_view);
mVideoView.setVisibility(View.INVISIBLE);
mImageView.setSaveEnabled(true);
Button photoBtn = (Button) findViewById(R.id.click);
setBtnListenerOrDisable(photoBtn, mTakePicOnClickListener, MediaStore.ACTION_IMAGE_CAPTURE);
Button videoBtn = (Button) findViewById(R.id.record_video);
setBtnListenerOrDisable(videoBtn,mTakeVidOnClickListener, MediaStore.ACTION_VIDEO_CAPTURE);
mPhotoStorageDirFactory = new BasePhotoDirFactory();
// Shows the up carat near app icon in ActionBar
getSupportActionBar().setDisplayUseLogoEnabled(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
public void viewCollection(View v)
// finishes/restarts the activity so the unsaved video does not corrupt
Intent intent = getIntent();
finish();
startActivity(intent);
// goes to Cute Collection activity
Intent i = new Intent(this, CuteCollection.class);
startActivity(i);
private File getAlbumDir()
File storageDir = null;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
storageDir = mPhotoStorageDirFactory.getAlbumStorageDir(getAlbumName());
if (storageDir != null)
if (! storageDir.mkdirs())
if (! storageDir.exists())
Log.d("Camera", "failed to create directory");
return null;
else
Log.v(getString(R.string.app_name), "External storage is not mounted READ/WRITE.");
return storageDir;
private File createImageFile() throws IOException
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = JPEG_FILE_PREFIX + timeStamp + "_";
File albumF = getAlbumDir();
File imageF = File.createTempFile(imageFileName, JPEG_FILE_SUFFIX, albumF);
return imageF;
private File setUpPhotoFile() throws IOException
File f = createImageFile();
mCurrentPhotoPath = f.getAbsolutePath();
return f;
private void setPic()
/* There isn't enough memory to open up more than a couple camera photos */
/* So pre-scale the target bitmap into which the file is decoded */
/* Get the size of the ImageView */
int targetW = mImageView.getWidth();
int targetH = mImageView.getHeight();
/* Get the size of the image */
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
/* Figure out which way needs to be reduced less */
int scaleFactor = 1;
if ((targetW > 0) || (targetH > 0))
scaleFactor = Math.min(photoW/targetW, photoH/targetH);
/* Set bitmap options to scale the image decode target */
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
/* Decode the JPEG file into a Bitmap */
mImageBitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
mImageBitmap = rotateBitmap(mImageBitmap, 90);
savePhoto(mImageBitmap);
/* Associate the Bitmap to the ImageView, make sure the VideoView
* is cleared to replace with ImageView */
mImageView.setImageBitmap(mImageBitmap);
Log.d("TAG", "Value of mImageBitmap inside setPic(): " + mImageBitmap);
mVideoUri = null;
mImageView.setVisibility(View.VISIBLE);
mVideoView.setVisibility(View.INVISIBLE);
// save your photo to SD card
private void savePhoto(final Bitmap bitmapPhoto)
// set OnClickListener to save the photo
mImageView.setOnClickListener(new View.OnClickListener()
public void onClick(View v)
boolean success = false;
File photoDir = new File(Environment.getExternalStoragePublicDirectory
(Environment.DIRECTORY_PICTURES) + "/Cute Photos");
photoDir.mkdirs();
Random generator = new Random();
int n = 10000;
n = generator.nextInt(n);
String photoName = "Photo"+ n +".jpg";
File filePhoto = new File (photoDir, photoName);
// if (filePhoto.exists ()) filePhoto.delete ();
try
FileOutputStream out = new FileOutputStream(filePhoto);
bitmapPhoto.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
success = true;
catch (Exception e)
e.printStackTrace();
if (success)
Toast toast = Toast.makeText(getApplicationContext(), "Cute photo saved!",
Toast.LENGTH_LONG);
LinearLayout toastLayout = (LinearLayout) toast.getView();
toastLayout.setBackgroundColor(getResources().getColor(R.color.toast_color));
TextView toastTV = (TextView) toastLayout.getChildAt(0);
toastTV.setTextSize(30);
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 80);
toast.show();
else
Toast.makeText(getApplicationContext(),
"Error during image saving", Toast.LENGTH_SHORT).show();
);
// save your video to SD card
protected void saveVideo(final Uri uriVideo)
// click the video to save it
mVideoView.setOnTouchListener(new View.OnTouchListener()
public boolean onTouch(View v, MotionEvent event)
boolean success = false;
if(event.getAction() == MotionEvent.ACTION_UP)
try
// make the directory
File vidDir = new File(android.os.Environment.getExternalStoragePublicDirectory
(Environment.DIRECTORY_MOVIES) + File.separator + "Cute Videos");
vidDir.mkdirs();
// create unique identifier
Random generator = new Random();
int n = 100;
n = generator.nextInt(n);
// create file name
String videoName = "Video" + n + ".mp4";
fileVideo = new File(vidDir.getAbsolutePath(), videoName);
videoPath = fileVideo.getAbsolutePath();
Log.d("TAG", "Value of videoPath:" + videoPath);
fileVideo.setWritable(true, false);
OutputStream out = new FileOutputStream(fileVideo);
InputStream in = getContentResolver().openInputStream(uriVideo);
byte buffer[] = new byte[1024];
int length = 0;
while ((length = in.read(buffer)) > 0)
out.write(buffer, 0, length);
out.close();
in.close();
success = true;
catch (Exception e)
e.printStackTrace();
if (success)
Toast toast = Toast.makeText(getApplicationContext(), "Cute video saved!",
Toast.LENGTH_SHORT);
LinearLayout toastLayout = (LinearLayout) toast.getView();
toastLayout.setBackgroundColor(getResources().getColor(R.color.toast_color));
TextView toastTV = (TextView) toastLayout.getChildAt(0);
toastTV.setTextSize(30);
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 80);
toast.show();
else
Toast.makeText(getApplicationContext(),
"Error during video saving", Toast.LENGTH_SHORT).show();
return true;
);
public Bitmap rotateBitmap(Bitmap source, int angle)
Matrix matrix = new Matrix();
matrix.set(matrix);
matrix.setRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(),
source.getHeight(), matrix, false);
private void galleryAddPic()
Intent mediaScanIntent = new Intent("android.intent.action.MEDIA_SCANNER_SCAN_FILE");
File f = new File(mCurrentPhotoPath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
private void dispatchTakePictureIntent(int actionCode)
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
switch(actionCode)
case ACTION_TAKE_PHOTO:
File f;
try
f = setUpPhotoFile();
mCurrentPhotoPath = f.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
catch (IOException e)
e.printStackTrace();
f = null;
mCurrentPhotoPath = null;
break;
default:
break;
// switch
startActivityForResult(takePictureIntent, actionCode);
// Captures video from Android camera component
protected void dispatchTakeVideoIntent()
Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
if (takeVideoIntent.resolveActivity(getPackageManager()) != null)
// set the video image quality to high
takeVideoIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
startActivityForResult(takeVideoIntent, ACTION_TAKE_VIDEO);
private void handleCameraPhoto()
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
Log.d("TAG", "Value of mCurrentPhotoPath: " + mCurrentPhotoPath);
if (mCurrentPhotoPath != null)
setPic();
galleryAddPic();
mCurrentPhotoPath = null;
// Post recorded video into VideoView
private Uri handleCameraVideo(Intent intent)
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
mVideoUri = intent.getData();
mVideoView.setVideoURI(mVideoUri);
// mImageBitmap = null;
mVideoView.setVisibility(View.VISIBLE);
mImageView.setVisibility(View.INVISIBLE);
mVideoView.start();
// saves video to file
saveVideo(mVideoUri);
return mVideoUri;
// click listener for the Android Camera button (not my app's button)
Button.OnClickListener mTakePicOnClickListener =
new Button.OnClickListener()
@Override
public void onClick(View v)
// mImageBitmap = null;
dispatchTakePictureIntent(ACTION_TAKE_PHOTO);
// releases the orientation lock
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
;
Button.OnClickListener mTakeVidOnClickListener =
new Button.OnClickListener()
@Override
public void onClick(View v)
dispatchTakeVideoIntent();
// releases the orientation lock
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
;
// Intent data is how the photo and video transfer into their views
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
switch (requestCode)
case ACTION_TAKE_PHOTO:
if (resultCode == RESULT_OK)
handleCameraPhoto();
else
Log.d("TAG", "Result of photo not work.");
break;
// ACTION_TAKE_PHOTO
case ACTION_TAKE_VIDEO:
if (resultCode == RESULT_OK)
handleCameraVideo(data);
break;
// ACTION_TAKE_VIDEO
// switch
// Some lifecycle callbacks so that the image can survive orientation change
@Override
protected void onSaveInstanceState(Bundle outState)
outState.putParcelable(BITMAP_STORAGE_KEY, mImageBitmap);
outState.putParcelable(VIDEO_STORAGE_KEY, mVideoUri);
outState.putBoolean(IMAGEVIEW_VISIBILITY_STORAGE_KEY, (mImageBitmap != null) );
outState.putBoolean(VIDEOVIEW_VISIBILITY_STORAGE_KEY, (mVideoUri != null) );
outState.putString("FILE_PATH", mCurrentPhotoPath);
if (mVideoUri != null)
// use onSaveInstanceState in order to store the video or photo
outState.putInt("PositionVideo", mVideoView.getCurrentPosition());
// playback position for orientation change
mVideoView.pause();
// super should be last in this method
super.onSaveInstanceState(outState);
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
mCurrentPhotoPath = savedInstanceState.getString("FILE_PATH");
mImageView.setImageBitmap(mImageBitmap);
mImageView.setVisibility(
savedInstanceState.getBoolean(IMAGEVIEW_VISIBILITY_STORAGE_KEY) ?
ImageView.VISIBLE : ImageView.INVISIBLE
);
mVideoUri = savedInstanceState.getParcelable(VIDEO_STORAGE_KEY);
mVideoView.setVideoURI(mVideoUri);
mVideoView.setVisibility(
savedInstanceState.getBoolean(VIDEOVIEW_VISIBILITY_STORAGE_KEY) ?
ImageView.VISIBLE : ImageView.INVISIBLE
);
if (mVideoUri != null)
// for video, restores position it was playing
position = savedInstanceState.getInt("PositionVideo");
mVideoView.seekTo(position);
super.onRestoreInstanceState(savedInstanceState);
/**
* Indicates whether the specified action can be used as an intent. This
* method queries the package manager for installed packages that can
* respond to an intent with the specified action. If no suitable package is
* found, this method returns false.
* http://android-developers.blogspot.com/2009/01/can-i-use-this-intent.html
*
* @param context The application's environment.
* @param action The Intent action to check for availability.
*
* @return True if an Intent with the specified action can be sent and
* responded to, false otherwise.
*/
public static boolean isIntentAvailable(Context context, String action)
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
List<ResolveInfo> list =
packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
private void setBtnListenerOrDisable(Button btn, Button.OnClickListener onClickListener,
String intentName)
if (isIntentAvailable(this, intentName))
btn.setOnClickListener(onClickListener);
else
btn.setClickable(false);
@Override
public boolean onOptionsItemSelected(MenuItem item)
// Makes the UP caret go back to the previous fragment MakeCuteFragment
switch (item.getItemId())
case android.R.id.home:
android.app.FragmentManager fm= getFragmentManager();
fm.popBackStack();
finish();
return true;
default:
return super.onOptionsItemSelected(item);
【问题讨论】:
你用什么设备进行测试? 这是三星 Galaxy 5S。 【参考方案1】:经过我所有的尝试和错误,我终于找到了问题所在。我的第二次更新非常接近,唯一的错误是在
mImageView.setVisibility(
savedInstanceState.getBoolean(IMAGEVIEW_VISIBILITY_STORAGE_KEY) ?
ImageView.VISIBLE : ImageView.INVISIBLE
);
方法。我在onCreate
有这个,只有在它通过空检查时才执行,但我忘记的是,当相机应用程序中的配置发生变化时,图像位图 确实 返回 null,所以可见性会始终设置为不可见。如果位图 not 为空,IMAGEVIEW_VISIBILITY_STORAGE_KEY
设置为真,如果为空,则设置为假。所以一旦我摆脱了这个,我的其他保存状态代码就工作得很好。下面是最终的工作代码。
MakePhotoVideo.java
package org.azurespot.makecute;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.VideoView;
import org.azurespot.R;
import org.azurespot.cutecollection.CuteCollection;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Random;
public class MakePhotoVideo extends ActionBarActivity
private static final int ACTION_TAKE_PHOTO = 1;
private static final int ACTION_TAKE_VIDEO = 2;
private static final String BITMAP_STORAGE_KEY = "viewbitmap";
private static final String IMAGEVIEW_VISIBILITY_STORAGE_KEY = "imageviewvisibility";
private ImageView mImageView;
private Bitmap mImageBitmap;
private static final String VIDEO_STORAGE_KEY = "viewvideo";
private static final String VIDEOVIEW_VISIBILITY_STORAGE_KEY = "videoviewvisibility";
private VideoView mVideoView;
private Uri mVideoUri;
private File fileVideo;
private String mCurrentPhotoPath;
String videoPath;
private int position = 0;
int targetH;
int targetW;
private static final String JPEG_FILE_PREFIX = "IMG_";
private static final String JPEG_FILE_SUFFIX = ".jpg";
private PhotoStorageDirFactory mPhotoStorageDirFactory = null;
/* Photo album for this application */
private String getAlbumName()
return getString(R.string.album_name);
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_make_photo_video);
mImageView = (ImageView) findViewById(R.id.taken_photo);
mVideoView = (VideoView) findViewById(R.id.video_view);
mImageView.setVisibility(View.VISIBLE);
mVideoView.setVisibility(View.INVISIBLE);
mImageView.setSaveEnabled(true);
Button photoBtn = (Button) findViewById(R.id.click);
setBtnListenerOrDisable(photoBtn, mTakePicOnClickListener, MediaStore.ACTION_IMAGE_CAPTURE);
Button videoBtn = (Button) findViewById(R.id.record_video);
setBtnListenerOrDisable(videoBtn, mTakeVidOnClickListener, MediaStore.ACTION_VIDEO_CAPTURE);
mPhotoStorageDirFactory = new BasePhotoDirFactory();
// Shows the up carat near app icon in ActionBar
getSupportActionBar().setDisplayUseLogoEnabled(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
public void viewCollection(View v)
// finishes/restarts the activity so the unsaved video does not corrupt
Intent intent = getIntent();
finish();
startActivity(intent);
// goes to Cute Collection activity
Intent i = new Intent(this, CuteCollection.class);
startActivity(i);
private File getAlbumDir()
File storageDir = null;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
storageDir = mPhotoStorageDirFactory.getAlbumStorageDir(getAlbumName());
if (storageDir != null)
if (! storageDir.mkdirs())
if (! storageDir.exists())
Log.d("Camera", "failed to create directory");
return null;
else
Log.v(getString(R.string.app_name), "External storage is not mounted READ/WRITE.");
return storageDir;
private File createImageFile() throws IOException
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = JPEG_FILE_PREFIX + timeStamp + "_";
File albumF = getAlbumDir();
File imageF = File.createTempFile(imageFileName, JPEG_FILE_SUFFIX, albumF);
return imageF;
private File setUpPhotoFile() throws IOException
File f = createImageFile();
mCurrentPhotoPath = f.getAbsolutePath();
return f;
private void setPic()
mImageView.setVisibility(View.VISIBLE);
mVideoView.setVisibility(View.INVISIBLE);
/* There isn't enough memory to open up more than a couple camera photos */
/* So pre-scale the target bitmap into which the file is decoded */
/* Get the size of the image */
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
targetH = 570;
targetW = 960;
else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
targetH = 960;
targetW = 570;
BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
/* Figure out which way needs to be reduced less */
int scaleFactor = Math.min(photoW/targetW, photoH/targetH);
/* Set bitmap options to scale the image decode target */
bmOptions.inJustDecodeBounds = false;
bmOptions.inPreferredConfig = Bitmap.Config.RGB_565;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inBitmap = mImageBitmap;
bmOptions.inPurgeable = true;
/* Decode the JPEG file into a Bitmap */
mImageBitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
mImageBitmap = rotateBitmap(mImageBitmap, 90);
savePhoto(mImageBitmap);
/* Associate the Bitmap to the ImageView, make sure the VideoView
* is cleared to replace with ImageView */
mImageView.setImageBitmap(mImageBitmap);
mVideoUri = null;
// save your photo to SD card
private void savePhoto(final Bitmap bitmapPhoto)
// set OnClickListener to save the photo
mImageView.setOnClickListener(new View.OnClickListener()
public void onClick(View v)
boolean success = false;
File photoDir = new File(Environment.getExternalStoragePublicDirectory
(Environment.DIRECTORY_PICTURES) + "/Cute Photos");
photoDir.mkdirs();
Random generator = new Random();
int n = 10000;
n = generator.nextInt(n);
String photoName = "Photo"+ n +".jpg";
File filePhoto = new File (photoDir, photoName);
try
FileOutputStream out = new FileOutputStream(filePhoto);
bitmapPhoto.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
success = true;
catch (Exception e)
e.printStackTrace();
if (success)
Toast toast = Toast.makeText(getApplicationContext(), "Cute photo saved!",
Toast.LENGTH_LONG);
LinearLayout toastLayout = (LinearLayout) toast.getView();
toastLayout.setBackgroundColor(getResources().getColor(R.color.toast_color));
TextView toastTV = (TextView) toastLayout.getChildAt(0);
toastTV.setTextSize(30);
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 80);
toast.show();
else
Toast.makeText(getApplicationContext(),
"Error during image saving", Toast.LENGTH_SHORT).show();
);
// save your video to SD card
protected void saveVideo(final Uri uriVideo)
// click the video to save it
mVideoView.setOnTouchListener(new View.OnTouchListener()
public boolean onTouch(View v, MotionEvent event)
boolean success = false;
if(event.getAction() == MotionEvent.ACTION_UP)
try
// make the directory
File vidDir = new File(android.os.Environment.getExternalStoragePublicDirectory
(Environment.DIRECTORY_MOVIES) + File.separator + "Cute Videos");
vidDir.mkdirs();
// create unique identifier
Random generator = new Random();
int n = 100;
n = generator.nextInt(n);
// create file name
String videoName = "Video" + n + ".mp4";
fileVideo = new File(vidDir.getAbsolutePath(), videoName);
videoPath = fileVideo.getAbsolutePath();
Log.d("TAG", "Value of videoPath:" + videoPath);
fileVideo.setWritable(true, false);
OutputStream out = new FileOutputStream(fileVideo);
InputStream in = getContentResolver().openInputStream(uriVideo);
byte buffer[] = new byte[1024];
int length = 0;
while ((length = in.read(buffer)) > 0)
out.write(buffer, 0, length);
out.close();
in.close();
success = true;
catch (Exception e)
e.printStackTrace();
if (success)
Toast toast = Toast.makeText(getApplicationContext(), "Cute video saved!",
Toast.LENGTH_SHORT);
LinearLayout toastLayout = (LinearLayout) toast.getView();
toastLayout.setBackgroundColor(getResources().getColor(R.color.toast_color));
TextView toastTV = (TextView) toastLayout.getChildAt(0);
toastTV.setTextSize(30);
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 80);
toast.show();
else
Toast.makeText(getApplicationContext(),
"Error during video saving", Toast.LENGTH_SHORT).show();
return true;
);
public Bitmap rotateBitmap(Bitmap source, int angle)
Matrix matrix = new Matrix();
matrix.set(matrix);
matrix.setRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(),
source.getHeight(), matrix, false);
private void galleryAddPic()
Intent mediaScanIntent = new Intent("android.intent.action.MEDIA_SCANNER_SCAN_FILE");
File f = new File(mCurrentPhotoPath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
private void dispatchTakePictureIntent(int actionCode)
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
switch(actionCode)
case ACTION_TAKE_PHOTO:
File f;
try
f = setUpPhotoFile();
Log.d("TAG", "Value of f in picture intent: " + f);
mCurrentPhotoPath = f.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
catch (IOException e)
e.printStackTrace();
f = null;
mCurrentPhotoPath = null;
break;
default:
break;
// switch
startActivityForResult(takePictureIntent, actionCode);
// Captures video from Android camera component
protected void dispatchTakeVideoIntent()
Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
if (takeVideoIntent.resolveActivity(getPackageManager()) != null)
// set the video image quality to high
takeVideoIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
startActivityForResult(takeVideoIntent, ACTION_TAKE_VIDEO);
private void handleCameraPhoto()
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
if (mCurrentPhotoPath != null)
setPic();
galleryAddPic();
mCurrentPhotoPath = null;
// Post recorded video into VideoView
private Uri handleCameraVideo(Intent intent)
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
mVideoUri = intent.getData();
mVideoView.setVideoURI(mVideoUri);
mImageBitmap = null;
mVideoView.setVisibility(View.VISIBLE);
mImageView.setVisibility(View.INVISIBLE);
mVideoView.start();
// saves video to file
saveVideo(mVideoUri);
return mVideoUri;
// click listener for the Android Camera button (not my app's button)
Button.OnClickListener mTakePicOnClickListener =
new Button.OnClickListener()
@Override
public void onClick(View v)
// mImageBitmap = null;
dispatchTakePictureIntent(ACTION_TAKE_PHOTO);
// releases the orientation lock
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
;
Button.OnClickListener mTakeVidOnClickListener =
new Button.OnClickListener()
@Override
public void onClick(View v)
dispatchTakeVideoIntent();
// releases the orientation lock
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
;
// Intent data is how the photo and video transfer into their views
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
switch (requestCode)
case ACTION_TAKE_PHOTO:
if (resultCode == RESULT_OK)
handleCameraPhoto();
break;
case ACTION_TAKE_VIDEO:
if (resultCode == RESULT_OK)
handleCameraVideo(data);
break;
// Some lifecycle callbacks so that the image can survive orientation change
@Override
protected void onSaveInstanceState(Bundle outState)
outState.putParcelable(BITMAP_STORAGE_KEY, mImageBitmap);
outState.putParcelable(VIDEO_STORAGE_KEY, mVideoUri);
outState.putBoolean(IMAGEVIEW_VISIBILITY_STORAGE_KEY, (mImageBitmap != null) );
outState.putBoolean(VIDEOVIEW_VISIBILITY_STORAGE_KEY, (mVideoUri != null) );
outState.putString("FILE_PATH", mCurrentPhotoPath);
if (mVideoUri != null)
// use onSaveInstanceState in order to store the video or photo
outState.putInt("PositionVideo", mVideoView.getCurrentPosition());
// playback position for orientation change
mVideoView.pause();
// super should be last in this method
super.onSaveInstanceState(outState);
// this is called after onCreate (when returning from camera app),
// so br careful what you put here
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
mCurrentPhotoPath = savedInstanceState.getString("FILE_PATH");
mVideoUri = savedInstanceState.getParcelable(VIDEO_STORAGE_KEY);
mVideoView.setVideoURI(mVideoUri);
mVideoView.setVisibility(
savedInstanceState.getBoolean(VIDEOVIEW_VISIBILITY_STORAGE_KEY) ?
ImageView.VISIBLE : ImageView.INVISIBLE
);
if (mVideoUri != null)
// for video, restores position it was playing
position = savedInstanceState.getInt("PositionVideo");
mVideoView.seekTo(position);
super.onRestoreInstanceState(savedInstanceState);
/**
* Indicates whether the specified action can be used as an intent. This
* method queries the package manager for installed packages that can
* respond to an intent with the specified action. If no suitable package is
* found, this method returns false.
* http://android-developers.blogspot.com/2009/01/can-i-use-this-intent.html
*
* @param context The application's environment.
* @param action The Intent action to check for availability.
*
* @return True if an Intent with the specified action can be sent and
* responded to, false otherwise.
*/
public static boolean isIntentAvailable(Context context, String action)
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
List<ResolveInfo> list =
packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
private void setBtnListenerOrDisable(Button btn, Button.OnClickListener onClickListener,
String intentName)
if (isIntentAvailable(this, intentName))
btn.setOnClickListener(onClickListener);
else
btn.setClickable(false);
@Override
public boolean onOptionsItemSelected(MenuItem item)
// Makes the UP caret go back to the previous fragment MakeCuteFragment
switch (item.getItemId())
case android.R.id.home:
android.app.FragmentManager fm= getFragmentManager();
fm.popBackStack();
finish();
return true;
default:
return super.onOptionsItemSelected(item);
【讨论】:
【参考方案2】:您好,我们的一款产品也有同样的问题,我们应用了以下解决方案,它工作正常,实际上我们的客户使用的是三星设备。让我们打开您的 android 清单文件,查看 MakePhotoVideo 活动。请参阅下面您必须在活动标签中添加的内容。
<activity android:name=".MakePhotoVideo "
android:label="@string/app_name" android:configChanges="keyboardHidden|orientation">
实际上有很多与相机意图相关的用例,尤其是三星设备由于本地应用程序的自定义而存在大部分问题,将此属性应用于您的活动标签。如果您有任何问题,请告诉我。谢谢。
【讨论】:
谢谢,我之前尝试过,它确实保留了照片,但是我无法为每个配置选择不同的布局。所以它只会为纵向和横向选择纵向布局。你找到解决这个问题的方法了吗?顺便说一句,我必须在该代码中添加screenSize
,否则我仍然会变得空白。但是,布局仍然是个问题。
更具体地说,由于 Android 不允许您从 layout-land
文件夹中手动选择布局,因此如果需要,无法使用横向布局。仅当清单中没有该代码时,它才会自动选择横向布局(如果有的话)。
是的,如果您在 android manifest 中指定了标志,它将自动选择您的布局。否则,您可以选择以编程方式处理方向。
知道怎么做吗?我试图找到一种方法来选择layout-land
中的布局,但我做不到。 2 个xml
文件完全相同,名称相同,只是一个位于layout
文件夹中,一个位于layout-land
中。我不知道如何在需要时以编程方式为横向选择一个。
您好,关于在方向更改时更改活动的布局,您可以在代码中加载您定义的 xml 文件,您知道吗?【参考方案3】:
相机应用程序在实现您的意图时发生的任何事情都会导致系统破坏您的活动,因为它需要更多内存。
您需要覆盖onSaveInstanceState(Bundle savedInstanceState)
并保存ImageView
状态,也许还有其他值。详情请参阅Saving Activity state in Android 的回复。
在创建 Activity 以回复其 startActivityForResult()
时,您应该小心恢复应用状态,如 here 所述。
【讨论】:
谢谢亚历克斯,不过我都试过了。我们确定(如下)它不起作用,因为它在技术上不是配置更改。该应用程序本身并不认为它正在改变,只有当它从相机应用程序返回时它才有所不同。当我在这些方法中编写代码时,应用程序内(我的应用程序)配置更改确实有效,但不适用于相机应用程序配置更改。我知道这一点是因为我做了一个日志,在我从应用程序到相机再到应用程序的过程中甚至没有调用这些方法。仅当我在应用程序内更改配置时才会调用它们。 您是对的,从您的活动的角度来看,没有配置更改。它在停止时被系统销毁。诀窍是 onActivityResult 和 onCreate、onStart、onResume 在这种情况下以不同的顺序调用。 哦...所以您是说,不要在onRestoreInstanceState
中恢复变量,而是在onCreate
中这样做,因为这是首先调用的,在其他人之前...我会试试这个。感谢您指出了这一点!我会联系...
嗨,Alex,我用我的新观察和变化更新了我的问题。但是当我在相机应用程序中更改配置时,仍然没有让ImageView
显示任何内容。知道为什么吗?我没有保留ImageView
本身,因为它在从相机返回时会重新创建,而且我听说视图会自动保留。我只是不明白为什么它不起作用。
那么,setPic()
返回一个黑色图像?【参考方案4】:
尝试在 onSaveInstanceState() 中接收位图:
mCurrentPhotoPath = savedInstanceState.getString("FILE_PATH");
mImageBitmap = savedInstanceState.getParceable("BITMAP_STORAGE_KEY");
mImageView.setImageBitmap(mImageBitmap);
mImageView.setVisibility(
savedInstanceState.getBoolean(IMAGEVIEW_VISIBILITY_STORAGE_KEY) ?
ImageView.VISIBLE : ImageView.INVISIBLE
);
【讨论】:
嗨,我不确定我是否理解...我无法在onSaveInstanceState()
中接收位图,因为位图尚未创建。当用户从相机应用程序返回时,它在onActivityResult()
中创建,并且是在调用onSaveInstanceState()
之后。我也尝试过将位图添加到 onSaveInstanceState()
,但在登录时得到一个 null,因为它不是在离开我的应用程序之前创建的。在那之前,只有文件路径被保存和使用,我确实保存在onSaveInstanceState()
中,在onCreate
中检索它。但还是什么都没有……以上是关于Camera 中的配置更改后,返回到具有空 ImageView 的应用程序的主要内容,如果未能解决你的问题,请参考以下文章
Android:为啥 DialogFragment 在方向更改时返回空指针