我正在构建一个照片上传表单,该表单必须从相机或图库或任何其他可用资源(Dropbox / Google Drive / Astro文件管理器等)获取图像。我认为除了一个小组件之外,我已经全部工作了。
如果所选图片来自网络资源(例如Google云端硬盘),则可能需要很长时间才能返回活动状态。我想让这个异步,以避免用户在图像下载时盯着黑屏。
我写了很多代码来完成整个工作,但这里是它的一般过程:
活动开始并显示表格。
Intent选择器自动打开并要求用户从相机或其他应用程序中选择图像。
用户选择某个应用,例如Google云端硬盘,选择图片并关闭该应用,然后返回选定的图片Uri
。
将文件内容缓存到变通方法KitKat权限,如果活动关闭并需要恢复,则可以阻止重新读取图像。
使用缓存文件作为源异步显示缩放位图。
Intent选择器关闭,图像显示在ImageButton中,用户可以在提交前填写表单。
点击提交按钮后,将启动异步任务,允许将多部分上传到服务器。
异步上传使用回调函数通知用户上传进度。
我陷入了#3和#4之间的过渡期。
如果我使用Thread或AsyncTask Intent选择器将保持打开状态,但图像也将显示在表单上。
如果我只是强迫用户等待(连续) 用户看着黑屏,然后关闭意图选择器,图像显示在表单上。
/**
* see http://stackoverflow.com/a/12347567/940217
*/
private void openImageIntent() {
// Camera.
final List<Intent> cameraIntents = new ArrayList<Intent>();
final PackageManager packageManager = getPackageManager();
Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File photoFile;
try {
uploadableImage.makeCameraTempFile();
photoFile = uploadableImage.getCameraTempFile();
uploadableImage.setCameraFilepath(photoFile.getAbsolutePath());
} catch (IOException ex) {
// Error occurred while creating the File
photoFile = null;
uploadableImage.invalidate();
Log.e(TAG, "Error while creating the temporary file needed for image capture from camera.");
ex.printStackTrace();
}
// Continue only if the File was successfully created
if (photoFile != null && packageManager != null) {
final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
for (ResolveInfo res : listCam) {
// Create the File where the photo should go
if (res.activityInfo == null) {
continue;
}
final String packageName = res.activityInfo.packageName;
final Intent intent = new Intent(captureIntent);
intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
intent.setPackage(packageName);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
cameraIntents.add(intent);
}
} else {
Toast.makeText(this, "Could not make temporary file.", Toast.LENGTH_LONG).show();
}
// Filesystem.
final Intent galleryIntent = new Intent();
galleryIntent.setType("image/*");
galleryIntent.setAction(Intent.ACTION_GET_CONTENT);
// Chooser of filesystem options.
final Intent chooserIntent = Intent.createChooser(galleryIntent, "Select Source");
// Add the camera options.
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[cameraIntents.size()]));
startActivityForResult(chooserIntent, REQUEST_IMAGE_CAPTURE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK && requestCode == REQUEST_IMAGE_CAPTURE) {
if (tempUpFile != null){
tempUpFile.invalidate();
}
if (data == null) {
Log.d(TAG, "Data was null -- source was from camera");
uploadableImage.preserveCameraFile();
setPic();
} else {
Uri selectedImageUri = data.getData();
if (selectedImageUri == null) {
Log.e(TAG, "An error that should never occur has occurred -- selectedImageUri was null when trying to get the data from the Image Capture activity result.");
Toast.makeText(this, "Unexpected error when trying to get an image from the gallery or file manager.", Toast.LENGTH_LONG).show();
return;
}
uploadableImage.setSourceImageUri(selectedImageUri, imageButton);
setPic(); // only do this if doing this serially.
}
} else if (resultCode == RESULT_CANCELED){
uploadableImage.invalidate();
if (tempUpFile != null){
uploadableImage = tempUpFile;
}
setPic();
}
}
public void setSourceImageUri(Uri selectedImageUri, final ImageView iView) {
// Important!! - clean up after ourselves!
deleteFile(this.cameraFile);
this.cameraFile = null;
InputStream tempInStream = null;
OutputStream tempOutStream = null;
try {
this.cacheFile = createFile();
tempInStream = context.getContentResolver().openInputStream(selectedImageUri);
tempOutStream = new FileOutputStream(this.cacheFile);
} catch (IOException e) {
Toast.makeText(context, "Error while trying to read the selected image.", Toast.LENGTH_LONG).show();
Log.e(TAG, "IOException while trying to copy the selected image to our cache file");
e.printStackTrace();
}
if (tempInStream == null || tempOutStream == null){
Toast.makeText(context, "Error while trying to "+(tempInStream == null ? "read" :"store")+" the selected image.", Toast.LENGTH_LONG).show();
Log.e(TAG, "Error while trying to get the " + (tempInStream == null ? "input" : "output") + " stream");
return;
}
// Works, but makes the UI hang on a black screen while waiting for the resource.
try {
copy(tempInStream, tempOutStream);
}catch (IOException e){
Toast.makeText(context, "Error while trying to read the selected image.", Toast.LENGTH_LONG).show();
Log.e(TAG, "IOException while trying to copy the selected image to our cache file");
e.printStackTrace();
}
/*
// Works, but leaves the IntentChooser open
final InputStream inStream = tempInStream;
final OutputStream outStream = tempOutStream;
new Thread(new Runnable() {
public void run() {
try {
copy(inStream, outStream);
// FIXME: somehow notify/display the bitmap in the UI
// --maybe use Message and Handler? callback Interface?
} catch (IOException e) {
Toast.makeText(context, "Error while trying to read the selected image.", Toast.LENGTH_LONG).show();
Log.e(TAG, "IOException while trying to copy the selected image to our cache file");
e.printStackTrace();
}
}
}).start();
*/
}
我发现这将/不会发生,具体取决于API级别。
不会发生:
确实发生:
以下是不应该发生的截图;图像来自KitKat模拟器。
答案 0 :(得分:2)
感觉非常愚蠢,但我找到了解决方案。
问题出在第二步:&#34;意图选择器会自动打开...&#34;
为了做到这一点,我正在检查位图是否可用,如果它不是,那么它应该显示占位符图像,然后打开Intent选择器。我在onStart
方法中有这个逻辑,它通常在onActivityResult
之后运行。但是,因为这一切都是异步发生的,所以我不能依赖这个顺序。
我必须做的是放入一个布尔标志,它可以告诉我是否在等待位图,然后如果我们这样做就不会打开选择器。