您的位置:首页 > 编程语言 > Java开发

RxJava+Retrofit+MVP实现相册相机图片上传

2017-07-25 21:31 381 查看

简介

又有一段不分享帖子了,今天刚做了一个模块的代码,实现的效果是获取相机和相册的图片有Post请求上传到服务器,感觉遇到的了一些坑,
翻帖子的时候就感觉这方面的帖子不是太多,爬出坑之后想和大家分享一下自己的经验,希望以后各位做这方面的时候少爬些坑。


效果展示



代码展示

通过上面的效果我们是用的新框架RXJava和Retrofit+MVP实现上传的效果,初步的UI就不和大家多分享了我和大家讲解一下,我会一步一步讲解最后大家可以实现。

1:第一步:

我们用的是Rxjava2.0+Retrofit2.0所以我们先导入下面的依赖


dependencies {
compile 'io.reactivex.rxjava2:rxjava:2.1.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-scalars:+'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
//图片显示我们有的glide
compile 'com.github.bumptech.glide:glide:3.8.0'
//6.0以后的动态权限封装
compile 'com.mylhyl:acp:1.1.7'
}


2:第二步

我们先定义我们的Retrofit的接口,大家都知道Retrofit是一注解和动态代理的原理实现网络请求的,它也是对OKHttp进行的封装,这是我们定义的接口,用的是Post请求;

public interface Port {

//上传图片

@Multipart

@POST("userAction_uploadImage.action")

Observable<UploadPhotoBean> uploadPhoto(@Part("user.file") MultipartBody file,

@Part MultipartBody.Part... parts);

}


3:第三步:

然后我是自己封装了一个Retrofit的网络请求类,并添加拦截器代码如下:

