一、相关知识
①Android权限申请
②网络访问框架OKHttp
③内存溢出问题:图片压缩
④Android 系统7.0以上调用系统相机无效
⑤有关图片上传过程中遇到的内存溢出问题
二、效果展示
二、代码
①HTML
1 <LinearLayout 2 android:layout_width="match_parent" 3 android:layout_height="wrap_content" 4 android:orientation="vertical" 5 android:background="@color/white" 6 > 7 <android.support.v7.widget.RecyclerView 8 android:id="@+id/rvPic" 9 android:layout_width="wrap_content" 10 android:layout_height="match_parent" 11 android:layout_gravity="center_horizontal"> 12 13 </android.support.v7.widget.RecyclerView> 14 15 <TextView 16 android:id="@+id/tvNum" 17 android:layout_width="wrap_content" 18 android:layout_height="wrap_content" 19 android:text="0/8" 20 android:textColor="#666666" 21 android:layout_gravity="right|bottom" 22 android:paddingRight="@dimen/dp_10"/> 23 24 25 </LinearLayout> 26 <Button 27 28 android:id="@+id/btn_Enter" 29 android:layout_width="match_parent" 30 android:layout_height="@dimen/dp_45" 31 android:layout_alignParentBottom="true" 32 android:background="@drawable/selecter_button" 33 android:text="确认上传" 34 android:textColor="@color/inButtonText" 35 android:textSize="@dimen/dp_18" />
②Java代码
<基本功能>
实体类
1 public class LoadFileVo {
2
3 File file;
4
5 int pg; //图片下方的进度条
6
7 boolean isUpload = false; //标识该文件是否上传
8
9 Bitmap bitmap;
10
11 public Bitmap getBitmap() {
12 return bitmap;
13 }
14
15 public void setBitmap(Bitmap bitmap) {
16 this.bitmap = bitmap;
17 }
18
19 public boolean isUpload() {
20 return isUpload;
21 }
22
23 public void setUpload(boolean upload) {
24 isUpload = upload;
25 }
26
27 public LoadFileVo() {
28 }
29
30 public LoadFileVo(File file, int pg) {
31 this.file = file;
32 this.pg = pg;
33 }
34
35 public LoadFileVo(File file, boolean isUpload, int pg,Bitmap bitmap) {
36 this.file = file;
37 this.pg = pg;
38 this.isUpload = isUpload;
39 this.bitmap = bitmap;
40 }
41
42 public File getFile() {
43 return file;
44 }
45
46 public void setFile(File file) {
47 this.file = file;
48 }
49
50 public int getPg() {
51 return pg;
52 }
53
54 public void setPg(int pg) {
55 this.pg = pg;
56 }
57 }
适配器
1 /*
2 *Create By 小群子 2018/12/10
3 */
4
5 public class LoadPicAdapter extends RecyclerView.Adapter<LoadPicAdapter.MyViewHolder> {
6
7 Context context;
8 List<LoadFileVo> fileList = null;
9 View view;
10 int picNum = 8;//列表的图片个数最大值
11
12 public LoadPicAdapter(Context context, List<LoadFileVo> fileList) {
13 this.context = context;
14 this.fileList = fileList;
15 }
16
17 public LoadPicAdapter(Context context, List<LoadFileVo> fileList, int picNum) {
18 this.context = context;
19 this.fileList = fileList;
20 this.picNum = picNum;
21 }
22
23 public interface OnItemClickListener {
24 void click(View view, int positon);
25
26 void del(View view);
27 }
28
29 OnItemClickListener listener;
30
31 public void setListener(OnItemClickListener listener) {
32 this.listener = listener;
33 }
34
35 @Override
36 public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
37
38 view = LayoutInflater.from(context).inflate(R.layout.load_item_pic, parent, false);
39 return new MyViewHolder(view);
40 }
41
42 @Override
43 public void onBindViewHolder(MyViewHolder holder, final int position) {
44
45 //通过默认设置第一个为空文件为添加退保,且在文件个数小于最大限制值的情况。当图片个数等于最大限制值,第一个则不是添加按钮
46 if (position == 0&&fileList.get(position).getBitmap()==null) {
47 holder.ivPic.setImageResource(R.drawable.addpic);//加号图片
48 holder.ivPic.setOnClickListener(new View.OnClickListener() {
49 @Override
50 public void onClick(View view) {
51 listener.click(view, position);
52 }
53 });
54 holder.ivDel.setVisibility(View.INVISIBLE);
55 holder.bg_progressbar.setVisibility(View.GONE);
56
57 } else {
58 // Uri uri = Uri.parse(fileList.get(position).getFile().getPath());
59 // holder.ivPic.setImageURI(uri);
60
61 holder.ivPic.setImageBitmap(fileList.get(position).getBitmap());
62 //使用压缩后的图片进行填充到界面上
63
64
65
66 holder.ivDel.setVisibility(View.VISIBLE);
67 holder.bg_progressbar.setVisibility(View.VISIBLE);
68 holder.bg_progressbar.setProgress(fileList.get(position).getPg());
69 }
70
71
72 holder.ivDel.setOnClickListener(new View.OnClickListener() {
73 @Override
74 public void onClick(View view) {
75 //判断图片是否上传,上传后将无法删除
76 if (fileList.get(position).isUpload()) {
77 Toast.makeText(context, "该图片已上传!", Toast.LENGTH_SHORT).show();
78 } else {
79 fileList.remove(position);
80 if (fileList.size()==picNum-1&&fileList.get(0).getBitmap()!=null){
81 fileList.add(0,new LoadFileVo());
82 }//如果数量达到最大数时,前面的加号去掉,然后再减去时,则添加前面的加号
83 notifyDataSetChanged();
84 if (listener!=null){
85 listener.del(view);//传递接口,计算图片个数显示在界面中
86 }
87
88 }
89 }
90 });
91
92
93 }
94
95 @Override
96 public int getItemCount() {
97 return fileList.size();
98 }
99
100
101 static class MyViewHolder extends RecyclerView.ViewHolder {
102 @BindView(R.id.ivPic)
103 ImageView ivPic;
104 @BindView(R.id.ivDel)
105 ImageView ivDel;
106 @BindView(R.id.bg_progressbar)
107 ProgressBar bg_progressbar;
108
109 View view;
110
111
112 MyViewHolder(View view) {
113 super(view);
114 this.view = view;
115 ButterKnife.bind(this, view);
116 }
117 }
118 }
item 布局//布局自行优化
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="@dimen/dp_110" 3 android:layout_height="@dimen/dp_115" 4 5 > 6 7 <LinearLayout 8 android:layout_width="match_parent" 9 android:layout_height="match_parent" 10 android:orientation="vertical" 11 android:padding="@dimen/dp_5"> 12 13 <ImageView 14 android:id="@+id/ivPic" 15 android:layout_width="match_parent" 16 android:layout_height="@dimen/dp_100" 17 android:scaleType="centerCrop" 18 android:src="@drawable/ic_pick" 19 /> 20 21 <ProgressBar 22 android:id="@+id/bg_progressbar" 23 style="@style/StyleProgressBarMini" 24 android:layout_width="match_parent" 25 android:layout_height="@dimen/dp_5" 26 android:background="@drawable/shape_progressbar_mini" 27 android:max="100" 28 android:progress="60" /> 29 </LinearLayout> 30 <ImageView 31 android:id="@+id/ivDel" 32 android:layout_width="@dimen/dp_25" 33 android:layout_height="@dimen/dp_25" 34 android:src="@drawable/delete_round" 35 android:layout_alignParentRight="true"/> 36 37 </RelativeLayout>
1 List<LoadFileVo> fileList = new ArrayList<>();
2 LoadPicAdapter adapter = null;
3
4 //这里使用ButterKnife
5 @BindView(R.id.rvPic)
6 RecyclerView rvPic;
7
8 @BindView(R.id.tvNum)
9 TextView tvNum;
10
11
12 //初始化Adapter
13 //设置图片选择的接口
14 private void initAdapter() {
15 fileList.add(new LoadFileVo());
16 adapter = new LoadPicAdapter(this, fileList,8);
17 rvPic.setAdapter(adapter);
18 rvPic.setLayoutManager(new GridLayoutManager(this, 3));
19 adapter.setListener(new LoadPicAdapter.OnItemClickListener() {
20 @Override
21 public void click(View view, int positon) {
22 if (fileList.size()>8){
23 showShortToast("一次最多上传8张图片!");
24 }else {
25 selectPic(); //选择添加图片方法
26 }
27
28 }
29
30 @Override
31 public void del(View view) {
32 tvNum.setText((fileList.size()-1)+"/8");
33 }
34 });
35 }
《核心代码》
1 String mPhtotPath;
2 Uri uriImage;
3 File mPhotoFile = null;
4
5
6 //选择图片
7 private void selectPic() {
8
9 //动态请求权限,除此之外还需进行Androidmanifest.xml中进行请求
10
11 if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
12 != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this,
13 Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
14 || ContextCompat.checkSelfPermission(this,
15 Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
16 ActivityCompat.requestPermissions(this,
17 new String[]{Manifest.permission.CAMERA,
18 Manifest.permission.READ_EXTERNAL_STORAGE,
19 Manifest.permission.WRITE_EXTERNAL_STORAGE},
20 1);
21 }
22
23
24 final CharSequence[] items = {"相册", "拍照"};
25 AlertDialog.Builder dlg = new AlertDialog.Builder(EndLoadMstActivity.this);
26 dlg.setTitle("添加图片");
27 dlg.setItems(items, new DialogInterface.OnClickListener() {
28 public void onClick(DialogInterface dialog, int item) {
29 // 这里item是根据选择的方式,
30 if (item == 0) {
31 Intent intent = new Intent(Intent.ACTION_PICK);
32 intent.setType("image/*");
33 startActivityForResult(intent, 0);
34 } else {
35 try {
36 Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
37 mPhtotPath = getSDPath() + "/" + getPhotoFileName();
38 mPhotoFile = new File(mPhtotPath);
39 if (!mPhotoFile.exists()) {
40 mPhotoFile.createNewFile();
41 }
42 // uriImage = FileProvider.getUriForFile(EndLoadMstActivity.this, getPackageName() + ".provider", createImageFile());
43
44 uriImage = FileProvider.getUriForFile(EndLoadMstActivity.this, "com.ahbcd.app.tms.provider", mPhotoFile);
45 Log.i("TAG", "onClick: "+mPhtotPath+"---------" + getPackageName() + ".provider");
46 // uriImage = Uri.fromFile(mPhotoFile);以上一句代替解决相机兼容问题
47 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
48 intent.putExtra(MediaStore.EXTRA_OUTPUT, uriImage);
49
50 startActivityForResult(intent, 1);
51
52 } catch (Exception e) {
53 e.printStackTrace();
54 }
55 }
56 }
57 }).create();
58 dlg.show();
59 }
60
61 public String getSDPath() {
62 File sdDir = null;
63 boolean sdCardExsit = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
64 if (sdCardExsit) {
65 sdDir = Environment.getExternalStorageDirectory();
66 }
67 return sdDir.toString();
68 }
69
70 private String getPhotoFileName() {
71 Date date = new Date(System.currentTimeMillis());
72 SimpleDateFormat dateFormat = new SimpleDateFormat("'IMG'_yyyyMMdd_HHmmss");
73 return dateFormat.format(date) + ".jpg";
74 }
注:这里需要在Android中配置一个proveder 具体请参考 Android 系统7.0以上调用相机兼容问题
《获取返回的图片》
1 //重写onActivityResult方法
2 @Override
3 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
4 super.onActivityResult(requestCode, resultCode, data);
5 if (requestCode == 1) {
6 BitmapFactory.Options options = new BitmapFactory.Options();
7 options.inSampleSize = 2; //图片宽高都为原来的二分之一,即图片为原来的四分之一
8 Bitmap bitmap = BitmapFactory.decodeFile(mPhtotPath, options);
9 if (bitmap != null) {
10 if (uriImage != null) {
11 saveUritoFile(uriImage,1);
12 }
13
14 if (!bitmap.isRecycled()) {
15 bitmap.recycle(); //回收图片所占的内存
16 System.gc(); //提醒系统及时回收
17 }
18 }
19 }
20 if (requestCode == 0) {
21 if (data != null) {
22 Uri uri = data.getData();
23 saveUritoFile(uri,0);
24 }
25 }
26
27 }
28
29 //将Uri图片类型转换成File,BitMap类型
30 //在界面上显示BitMap图片,以防止内存溢出
31 //上传可选择File文件上传
32
33 private void saveUritoFile(Uri uriImage,int type) {
34 Bitmap photoBmp = null;
35
36 if (uriImage != null) {
37 try {
38 // photoBmp = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uriImage);
39 // ByteArrayOutputStream fos = new ByteArrayOutputStream();
40 // photoBmp.compress(Bitmap.CompressFormat.JPEG, 80, fos);
41 //以上代码压缩不行,还是会造成内存溢出
42
43 BitmapFactory.Options options = new BitmapFactory.Options();
44 options.inSampleSize = 2; //图片宽高都为原来的二分之一,即图片为原来的四分之一
45 photoBmp = BitmapFactory.decodeStream(this.getContentResolver()
46 .openInputStream(uriImage), null, options);
47
48 File file = new File("");
49 if (type==0){
50 file = FileParseUtils.getFileByUri(this, uriImage);
51
52 }else {
53 if (mPhotoFile!=null){
54 file = mPhotoFile;
55 }
56
57 }
58 // File file = new File("");
59 // try {
60 // file = new File(new URI(uriImage.toString()));
61 // } catch (URISyntaxException e) {
62 // e.printStackTrace();
63 // }
64 fileList.add(new LoadFileVo(file, false, 0, photoBmp));
65 tvNum.setText((fileList.size()-1)+"/8");
66 if (fileList.size()>8){ //判断时候达到最大数量,如果达到最大数量,则去掉前面的加号
67 fileList.remove(0);
68 }
69
70 adapter.notifyDataSetChanged();
71
72 } catch (IOException e) {
73 e.printStackTrace();
74 Log.i("TAG", "saveUritoFile: ---------压缩图片异常!");
75 }
76
77
78 }
79
80 }
图片上传到后台OKhttp
1 //一张张图片轮流上传
2 public void netUpload(int i, final String joData) {//用jsonOject方式转string传递其他参数
3 try {
4
5 if (!isRequestHttp) {
6 isRequestHttp = true;
7 final int finalI = i;
8 enterEnable(false);
9
10 if (fileList.get(finalI).isUpload()) {
11 netUpload(finalI + 1, joData);
12 } else {
13
14 RequestBody requestBody = new MultipartBody.Builder()
15 .setType(MultipartBody.FORM)
16 .addFormDataPart("mstjson",msg) //其他信息
17 .addFormDataPart("file", file.getName(),
18 RequestBody.create(MediaType.parse("application/octet-stream"), file))//文件
19 .build();
20 Request request = new Request.Builder()
21 .url(uploadPic---这里是图片上传的地址).post(requestBody)
22 .build();
23 okHttpClient.newCall(request).enqueue(new UICallBack() {
24 @Override
25 public void onFailureUI(Call call, IOException e) {
26 showShortToast("连接服务器失败");
27 isRequestHttp = true;
28 enterEnable(true);
29
30 }
31
32 @Override
33 public void onResponseUI(Call call, Response response) {
34 try {
35
36 isRequestHttp = false;
37
38 String result = response.body().string();
39 Utils.log("上传图片结果:" + result);
40
41
42 if (!response.isSuccessful()) {
43 Utils.log("响应失败:" + response.code());
44 showShortToast("响应失败:" + response.code());
45 enterEnable(true);
46
47 return;
48 }
49 if (result.equals("{}")) {
50 showShortToast("获取服务端数据为空");
51 enterEnable(true);
52
53 return;
54 }
55 JSONObject jsonObject = new JSONObject(result);
56 if (jsonObject.getString("IsError").equals("true")) {
57 showShortToast(jsonObject.getString("ErrMsg"));
58 enterEnable(true);
59
60 } else {
61 String Data = jsonObject.getString("Data");
62 fileList.get(finalI).setPg(100);
63 fileList.get(finalI).setUpload(true);
64 adapter.notifyDataSetChanged();
65
66 if (finalI == fileList.size() - 1) {
67 showShortToast("上传成功!");
68
69 btnEnter.setText("已上传");
70
71 } else {
72 netUpload((finalI + 1), joData);
73 }
74
75 }
76 } catch (Exception e) {
77 isRequestHttp = true;
78 hideLoadingDialog();
79 showShortToast("上传凭证发生异常!");
80 LogToFile.e("上传凭证发生异常," + e);
81 enterEnable(true);
82
83 }
84 }
85 });
86
87 }
88 }
89 } catch (Exception e) {
90 isRequestHttp = true;
91 hideLoadingDialog();
92
93 showShortToast("上传图片请求异常!");
94 LogToFile.e("上传图片请求异常," + e);
95 enterEnable(true);
96
97 }
98 }
99
100
101 //用来提示用户文件上传的情况。同时也是避免同时反复操作
102 public void enterEnable(boolean isEnabled) {
103 if (isEnabled) {
104 btnEnter.setText("重新上传");
105 btnEnter.setEnabled(true);
106 btnEnter.setBackgroundResource(R.drawable.selecter_button);
107 btnEnter.setTextColor(getResources().getColor(R.color.inButtonText));
108
109 } else {
110 btnEnter.setText("正在上传···");
111 btnEnter.setEnabled(false);
112 btnEnter.setBackgroundResource(R.drawable.buttonshap);
113 btnEnter.setTextColor(getResources().getColor(R.color.outButtonText));
114 }
115 }
最后给大家分享一份非常系统和全面的Android进阶技术大纲及进阶资料,及面试题集
想学习更多Android知识,请加入Android技术开发企鹅交流 7520 16839
进群与大牛们一起讨论,还可获取Android高级架构资料、源码、笔记、视频
包括 高级UI、Gradle、RxJava、小程序、Hybrid、移动架构、React Native、性能优化等全面的Android高级实践技术讲解性能优化架构思维导图,和BATJ面试题及答案!
群里免费分享给有需要的朋友,希望能够帮助一些在这个行业发展迷茫的,或者想系统深入提升以及困于瓶颈的朋友,在网上博客论坛等地方少花些时间找资料,把有限的时间,真正花在学习上,所以我在这免费分享一些架构资料及给大家。希望在这些资料中都有你需要的内容。