本篇将涉及: 1.调用系统相机、上传到服务器操作 2.大照片通过采样并压缩尺寸避免OOM 3.media中图片的内容提供者使用方法,增删改查,获取手机所有图片路径 4.显示最近100张照片 5.通过系统图库选择一张加载到界面
// 注:Cons.CAMERA_RESULT = 1
startActivityForResult(new Intent(MediaStore.ACTION_IMAGE_CAPTURE), Cons.CAMERA_RESULT);
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
//通过intent获取Bundle中的"data"键的数据(即Bitmap)
Bitmap bitmap = (Bitmap) data.getExtras().get("data");
//上传照片逻辑
doUpload(bitmap);
}
}
查看源码发现RequestBody.create不止可以接收File,还可以接收字节数组byte[]。思路: 网络权限:
<uses-permission android:name="android.permission.INTERNET"/>
1.通过ByteArrayOutputStream拷贝Bitmap的字节 2.通过MultipartBody模拟表单,生成请求对象Request 3.将Request封装为Call对象,并执行Call
/**
* 模拟表单上传Bitmap:通过MultipartBody
*/
private void doUpload(Bitmap bitmap) {
//0.准备一个字节数组,准备将Bitmap放入其中
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
//1.获取OkHttpClient对象
OkHttpClient okHttpClient = new OkHttpClient();
//2.获取Request对象
RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), baos.toByteArray());
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", "test.jpg", fileBody)
.build();
Request request = new Request.Builder()
.url(Cons.BASE_URL + "upload").post(requestBody).build();
//3.将Request封装为Call对象,并执行Call
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, "onFailure: " + e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String result = response.body().string();
Log.e(TAG, "onResponse: " + result);
runOnUiThread(() -> ToastUtil.showAtOnce(MainActivity.this, result));
}
});
}
客户端完成了,多文件上传的服务端使用SpringBoot很轻松可以实现
/**
* 多文件上传(包括一个)
*
* @param files
* @return
*/
@PostMapping(value = "/upload")
public @ResponseBody
String uploadImg(@RequestParam("file") List<MultipartFile> files) {
StringBuilder result = new StringBuilder();
for (MultipartFile file : files) {
if (file.isEmpty()) {
return "false";
}
String fileName = file.getOriginalFilename();//获取名字
String path = "F:/SpringBootFiles/imgs/";
File dest = new File(path + "/" + fileName);
if (!dest.getParentFile().exists()) { //判断文件父目录是否存在
dest.getParentFile().mkdir();
}
try {
file.transferTo(dest); //保存文件
result.append(fileName + "上传成功!\n");
} catch (IllegalStateException | IOException e) {
e.printStackTrace();
result.append(fileName + "上传失败!\n");
}
}
return result.toString();
}
手机拍照.png
内存问题:避免大图片导致的OOM(加载一个9.2M的图片,点两下):
private void bitmapTest() {
//生成Bitmap对象
Bitmap bitmap = BitmapFactory.decodeFile(
Environment.getExternalStorageDirectory() + "/DCIM/Camera/IMG20181018103528.jpg");
mImageView.setImageBitmap(bitmap);
}
OOM时.png
传说中的OOM.png
1)创建Options对象op,对Bitmap做配置 2)只获取Bitmap信息,不分配内存:
op.inJustDecodeBounds = true
3)设置压缩率,根据需求自己设置,这里为适应屏幕 4)设置op解析出可显示的Bitmap,使用
private void bitmapTest() {
String pathName = Environment.getExternalStorageDirectory() + "/DCIM/Camera/IMG20181018103528.jpg";
//1.创建Options对象op,对Bitmap做配置
BitmapFactory.Options op = new BitmapFactory.Options();
//2.只获取Bitmap信息,不分配内存
op.inJustDecodeBounds = true;
//以op配置来解析出Bitmap:fakeBitmap由于inJustDecodeBounds=true,无法显示
Bitmap fakeBitmap = BitmapFactory.decodeFile(pathName, op);
L.d(op.outWidth + "*" + op.outHeight + L.l());// 6240*8320
//3.设置压缩率
//获取屏幕尺寸
int winH = ScrUtil.getScreenHeight(this);
int winW = ScrUtil.getScreenWidth(this);
op.inSampleSize = (int) (op.outHeight > op.outWidth ?
Math.ceil(op.outHeight / winH) :
Math.ceil(op.outWidth / winW));
L.d(op.inSampleSize + L.l());// 4
op.inJustDecodeBounds = false;
//4.设置op解析出可显示的Bitmap,使用
Bitmap realBitmap = BitmapFactory.decodeFile(pathName, op);
mImageView.setImageBitmap(realBitmap);
加载正常.png
media作为手机的三鼎之一,自然是少不了内容提供者来向外界暴漏信息 主要储存在
external.db(外部)
和internal.db(内部)
两个数据库中 数据库中图片的主要字段有:
_id:id标识 _data: 图片绝对路径 _size: 图片大小 mime_type: 类型
data_added:添加的时间 data_modifide:最后修改时间 _display_name:显示名称 description:描述
width:宽 height:高
media的内容提供者数据库.png
private void insertImg() {
//1.创建ContentValues对象,记录插入照片信息
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, "张风捷特烈");
values.put(MediaStore.Images.Media.DESCRIPTION, "天下无双");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
//2.获取内容提供者,插入(外部图片存储Uri,values),返回插入图片的Uri
Uri imgFileUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
L.d(imgFileUri + L.l());//content://media/external/images/media/1064830
//3.通过打开图片的意图添加额外信息将imgFileUri发送给系统相机
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imgFileUri);
startActivity(intent);
}
private void readImg() {
try {
//1.获取内容提供者,通过刚才的Uri打开输入流
Uri imgUri = Uri.parse("content://media/external/images/media/1064830");
InputStream is = getContentResolver().openInputStream(imgUri);
//2.将图片解码展示
Bitmap bitmap = BitmapFactory.decodeStream(is);
mImageView.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
private void updateImg() {
ContentValues values = new ContentValues(2);
values.put(MediaStore.Images.Media.DISPLAY_NAME, "Toly");
values.put(MediaStore.Images.Media.DESCRIPTION, "Only Me");
//1.获取内容提供者,通过刚才的Uri打开输入流
Uri imgUri = Uri.parse("content://media/external/images/media/1064830");
getContentResolver().update(imgUri, values, null, null);
}
既然是内容提供者,玩个表再所难免,借此回顾一下内容提供者的使用 可见上面的修改方法成功
private void queryImg() {
//1.查询获得游标
Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, "_id=1064830",
null, null, null);
//2.让游标移动,到尾为止
while (cursor.moveToNext()) {
String filePath = cursor.getString(cursor.getColumnIndex("_data"));
String name = cursor.getString(cursor.getColumnIndex("_display_name"));
String description = cursor.getString(cursor.getColumnIndex("description"));
L.d(filePath + L.l());//storage/emulated/0/DCIM/Camera/1539834068417.jpg
L.d(name + L.l());//Toly
L.d(description + L.l());//Only Me
}
private void deleteImg() {
Uri imgUri = Uri.parse("content://media/external/images/media/1064830");
int delete = getContentResolver().delete(imgUri, "_id=1064830", null);
L.d(delete + L.l());//1 表示删除了1行
}
一共12540张图片,方法耗时:1.289秒,属于耗时操作应该放在子线程 可以获取数据库中的字段,封装一个图片的实体类,以便使用
private ArrayList<String> queryAllImg() {
ArrayList<String> imgPaths = new ArrayList<>();
//1.查询获得游标
Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, "",
null, null, null);
//2.让游标移动,到尾为止
while (cursor.moveToNext()) {
String filePath = cursor.getString(cursor.getColumnIndex("_data"));
imgPaths.add(filePath);
}
return imgPaths;
}
查询所有图片.png
为了简便,使用Picasso来加载图片:详情可见--O2-开源框架使用之Picasso
查询最近100张图片.png
排序条件:
"date_added desc"
表示根据date_added字段倒序查询 将数据盛放在List中,并根据列表元素个数来决定跳出while循环
private ArrayList<String> queryPic100() {
ArrayList<String> imgPaths = new ArrayList<>();
//1.查询获得游标
String queryCol = MediaStore.Images.Media.DATE_ADDED;
Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, "",
null, "date_added desc", null);
//2.让游标移动,到尾为止
while (cursor.moveToNext()) {
if (imgPaths.size() >= 100) {
break;
}
String filePath = cursor.getString(cursor.getColumnIndex("_data"));
imgPaths.add(filePath);
}
return imgPaths;
}
1).创建适配器类和ViewHolder 2).设置RecyclerView样式
/**
* 适配器
*/
class PicRVAdapter extends RecyclerView.Adapter<PicViewHolder> {
@NonNull
@Override
public PicViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(Pic100Activity.this).inflate(R.layout.item_img, null);
return new PicViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull PicViewHolder holder, int position) {
//使用Picasso加载文件图片
Picasso.get().setIndicatorsEnabled(true);
Picasso.get()
.load(new File(mList.get(position)))//文件
.resize(200,200)//重设尺寸
.into(holder.mIv_icon);
}
@Override
public int getItemCount() {
return mList.size();
}
}
/**
* ViewHolder
*/
public class PicViewHolder extends RecyclerView.ViewHolder {
public final ImageView mIv_icon;
/**
* itemView为MyViewHolder中onCreateViewHolder加载的布局
*
* @param itemView 条目
*/
public PicViewHolder(View itemView) {
super(itemView);
mIv_icon = itemView.findViewById(R.id.id_iv_item);
}
}
//1.设置适配器
mIdRvPic100.setAdapter(new PicRVAdapter());
//2.!!创建布局管理器
mGLM = new GridLayoutManager(this, 4, GridLayoutManager.VERTICAL, false);
//3.!!!设置布局管理器
mIdRvPic100.setLayoutManager(mGLM);
@OnClick(R.id.imageView)
public void onViewClicked() {
//打开相册
Intent picIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(picIntent, 0);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
//获取返回的图片Uri
Uri picUri = data.getData();
InputStream is = null;
try {
//将Uri开流,形成输入流
is = getContentResolver().openInputStream(picUri);
Bitmap bitmap = BitmapFactory.decodeStream(is);
mImageView.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}