public class HttpMethods {
private static final int DEFAULT_TIMEOUT = 5;
private final Retrofit mRetrofit1;
private static String thpath;
private final Port mPort;
//构造方法私有
private HttpMethods(String path) {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//手动创建一个OkHttpClient并设置超时时间
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
httpClientBuilder.cookieJar(new CookiesManager(MyApp.getinster()));
httpClientBuilder.addInterceptor(loggingInterceptor);
httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
//实例化一个网络框架
mRetrofit1 = new Retrofit.Builder()
.client(httpClientBuilder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(path)
.build();
mPort = mRetrofit1.create(Port.class);
}
//在访问HttpMethods时创建单例

4000
private static class SingletonHolder{

private static final HttpMethods INSTANCE = new HttpMethods(thpath);
}
//获取单例
public static HttpMethods getInstance(String path){
thpath=path;
return SingletonHolder.INSTANCE;
}
/**
* 实现图片上传的方法
* @param multipartBody
* @param map
* @param observer
*/
public  void uploadPhoto(MultipartBody multipartBody, Map<String,String> map, Observer observer){
//这些是我们需要的参数,如果我们要改变的话就直接改参数就可以
MultipartBody.Part timer =                MultipartBody.Part.createFormData("user.currenttimer",map.get("user.currenttimer"));
MultipartBody.Part picWidth =
MultipartBody.Part.createFormData("user.picWidth",map.get("user.picWidth"));
MultipartBody.Part picHeight =
MultipartBody.Part.createFormData("user.picHeight",map.get("user.picHeight"));
MultipartBody.Part sign =
MultipartBody.Part.createFormData("user.sign",map.get("user.sign"));

mPort.uploadPhoto(multipartBody,timer,picWidth,picHeight,sign)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
}


4:第四步:

就该显示布局了把数据显示到跳转的页面上,我是一个fragment上面跳转到Activity上面,
在通过Activity里面回传到Fragment上面,我们可以先定义一个Button按钮实现跳转,放在我们需要点击的方法里面;


Intent intent=new Intent(getActivity(),XiangjiActivity.class);
startActivityForResult(intent, 1);


5:第五步:

我们跳到了一个Activity里面,我们的xml里面用一个Recyclerview来实现显示图片的列表,代码如下:
```
//定义一个集合来存放图片路径
private List<String> paths = new ArrayList<>();
//定义了一个方法是获取相册信息的方法
public void initdata() {
//我们调用别人封装的添加6.0网络权限的类,就是我们上面到的依赖
Acp.getInstance(this).request(new AcpOptions.Builder()
.setPermissions(
//读、写和照相的权限                          Manifest.permission.READ_EXTERNAL_STORAGE
,Manifest.permission.WRITE_EXTERNAL_STORAGE
,   Manifest.permission.CAMERA)
.build(),
new AcpListener() {
@Override
public void onGranted() {
//得到我们的照片路径
Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null);
//遍历相册
while (cursor.moveToNext()) {
String path = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DATA));
//将图片路径添加到集合
paths.add(path);
}
cursor.close();
}
@Override
public void onDenied(List<String> permissions) {
Toast.makeText(XiangjiActivity.this,"没权限",Toast.LENGTH_SHORT).show();
}
});


//判断集合是否为空不空的话就调去下面的方法

if (paths != null) {

//方法是适配器给Recyclerview适配数据

xiangcedata();

}else {

Toast.makeText(XiangjiActivity.this,”数据为空”,Toast.LENGTH_SHORT).show();

}

}

“`

6:第六步:

适配适配器的数据,我用的适配recyclerview_helper的方法,不用写适配器直接就可以适配数据,点击条目回传数据到Fragment;


public void xiangcedata(){
//recyclerview_helper的适配器几行代码不用书写Adapter就实现数据也是导入依赖
CommonAdapter<String> mAdapter = new CommonAdapter<String>(XiangjiActivity.this, R.layout.xiangceliebiao, paths) {
@Override
public void convert(BaseViewHolder holder, final int position) {
View itemView = holder.getItemView();
ImageView immg = itemView.findViewById(R.id.xiangce_image);
if (position == 0) {
immg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {

}
});
}else {

immg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//实现数据回传的方法
Intent mIntent = new Intent();
mIntent.putExtra("path", paths.get(position));
// 设置结果,并进行传送
setResult(1, mIntent);
finish();
}
});
//使用谷歌官方提供的Glide加载图片
Glide.with(XiangjiActivity.this).load(new File(paths.get(position))).diskCacheStrategy(DiskCacheStrategy.ALL).centerCrop().into(immg);
}
}
};
mHrecyclerviewXiangce.setAdapter(mAdapter);

}


7:第七步:

数据已经通过 setResult(1, mIntent)把数据回传到Fragment里面了,然后我们可以拿到数据了对数据进行操代码:


@Override//intent回传的方法
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//得到数据
String path = data.getStringExtra("path");
//数据添加到本地Imageview上面
Glide.with(getActivity()).load(new File(path)).diskCacheStrategy(DiskCacheStrategy.ALL).centerCrop().into(mCircleimgMine);
//使用谷歌官方提供的Glide加载图片
File file = new File(path);

try {
//压缩图片的大小并后期
Bitmap bitmap = ImageResizeUtils.resizeImage(path, RESIZE_PIC);
width = bitmap.getWidth()/2;
height = bitmap.getHeight()/2;
//图片压缩的方法
FileOutputStream fos = new FileOutputStream(path);
if (bitmap != null) {
if (bitmap.compress(Bitmap.CompressFormat.JPEG, 85, fos)) {
fos.close();
fos.flush();
}
if (!bitmap.isRecycled()) {
bitmap.isRecycled();
}
}
} catch (Exception e) {
e.printStackTrace();
}
//调用Retrofit  Post的方法
uploadFile(file);

}


8:第八步:

拿到数据我们就网络解析了

