一、圖片顯示
1、要做到圖片不變形,不裁剪,正常顯示,必須知道圖片的寬高或者是寬高比,加載的時(shí)候,如果高度大于寬度,就先加載高度為父布局高度,寬度根據(jù)比例加載,反之一樣。
private fun setImageWidthHeight(imageView: ImageView,width:Int,height:Int){
imageView.post {
val params: RelativeLayout.LayoutParams = imageView.layoutParams as RelativeLayout.LayoutParams
if (height >= width){ //撐滿高度
imageView.layoutParams.height = mImageWidth
val tempWidth: Float = (width.toFloat()) / (height.toFloat())
imageView.layoutParams.width = (tempWidth * mImageWidth).toInt()
imageView.layoutParams = params
Log.d("setImageWidthHeight", "setImageWidthHeight: 圖片寬度是:${imageView.layoutParams.width},," +
",高度是:${imageView.layoutParams.height}"+ ",,,,"+tempWidth)
}else{
imageView.layoutParams.width = mImageWidth
val tempHeight: Float = (height.toFloat()) / (width.toFloat())
imageView.layoutParams.height = (tempHeight * mImageWidth).toInt()
imageView.layoutParams = params
Log.d("setImageWidthHeight", "setImageWidthHeight: 圖片寬度是:${imageView.layoutParams.width},," +
",高度是:${imageView.layoutParams.height}"+ ",,,,"+tempHeight)
}
}
}
或者使用約束布局,前提也是必須知道寬高比或者寬高,只有知道了寬高比,才不會(huì)壓縮和變形
二、圖片加載
圖片的選取,壓縮、顯示,建議使用第三方加載庫(kù):pictureselector,里面已經(jīng)適配了Android 13版本
注:GIF圖片不能進(jìn)行壓縮,一旦壓縮就變成了靜態(tài)圖片
三、圖片上傳
上傳時(shí)區(qū)分類型
gif圖片使用MediaType.parse("image/gif");
靜態(tài)圖片使用MediaType.parse("image/png");類型
fun <T> uploadImage(
cls: Class<T>,
reqUrl: String,
fileList: ArrayList<BeanImage>,
callBack: RequestCallBack<T>
) {
val params = HashMap<String, Any>()
HttpUtils.addCommonData(params)
val multipartBodyBuilder = MultipartBody.Builder()
multipartBodyBuilder.setType(MultipartBody.FORM)
//遍歷map中所有參數(shù)到builder
for (key in params.keys) {
multipartBodyBuilder.addFormDataPart(key, params[key].toString() + "")
}
//遍歷paths中所有圖片絕對(duì)路徑到builder,并約定key如“upload”作為后臺(tái)接受多張圖片的key
LogUtils.d(TAG, "uploadImage: 需要上傳的圖片是:${fileList}")
for (index in 0 until fileList.size) {
val url = fileList[index].url ?: ""
val file = File(url)
if (!file.exists()) {
return
}
val fileName = System.currentTimeMillis().toString() + "_" + file.name
LogUtils.d(TAG, "uploadImage 文件的名稱是: $fileName")
if (fileList[index].isGif()) {
Log.d(TAG, "uploadImage: 當(dāng)前是gif圖,,$fileName")
multipartBodyBuilder.addFormDataPart(
"file${index}",
fileName,
file.asRequestBody(HttpUtils.MEDIA_TYPE_GIF)
)
} else {
multipartBodyBuilder.addFormDataPart(
"file${index}",
fileName,
file.asRequestBody(HttpUtils.MEDIA_TYPE_PNG)
)
}
multipartBodyBuilder.addFormDataPart("type", "1")
}
val builder = CacheControl.Builder()
builder.noCache() //不使用緩存,全部走網(wǎng)絡(luò)
builder.noStore() //不使用緩存,也不存儲(chǔ)緩存
val cache: CacheControl = builder.build()
val requestBody: RequestBody = multipartBodyBuilder.build()
val requestBuilder = Request.Builder()
requestBuilder.url(reqUrl)
requestBuilder.cacheControl(cache)
requestBuilder.post(requestBody)
//設(shè)置header
var headersMap: HashMap<String, String> = HashMap()
headersMap = OKHttpExecuter.initHttpRequestHeader(headersMap)
if (headersMap != null && headersMap.size > 0) {
try {
val headers: Headers = headersMap.toHeaders()
requestBuilder.headers(headers)
} catch (e: Exception) {
Log.d(TAG, "uploadImage: " + e.message)
}
}
val request: Request = requestBuilder.build()
HttpUtils.client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
call.cancel()
Log.d(TAG, "onFailure: " + e.message.toString())
BaseUtils.getHandler().post {
callBack.error(e.message.toString())
}
}
@Throws(IOException::class)
override fun onResponse(call: Call, response: Response) {
val str = response.body?.string() //圖片返回json數(shù)據(jù)
call.cancel()
}
})
}
三、圖片下載
圖片下載保存到圖庫(kù)需要申請(qǐng)存儲(chǔ)權(quán)限,使用XXPermissions申請(qǐng)權(quán)限。
保存到圖庫(kù)需要適配Android12分區(qū)存儲(chǔ),代碼參考pictureselector,靜態(tài)圖和動(dòng)態(tài)圖的下載要注意區(qū)分。
/**
* 保存文件
*
* @param context 上下文
* @param path 文件下載路徑url
* @param mimeType 文件類型
* @param listener 結(jié)果回調(diào)監(jiān)聽(tīng)
*/
public static void saveLocalFile(Context context, String path, String mimeType,
OnCallbackListener<String> listener) {
PictureThreadUtils.executeByIo(new PictureThreadUtils.SimpleTask<String>() {
@Override
public String doInBackground() {
try {
Uri uri;
ContentValues contentValues = new ContentValues();
String time = ValueOf.toString(System.currentTimeMillis());
if (PictureMimeType.isHasAudio(mimeType)) {
contentValues.put(MediaStore.Audio.Media.DISPLAY_NAME, DateUtils.getCreateFileName("AUD_"));
contentValues.put(MediaStore.Audio.Media.MIME_TYPE, TextUtils.isEmpty(mimeType)
|| mimeType.startsWith(PictureMimeType.MIME_TYPE_PREFIX_VIDEO)
|| mimeType.startsWith(PictureMimeType.MIME_TYPE_PREFIX_IMAGE) ? PictureMimeType.MIME_TYPE_AUDIO : mimeType);
if (SdkVersionUtils.isQ()) {
contentValues.put(MediaStore.Audio.Media.DATE_TAKEN, time);
contentValues.put(MediaStore.Audio.Media.RELATIVE_PATH, Environment.DIRECTORY_MUSIC);
} else {
File dir = TextUtils.equals(Environment.getExternalStorageState(), Environment.MEDIA_MOUNTED)
? Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC)
: context.getExternalFilesDir(Environment.DIRECTORY_MUSIC);
contentValues.put(MediaStore.MediaColumns.DATA, dir.getAbsolutePath() + File.separator
+ DateUtils.getCreateFileName("AUD_") + PictureMimeType.AMR);
}
uri = context.getContentResolver().insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, contentValues);
} else if (PictureMimeType.isHasVideo(mimeType)) {
contentValues.put(MediaStore.Video.Media.DISPLAY_NAME, DateUtils.getCreateFileName("VID_"));
contentValues.put(MediaStore.Video.Media.MIME_TYPE, TextUtils.isEmpty(mimeType)
|| mimeType.startsWith(PictureMimeType.MIME_TYPE_PREFIX_AUDIO)
|| mimeType.startsWith(PictureMimeType.MIME_TYPE_PREFIX_IMAGE) ? PictureMimeType.MIME_TYPE_VIDEO : mimeType);
if (SdkVersionUtils.isQ()) {
contentValues.put(MediaStore.Video.Media.DATE_TAKEN, time);
contentValues.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES);
} else {
File dir = TextUtils.equals(Environment.getExternalStorageState(), Environment.MEDIA_MOUNTED)
? Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)
: context.getExternalFilesDir(Environment.DIRECTORY_MOVIES);
contentValues.put(MediaStore.MediaColumns.DATA, dir.getAbsolutePath() + File.separator
+ DateUtils.getCreateFileName("VID_") + PictureMimeType.MP4);
}
uri = context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues);
} else { // 如果是圖片
contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, DateUtils.getCreateFileName("IMG_"));
contentValues.put(MediaStore.Images.Media.MIME_TYPE, TextUtils.isEmpty(mimeType)
|| mimeType.startsWith(PictureMimeType.MIME_TYPE_PREFIX_AUDIO)
|| mimeType.startsWith(PictureMimeType.MIME_TYPE_PREFIX_VIDEO) ? PictureMimeType.MIME_TYPE_IMAGE : mimeType);
if (SdkVersionUtils.isQ()) {
contentValues.put(MediaStore.Images.Media.DATE_TAKEN, time);
contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, PictureMimeType.DCIM);
} else {
if (PictureMimeType.isHasGif(mimeType) || PictureMimeType.isUrlHasGif(path)) {
File dir = TextUtils.equals(Environment.getExternalStorageState(), Environment.MEDIA_MOUNTED)
? Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
: context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
contentValues.put(MediaStore.MediaColumns.DATA, dir.getAbsolutePath() + File.separator
+ DateUtils.getCreateFileName("IMG_") + PictureMimeType.GIF);
}
}
//插入到圖庫(kù)相冊(cè)
uri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
}
if (uri != null) {
InputStream inputStream;
if (PictureMimeType.isHasHttp(path)) {
inputStream = new URL(path).openStream();
} else {
if (PictureMimeType.isContent(path)) {
inputStream = PictureContentResolver.getContentResolverOpenInputStream(context, Uri.parse(path));
} else {
inputStream = new FileInputStream(path);
}
}
OutputStream outputStream = PictureContentResolver.getContentResolverOpenOutputStream(context, uri);
boolean bufferCopy = PictureFileUtils.writeFileFromIS(inputStream, outputStream);
if (bufferCopy) {
return PictureFileUtils.getPath(context, uri);
}
}
} catch (Exception e) {
Log.d("saveLocalFile", "Exception - doInBackground: " + e.getMessage());
PictureThreadUtils.cancel(this);
if (listener != null) {
listener.onCall("");
}
e.printStackTrace();
}
return null;
}
@Override
public void onSuccess(String result) {
PictureThreadUtils.cancel(this);
ModifyExif.setExif(context,result);
if (listener != null) {
listener.onCall(result);
}
}
@Override
public void onCancel() {
Log.d("saveLocalFile", "onCancel: " + "");
PictureThreadUtils.cancel(this);
if (listener != null) {
listener.onCall("");
}
}
@Override
public void onFail(Throwable t) {
Log.d("saveLocalFile", "onFail: " + t.getMessage());
// PictureThreadUtils.cancel(this);
// if (listener != null) {
// listener.onCall("");
// }
}
});
}
2、坑:解決圖片插入到圖庫(kù)時(shí)間不對(duì)的bug,系統(tǒng)自帶的相冊(cè)圖庫(kù)會(huì)根據(jù)圖片攜帶的時(shí)間信息進(jìn)行時(shí)間順序排列,可以通過(guò)代碼修改圖片的創(chuàng)建時(shí)間來(lái)修改圖片原始的創(chuàng)建時(shí)間
導(dǎo)入exifinterface框架,需要先申請(qǐng)存儲(chǔ)權(quán)限
implementation 'androidx.exifinterface:exifinterface:1.3.0'
//設(shè)置exif
public static void setExif(Context context,String filepath) {
boolean granted = XXPermissions.isGranted(context, Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE);
if (!granted){
return;
}
LogUtils.d("ModifyExif","需要修改的圖片的路徑是:" + filepath);
SimpleDateFormat lastModifiedDate = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", Locale.CHINA);
try {
exif = new ExifInterface(filepath); //根據(jù)圖片的路徑獲取圖片的Exif
} catch (IOException ex) {
Log.e("Mine", "cannot read exif", ex);
}
exif.setAttribute(ExifInterface.TAG_DATETIME, lastModifiedDate.format(System.currentTimeMillis())); //把時(shí)間寫(xiě)進(jìn)exif
try {
exif.saveAttributes(); //最后保存起來(lái)
} catch (IOException e) {
Log.e("Mine", "cannot save exif", e);
}
}