照片上传的设计实现
Design implementation of photo upload
我正在构建一个 Android 应用程序,其中有五个 Activity
class,或者如果您熟悉 MVC 模式,它们通常是 Controller
classes。
具体来说,User
将进入这 5 个 Activity
class 之一(通过在整个应用程序中导航),有时他们可能会上传照片。现在上传照片的代码遵循非常相似的模式。请注意,所有这些代码在所有 5 classes (YUCK) 中重复了 5 次。
全局变量:
/*
Tracking
*/
private static final int TAKE_PHOTO_REQUEST = 1;
private static final int GET_FROM_GALLERY = 2;
private Uri mUri;
private String mCurrentPhotoPath;
private File mFile;
private TypedFile mTypedFile; // For Retrofit
用户点击照片上传按钮,弹出一个警告对话框:
private void showFileOptions() {
new AlertDialog.Builder(this)
.setItems(R.array.uploadOptions, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0:
dispatchTakePicture();
break;
case 1:
dispatchUploadFromGallery();
break;
}
}
})
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
})
.show();
}
dispatchTakePicture:
/*
Take picture from your camera
*/
private void dispatchTakePicture() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Make sure that there is a camera activity to handle the intent
if (intent.resolveActivity(getPackageManager()) != null) {
// Create the File where the mTypedFile would go
File picFile = null;
try {
picFile = createImageFile();
mFile = picFile;
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
}
// Continue only if the file was successfully created
if (picFile != null) {
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(picFile));
startActivityForResult(intent, TAKE_PHOTO_REQUEST);
}
}
}
dispatchUploadFromGallery:
/*
Take a mTypedFile from your gallery
*/
private void dispatchUploadFromGallery() {
// Launch gallery intent
startActivityForResult(new Intent(Intent.ACTION_PICK, MediaStore
.Images.Media.INTERNAL_CONTENT_URI), GET_FROM_GALLERY);
}
请注意,startActivityForResult
在这两种方法中都会被调用。接下来是 createImageFile()
方法,如果用户想从 Camera
API:
中拍照
private File createImageFile() throws IOException {
// Create the Image File name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, // Prefix
".jpg", // Suffix
storageDir // Directory
);
// Save the file, path for ACTION_VIEW intents
mCurrentPhotoPath = "file:" + image.getAbsolutePath();
mUri = Uri.fromFile(image);
return image;
}
现在终于有了我们的 startActivityForResult(...)
方法:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == TAKE_PHOTO_REQUEST && resultCode == RESULT_OK) {
startUploadProgress();
showContainer();
mTypedFile = new TypedFile("image/*", mFile);
RotatePictureHelper.rotatePicture(mFile, ExampleActivity.this, mAttachment); // Helper class to rotate pictures
mBus.post(new LoadUploadFileEvent(mTypedFile));
} else if (requestCode == GET_FROM_GALLERY && resultCode == RESULT_OK) {
startUploadProgress();
showContainer();
mUri = data.getData();
mTypedFile = UriHelper.handleUri(mUri, this); // Helper class to handle bitmap manipulation
mFile = mTypedFile.file();
mBus.post(new LoadUploadFileEvent(mTypedFile));
} else if (resultCode != Activity.RESULT_CANCELED) {
Toast.makeText(this, R.string.generalError, Toast.LENGTH_LONG).show();
}
}
请注意,我已经创建了助手 classes 来处理位图操作和图片旋转问题。
仍然,这是非常丑陋的丑陋代码,并在 5 class 秒内重复此代码。
我现在有几个想法:
- 创建一个服务并将需要的变量传递给该服务来处理这个问题。
- 将
AlertDialog
选项移动到助手 class 并根据 instanceOf
调用不同的 AlertDialogs
无论 Activity
调用它什么。
- 我是否应该创建一个具有这些方法的父
Activity
class,然后扩展 5 个 Activity
子 class 并调用这些方法?
创建一个扩展 DialogFragment 的片段(例如 UploadPhotoFragment),覆盖 onCreateDialog 和 onActivityResult 方法。并在需要时调用
(new UploadPhotoFragment()).show(getFragmentManager(), null);
Ps: 如果你正在使用支持库,请使用支持库的 DialogFragment 和 FragmentManager。
为了解决这个问题,我选择了第三个选项:
我创建了一个名为 CameraActivity
的 activity,然后在我的另外 5 个 Activity
类 中扩展了这个 Activity
。每个 类 的代码有 95% 的相似度,可能分别有 5% 的差异。
我的 CameraActivity
会有以下代码:
public class CameraActivity extends Activity {
/*
Tracking
*/
private static final int TAKE_PHOTO_REQUEST = 1;
private static final int GET_FROM_GALLERY = 2;
private Uri mUri;
private String mCurrentPhotoPath;
private File mFile;
private TypedFile mTypedFile; // For Retrofit
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
startProgress();
switch (requestCode) {
case TAKE_PHOTO_REQUEST:
if (resultCode == RESULT_OK) {
mTypedFile = new TypedFile("image/*", mFile);
}
break;
case GET_FROM_GALLERY:
if (resultCode == RESULT_OK) {
mUri = data.getData();
mTypedFile = UriHelper.handleUri(mUri, this);
}
break;
default:
stopProgress();
Toast.makeText(this, R.string.generalError, Toast.LENGTH_LONG).show();
break;
}
}
protected void showFileOptions() {
new AlertDialog.Builder(this)
.setItems(R.array.uploadOptions, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0:
dispatchTakePicture();
break;
case 1:
dispatchUploadFromGallery();
break;
default:
dispatchUploadFromGallery();
break;
}
}
})
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
})
.show();
}
// Other code for handling Uri and File goes here....
}
现在我可以在我的另一个 Activity
类 中扩展这个 Activity
并在那里实现其余 5% 的差异。请注意,showFileOptions()
已更改为 protected
状态,因此我可以从 child Activity
调用它。这里的一个例子是 activity.
public class PhotoUploadActivity extends CameraActivity {
// Initialize methods
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showFileOptions(); // Calling AlertDialog from Parent Activity
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Super call executes code in CameraActivity
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case TAKE_PHOTO_REQUEST:
if (resultCode == RESULT_OK) {
// Implement logic if user take a photo, do something with mUri or mTypedFile
}
break;
case GET_FROM_GALLERY:
if (resultCode == RESULT_OK) {
// Implement logic if user gets something from gallery, do something with mUri or mTypedFile
}
break;
default:
break;
}
}
}
现在我可以简单地扩展我的 5 child Activity
类 在那些 类 中调用 super.onActivityResult(...)
并考虑剩余的 5% 代码,同时 CameraActivity
处理其余部分。
我正在构建一个 Android 应用程序,其中有五个 Activity
class,或者如果您熟悉 MVC 模式,它们通常是 Controller
classes。
具体来说,User
将进入这 5 个 Activity
class 之一(通过在整个应用程序中导航),有时他们可能会上传照片。现在上传照片的代码遵循非常相似的模式。请注意,所有这些代码在所有 5 classes (YUCK) 中重复了 5 次。
全局变量:
/*
Tracking
*/
private static final int TAKE_PHOTO_REQUEST = 1;
private static final int GET_FROM_GALLERY = 2;
private Uri mUri;
private String mCurrentPhotoPath;
private File mFile;
private TypedFile mTypedFile; // For Retrofit
用户点击照片上传按钮,弹出一个警告对话框:
private void showFileOptions() {
new AlertDialog.Builder(this)
.setItems(R.array.uploadOptions, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0:
dispatchTakePicture();
break;
case 1:
dispatchUploadFromGallery();
break;
}
}
})
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
})
.show();
}
dispatchTakePicture:
/*
Take picture from your camera
*/
private void dispatchTakePicture() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Make sure that there is a camera activity to handle the intent
if (intent.resolveActivity(getPackageManager()) != null) {
// Create the File where the mTypedFile would go
File picFile = null;
try {
picFile = createImageFile();
mFile = picFile;
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
}
// Continue only if the file was successfully created
if (picFile != null) {
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(picFile));
startActivityForResult(intent, TAKE_PHOTO_REQUEST);
}
}
}
dispatchUploadFromGallery:
/*
Take a mTypedFile from your gallery
*/
private void dispatchUploadFromGallery() {
// Launch gallery intent
startActivityForResult(new Intent(Intent.ACTION_PICK, MediaStore
.Images.Media.INTERNAL_CONTENT_URI), GET_FROM_GALLERY);
}
请注意,startActivityForResult
在这两种方法中都会被调用。接下来是 createImageFile()
方法,如果用户想从 Camera
API:
private File createImageFile() throws IOException {
// Create the Image File name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, // Prefix
".jpg", // Suffix
storageDir // Directory
);
// Save the file, path for ACTION_VIEW intents
mCurrentPhotoPath = "file:" + image.getAbsolutePath();
mUri = Uri.fromFile(image);
return image;
}
现在终于有了我们的 startActivityForResult(...)
方法:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == TAKE_PHOTO_REQUEST && resultCode == RESULT_OK) {
startUploadProgress();
showContainer();
mTypedFile = new TypedFile("image/*", mFile);
RotatePictureHelper.rotatePicture(mFile, ExampleActivity.this, mAttachment); // Helper class to rotate pictures
mBus.post(new LoadUploadFileEvent(mTypedFile));
} else if (requestCode == GET_FROM_GALLERY && resultCode == RESULT_OK) {
startUploadProgress();
showContainer();
mUri = data.getData();
mTypedFile = UriHelper.handleUri(mUri, this); // Helper class to handle bitmap manipulation
mFile = mTypedFile.file();
mBus.post(new LoadUploadFileEvent(mTypedFile));
} else if (resultCode != Activity.RESULT_CANCELED) {
Toast.makeText(this, R.string.generalError, Toast.LENGTH_LONG).show();
}
}
请注意,我已经创建了助手 classes 来处理位图操作和图片旋转问题。
仍然,这是非常丑陋的丑陋代码,并在 5 class 秒内重复此代码。
我现在有几个想法:
- 创建一个服务并将需要的变量传递给该服务来处理这个问题。
- 将
AlertDialog
选项移动到助手 class 并根据instanceOf
调用不同的AlertDialogs
无论Activity
调用它什么。 - 我是否应该创建一个具有这些方法的父
Activity
class,然后扩展 5 个Activity
子 class 并调用这些方法?
创建一个扩展 DialogFragment 的片段(例如 UploadPhotoFragment),覆盖 onCreateDialog 和 onActivityResult 方法。并在需要时调用
(new UploadPhotoFragment()).show(getFragmentManager(), null);
Ps: 如果你正在使用支持库,请使用支持库的 DialogFragment 和 FragmentManager。
为了解决这个问题,我选择了第三个选项:
我创建了一个名为 CameraActivity
的 activity,然后在我的另外 5 个 Activity
类 中扩展了这个 Activity
。每个 类 的代码有 95% 的相似度,可能分别有 5% 的差异。
我的 CameraActivity
会有以下代码:
public class CameraActivity extends Activity {
/*
Tracking
*/
private static final int TAKE_PHOTO_REQUEST = 1;
private static final int GET_FROM_GALLERY = 2;
private Uri mUri;
private String mCurrentPhotoPath;
private File mFile;
private TypedFile mTypedFile; // For Retrofit
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
startProgress();
switch (requestCode) {
case TAKE_PHOTO_REQUEST:
if (resultCode == RESULT_OK) {
mTypedFile = new TypedFile("image/*", mFile);
}
break;
case GET_FROM_GALLERY:
if (resultCode == RESULT_OK) {
mUri = data.getData();
mTypedFile = UriHelper.handleUri(mUri, this);
}
break;
default:
stopProgress();
Toast.makeText(this, R.string.generalError, Toast.LENGTH_LONG).show();
break;
}
}
protected void showFileOptions() {
new AlertDialog.Builder(this)
.setItems(R.array.uploadOptions, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0:
dispatchTakePicture();
break;
case 1:
dispatchUploadFromGallery();
break;
default:
dispatchUploadFromGallery();
break;
}
}
})
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
})
.show();
}
// Other code for handling Uri and File goes here....
}
现在我可以在我的另一个 Activity
类 中扩展这个 Activity
并在那里实现其余 5% 的差异。请注意,showFileOptions()
已更改为 protected
状态,因此我可以从 child Activity
调用它。这里的一个例子是 activity.
public class PhotoUploadActivity extends CameraActivity {
// Initialize methods
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showFileOptions(); // Calling AlertDialog from Parent Activity
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Super call executes code in CameraActivity
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case TAKE_PHOTO_REQUEST:
if (resultCode == RESULT_OK) {
// Implement logic if user take a photo, do something with mUri or mTypedFile
}
break;
case GET_FROM_GALLERY:
if (resultCode == RESULT_OK) {
// Implement logic if user gets something from gallery, do something with mUri or mTypedFile
}
break;
default:
break;
}
}
}
现在我可以简单地扩展我的 5 child Activity
类 在那些 类 中调用 super.onActivityResult(...)
并考虑剩余的 5% 代码,同时 CameraActivity
处理其余部分。