//封装方法实现网络请求并获得数据是否成功
public void uploadFile(File file){

if(!file.exists()){
Toast.makeText(getActivity(),"图片不存在",Toast.LENGTH_SHORT).show();
return;
}
//拆分数据
String [] arr = file.getAbsolutePath().split("/");

RequestBody requestFile =
RequestBody.create(MediaType.parse("multipart/form-data"), file);
//获取当前时间
long ctimer = System.currentTimeMillis() ;
//拼写参数
Map<String,String> map = new HashMap<String,String>();
map.put("user.currenttimer",ctimer+"");
map.put("user.picWidth",width+"");
map.put("user.picHeight",height+"");
//把MAP集合转换字符串,调去封装的.so库实现
String sign = JNICore.getSign(SortUtils.getMapResult(SortUtils.sortString(map)));

map.put("user.sign",sign);

MultipartBody body = new MultipartBody.Builder()
.addFormDataPart("image",arr[arr.length-1],requestFile)
.build();
//调我们封装的Retrofit实现类并传参数、网络路径得到对象在ONText方法里面
HttpMethods.getInstance(DataUrl.registerpath).uploadPhoto(body, map, new Observer<UploadPhotoBean>() {
@Override
public void onSubscribe(@NonNull Disposable d) {

}

@Override//得到数据的方法
public void onNext(@NonNull UploadPhotoBean uploadPhotoBean) {
Log.d("eeee", "onNext: "+uploadPhotoBean.toString());
Toast.makeText(getActivity(),uploadPhotoBean.getResult_message(),Toast.LENGTH_SHORT).show();
}

@Override//异常的方法
public void onError(@NonNull Throwable e) {
Log.d("eeee", "onErroy: "+e.toString());
}

@Override
public void onComplete() {

}
});
}


以上总结

这样大家就可以实现我们的效果了,或许我们看着会很乱,是为了个大家分析一下每个方法的用途和作用,下面我会把两个里面的方法联合起来给大家展示一下。


fragment的全部代码,xml就掠过了

private int INT_TOP=1;
public String LocalPhotoName;
public  final int RESIZE_PIC = 720 ;
private int width ;
private int height ;
@Override
protected int attachLayoutRes() {
return R.layout.mine_fragment;
}

@Override
protected void initViews() {
}

@Override
protected void updateViews(boolean isRefresh) {
}
@OnClick({R.id.autola_mine, R.id.autolayout_mine_text, R.id.autola_mine_setting})
public void onClick(View view) {
switch (view.getId()) {
case R.id.autola_mine:
Intent intent=new Intent(getActivity(),XiangjiActivity.class);
startActivityForResult(intent, INT_TOP);
break;
case R.id.autolayout_mine_text:
TZutil.onClick(view,getActivity(), ParticularsActivity.class);
break;
case R.id.autola_mine_setting:
TZutil.onClick(view,getActivity(), SettingActivity.class);
break;
}
}

public void uploadFile(File file){

if(!file.exists()){
Toast.makeText(getActivity(),"图片不存在",Toast.LENGTH_SHORT).show();
return;
}
String [] arr = file.getAbsolutePath().split("/");

RequestBody requestFile =
RequestBody.create(MediaType.parse("multipart/form-data"), file);

long ctimer = System.currentTimeMillis() ;
Map<String,String> map = new HashMap<String,String>();
map.put("user.currenttimer",ctimer+"");
map.put("user.picWidth",width+"");
map.put("user.picHeight",height+"");
String sign = JNICore.getSign(SortUtils.getMapResult(SortUtils.sortString(map)));

map.put("user.sign",sign);

MultipartBody body = new MultipartBody.Builder()
.addFormDataPart("image",arr[arr.length-1],requestFile)
.build();
//        BaseApiClient.getInstance(getActivity(),DataUrl.registerpath).shangchuan(DataUrl.tupian,body,map,new BaseObserBean<String>(getActivity()){
//            @Override
//            public void Next(String data) {
//                Log.d("wwww", "Next:------> "+data);
//            }
//        });

HttpMethods.getInstance(DataUrl.registerpath).uploadPhoto(body, map, new Observer<UploadPhotoBean>() {
@Override
public void onSubscribe(@NonNull Disposable d) {

}

@Override
public void onNext(@NonNull UploadPhotoBean uploadPhotoBean) {
Log.d("eeee", "onNext: "+uploadPhotoBean.toString());
Toast.makeText(getActivity(),uploadPhotoBean.getResult_message(),Toast.LENGTH_SHORT).show();
}

@Override
public void onError(@NonNull Throwable e) {
Log.d("eeee", "onErroy: "+e.toString());
}

@Override
public void onComplete() {

}
});
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
String path = data.getStringExtra("path");
Glide.with(getActivity()).load(new File(path)).diskCacheStrategy(DiskCacheStrategy.ALL).centerCrop().into(mCircleimgMine);
//使用谷歌官方提供的Glide加载图片
File file = new File(path);

try {
//压缩图片的大小并后期
Bitmap bitmap = ImageResizeUtils.resizeImage(path, RESIZE_PIC);
width = bitmap.getWidth()/2;
height = bitmap.getHeight()/2;
//图片压缩的方法
FileOutputStream fos = new FileOutputStream(path);
if (bitmap != null) {
if (bitmap.compress(Bitmap.CompressFormat.JPEG, 85, fos)) {
fos.close();
fos.flush();
}
if (!bitmap.isRecycled()) {
bitmap.isRecycled();
}
}
} catch (Exception e) {
e.printStackTrace();
}
uploadFile(file);

}
}


