项目场景:
1.本项目是MVVP模式,使用retrofit
2.ActivityA点击imageView控件后进入CaptureImgActivity中,CaptureImgActivity中支持拍照/相册,点击拍照/相册后,需要把拍照/相册后的照片通过接口上传到文件服务器上,同时将图片显示到activityA的页面上;
3.因为我是写完项目之后回来写的这篇博客,如果你在复制代码后有报红的,请不要烦躁,仔细看代码,主要实现代码都详细贴上来了;
4.这里我绑定对应的控件用的是databinding形式
开发前需简单了解的基础知识
1.MediaStore这个类是android系统提供的一个多媒体数据库,android中多媒体信息都可以从这里提取。
2.通过隐式intent意图打开对应的拍照或者相册选择功能
Intent intent = new Intent(Intent.ACTION_PICK, null);
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
3.https://blog.csdn.net/juer2017/article/details/102933451这篇博客中解释的A与B中requestcode和resultcode中的对应关系
4.重写onActivityResult方法,onActivityResult可以可以接收到拍照/相册选择后自动返回来的数据(Intent data)
(startActivityForResult,setResult方法)
具体实现代码:
在写代码之前你需要注意的点:
1.代码中mView.hideLoading();mView.onError(err);之类的代码你可以注释掉没有影响; 2.需要添加相机、相册查看等权限,具体啥权限自己找吧… 3.AndroidManifest需要添加provider 4.AndroidManifest中配置权限file_paths,file_paths中external-path中的xxx是:AndroidManifest中package名字; 5.requestcode resultcode在不同的activity中对应 6.gradle(:app)中添加glid相关 //Glid圖片加載庫 implementation ‘com.github.bumptech.glide:glide:4.8.0’
1.AndroidManifest添加相机、相册查看等权限 AndroidManifest除了需要添加相机相册权限外还需要添加:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
2.res->xml文件中配置新增file_paths.xml 注意:external-path中的xxx是:AndroidManifest中package名字
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<external-path path="xxx" name="files_root" />
<external-path path="." name="external_storage_root" />
<root-path name="root_path" path="."/>
<external-path name="camera_photos" path="." />
</paths>
</resources>
3.Constanst全局中:
public static int PICTURE_REQUEST_CODE = 1;
public static int PICTURE_RESULT_CODE = 2;
public static int CAMERA_REQUEST_CODE= 3;
4.activityA中:
binding.ivImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(context, CaptureImgActivity.class);
context.startActivityForResult(intent, PICTURE_REQUEST_CODE);
}
});
【 解释: 为什么要用PICTURE_REQUEST_CODE来标识呢?(如果看不懂这个解释,那请跳过…) 1.可以先看完上面基础知识第三点以及知道onActivityResult是用来干啥的之后解释起来就容易懂, activityA中的onActivityResult会接受到多个不同的activity返回的intent数据, 2.通过你自己定义的标识就能知道是哪个activity传回来的值了,在本代码中,当onActivityResult中判断if (requestCode == PICTURE_REQUEST_CODE && resultCode == RESULT_CANCELED)就说明intent是由CaptureImgActivity这个activity传递过来的 】
5.CaptureImgActivity中:xml样式
6.CaptureImgActivity代码:
private String imageSavePath;
@Override
public void initView() {
binding.tvCapture.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
File photoFile = createImageSaveFile(getApplicationContext());
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
Uri imageUri = FileProvider.getUriForFile(
getApplicationContext(),
getPackageName() + ".fileprovider",
photoFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, PICTURE_REQUEST_CODE);
}
});
binding.tvAlbum.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (ContextCompat.checkSelfPermission(CaptureImgActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(CaptureImgActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
}else {
Intent intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(intent, 3);
}
}
});
binding.ivBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("image","0");
setResult(RESULT_CANCELED,intent);
finish();
return;
}
});
}
private File createImageSaveFile(Context ctx) {
File myCaptureFile = null;
try {
File pic = ctx.getExternalFilesDir(Environment.DIRECTORY_PICTURES + "/save");
if (!pic.exists()) {
pic.mkdirs();
}
myCaptureFile = File.createTempFile("utc", ".jpg", pic);
if (!myCaptureFile.exists()) {
myCaptureFile.createNewFile();
}
imageSavePath = myCaptureFile.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
}
return myCaptureFile;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICTURE_REQUEST_CODE && resultCode == RESULT_CANCELED){
Intent intent = new Intent();
intent.putExtra("image","0");
setResult(Constans.PICTURE_RESULT_CODE,intent);
finish();
return;
}
if (requestCode == PICTURE_REQUEST_CODE && resultCode == RESULT_OK) {
Intent intent = new Intent();
intent.putExtra("image",imageSavePath);
setResult(Constans.PICTURE_RESULT_CODE,intent);
finish();
return;
}
if (requestCode == 3 && resultCode == RESULT_OK) {
Uri originalUri = data.getData();
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = managedQuery(originalUri, proj, null, null, null);
if (cursor == null){
cursor = getContentResolver().query(originalUri, proj, null, null, null);
}
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
String path = cursor.getString(column_index);
Intent intent = new Intent();
intent.putExtra("image",path);
setResult(Constans.PICTURE_RESULT_CODE,intent);
finish();
return;
}
if (requestCode == 3 && resultCode == RESULT_CANCELED){
Intent intent = new Intent();
intent.putExtra("image","0");
setResult(RESULT_CANCELED,intent);
finish();
return;
}
}
7.activityA中: //接收返回来的数据 //当没有拍照或者没有选择相册就返回到A中,则不执行任何操作 //若拍照了或者选择了照片,则将照片通过接口上传到文件服务器上
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICTURE_REQUEST_CODE && resultCode == RESULT_CANCELED){
return;
}
if (requestCode == PICTURE_REQUEST_CODE && resultCode == -2){
return;
}
if (requestCode == PICTURE_REQUEST_CODE && resultCode == RESULT_CANCELED){
}
if (requestCode == PICTURE_REQUEST_CODE && resultCode == Constans.PICTURE_RESULT_CODE) {
String imageLocalPath = data.getStringExtra("image");
if (imageLocalPath!=null){
SPUtils.getInstance().save(Constans.PICTURE_LOCAL_PATH,imageLocalPath);
mPresenter.postImage(imageLocalPath,1);
}
}
}
8.通过接口上传图片到服务器 新手可以先去看这篇文章: retrofit2 -Multipart上传图片,文件, 带多参数上传: https://blog.csdn.net/qq_36767261/article/details/108886357
APIService接口:
@Multipart
@POST("v1/upload/")
Observable<Response<String>> uploadImage(@Part MultipartBody.Part file, @Part("number") int number);
注意:下面的9中,因为每个人的项目不太一样,9这一项仅作为参考!!!!!!!按照你自己项目的规范来写就行 因为文件服务器使用的地址是单独的,所以还需要在retrofitClient中单独写一个文件服务器的Url地址 9.RetrofitClient中:
public class RetrofitClient {
private APIService apiServiceUploadImg;
private Retrofit retrofitUploadImg;
public APIService getUploadImgApi() {
if (retrofitUploadImg == null) {
retrofitUploadImg = new Retrofit.Builder()
.baseUrl(baseUrl_uploadimge)
.addConverterFactory(CheckGsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.client(getOkHttpClient())
.build();
}
if (apiServiceUploadImg == null) {
apiServiceUploadImg = retrofitUploadImg.create(APIService.class);
}
return apiServiceUploadImg;
}
}
10.model层: //因为项目不一样,这里的return RetrofitClient.getInstance().getUploadImgApi().uploadImage(partFile,number);可能在你的项目中不能用,这里仅作为参考,按照你自己的项目的规范来就可以!!
@Override
public Observable<Response<String>> uploadImage(String imagePath, int number) {
File file = new File(imagePath);
RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"),file);
MultipartBody.Part partFile = MultipartBody.Part.createFormData("file",file.getName(),requestBody);
**return RetrofitClient.getInstance().getUploadImgApi().uploadImage(partFile,number);
}
11.Contract层:
public interface Contract {
interface IModel {
Observable<Response<String>> uploadImage(String imagePath,int number);
}
interface View extends BaseView {
}
interface Presenter {
}
12.presenter层:
private CheckDetailContract_CHUJIAN.IModel model;
public void postImage(String imageLocalPath, int number) {
model.uploadImage(imageLocalPath, number)
.subscribeOn(Schedulers.io())
.doOnSubscribe(disposable -> {
}).subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.doFinally(() -> {
})
.subscribe(new Observer<Response<String>>(){
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull Response<String> stringResponse) {
String imageSeverUrl = "";
if (stringResponse.getCode() == 100) {
imageSeverUrl = stringResponse.getData();
notifyPicture(imageSeverUrl );
} else {
String str = "";
if (stringResponse.getMsg() == null || stringResponse.getMsg().length() < 1) {
} else {
str = stringResponse.getMsg();
}
}
}
@Override
public void onError(@NonNull Throwable e) {
mView.hideLoading();
if (e instanceof HttpException) {
ResponseBody body = ((HttpException) e).response().errorBody();
try {
String err = JsonFormatUtils.getErrMsg(body.string());
mView.onError(err);
} catch (IOException IOe) {
IOe.printStackTrace();
}
} else if (e instanceof UnknownHostException) {
}
}
@Override
public void onComplete() {
}
});
}
上传图片之后需要将服务器返回的相当于网络图片显示在页面上:
ivImage是imageview要展示图片的控件 将手机拍完的照片上传到图片服务器之后,这个图片就相当于是网络图片了,文件服务器返回这张图片的路径,将网络图片显示在对应的imageview中:
通过glide图片加载框架
首先需要在添加配置 build.gradle(:app)中添加:
implementation 'com.github.bumptech.glide:glide:4.8.0'
activityA:
public void notifyPicture(String imageSeverUrl) {
Glide .with(context).load(imageSeverUrl).into(ivImage);
}
|