Activity的xml文件

<com.lvr.library.recyclerview.HRecyclerView
android:id="@+id/hrecyclerview_xiangce"
android:layout_width="match_parent"
android:layout_height="match_parent">

</com.lvr.library.recyclerview.HRecyclerView>``


Activity的代码:

public class XiangjiActivity extends MVPBaseActivity<XiangjiContract.View, XiangjiPresenter> implements XiangjiContract.View {
@BindView(R.id.img_phone_kuaijieht)
ImageView mImgPhoneKuaijieht;
@BindView(R.id.hrecyclerview_xiangce)
HRecyclerView mHrecyclerviewXiangce;
private List<String> paths = new ArrayList<>();

@Override
protected int attachLayoutRes() {
return R.layout.xiangce;
}

@Override
protected void initViews() {
mHrecyclerviewXiangce .setLayoutManager(new GridLayoutManager(this,3));
initdata();

}

public void initdata() {
Acp.getInstance(this).request(new AcpOptions.Builder()
.setPermissions(
Manifest.permission.READ_EXTERNAL_STORAGE
,Manifest.permission.WRITE_EXTERNAL_STORAGE
,   Manifest.permission.CAMERA)
.build(),
new AcpListener() {
@Override
public void onGranted() {
Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null);
//遍历相册
while (cursor.moveToNext()) {
String path = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DATA));
//将图片路径添加到集合
paths.add(path);
}
cursor.close();
}
@Override
public void onDenied(List<String> permissions) {
Toast.makeText(XiangjiActivity.this,"没权限",Toast.LENGTH_SHORT).show();
}
});
if (paths != null) {
xiangcedata();
}else {
Toast.makeText(XiangjiActivity.this,"数据为空",Toast.LENGTH_SHORT).show();
}

}
public void xiangcedata(){
CommonAdapter<String> mAdapter = new CommonAdapter<String>(XiangjiActivity.this, R.layout.xiangceliebiao, paths) {
@Override
public void convert(BaseViewHolder holder, final int position) {
View itemView = holder.getItemView();
ImageView immg = itemView.findViewById(R.id.xiangce_image);
if (position == 0) {
immg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {

}
});
}else {

immg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent mIntent = new Intent();
mIntent.putExtra("path", paths.get(position));
// 设置结果,并进行传送
setResult(1, mIntent);
finish();
}
});
//使用谷歌官方提供的Glide加载图片
Glide.with(XiangjiActivity.this).load(new File(paths.get(position))).diskCacheStrategy(DiskCacheStrategy.ALL).centerCrop().into(immg);
}
}
};
mHrecyclerviewXiangce.setAdapter(mAdapter);

}

}


总体总结

分享了全部的实现流程,希望能给大家多小带来帮助,
也希望大神们的指点和切磋,有问题请留言,会第一时间回复!
感觉有意思的可以点赞,有不太明白的朋友可以加我QQ3532877729和我联系也可以关注我的公众号更多精彩内容!!


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