添加依赖:
implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
implementation 'com.soundcloud.android:android-crop:1.0.1@aar' //裁剪包,高版本调用手机裁剪功能不行,调用这个包
//glide
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
//阿里云oss上传
// implementation 'com.aliyun.dpa:oss-android-sdk:+'
implementation 'com.aliyun.dpa:oss-android-sdk:2.9.13'
implementation 'commons-codec:commons-codec:1.4'
//裁剪控件
// api 'com.theartofdev.edmodo:android-image-cropper:2.7.0'
api 'com.theartofdev.edmodo:android-image-cropper:2.8.+'
implementation 'top.zibin:Luban:1.1.8'
//RecyclerViewAdapter
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.50'
在清单文件AndroidmMainfest中添加权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 允许程序读取或写入系统设置 -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" /><!-- 10.0访问 图片位置信息 -->
打开相册前动态请求图库权限:
static final String[] PS={
/*Manifest.permission.READ_CONTACTS,*/
/*Manifest.permission.CAMERA,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.RECORD_AUDIO,*/
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
boolean checkPermission(){
boolean ok=true;
for(String p: PS){
ok=checkCallingOrSelfPermission(p)== PackageManager.PERMISSION_GRANTED;
if(!ok) break;
}
if(!ok) return false;
return true;
}
//判断是否拥有权限:
if(!checkPermission()){
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){
requestPermissions(PS, PS.hashCode());
}
return;
}
startActivity(new Intent(MainActivity.this, SelectPicActivity.class));
SelectPicActivity:
package com.example.testcsdnproject.picdemo;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.example.testcsdnproject.R;
import com.luck.picture.lib.PictureSelector;
import com.luck.picture.lib.animators.AnimationType;
import com.luck.picture.lib.config.PictureConfig;
import com.luck.picture.lib.config.PictureMimeType;
import com.luck.picture.lib.entity.LocalMedia;
import com.luck.picture.lib.language.LanguageConfig;
import com.luck.picture.lib.tools.SdkVersionUtils;
import java.util.ArrayList;
import java.util.List;
//选择相册
public class SelectPicActivity extends AppCompatActivity {
private TextView tv_upload;
private RecyclerView rv_dynamic;
private GridImageAdapter2 gridImageAdapter2;
private List<LocalMedia> selectList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_select_pic);
tv_upload = findViewById(R.id.tv_upload);
rv_dynamic = findViewById(R.id.rv_dynamic);
GridLayoutManager manager = new GridLayoutManager(this,4,RecyclerView.VERTICAL,false);
rv_dynamic.setLayoutManager(manager);
gridImageAdapter2 = new GridImageAdapter2(this,onAddPicClickListener);
rv_dynamic.setAdapter(gridImageAdapter2);
tv_upload.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (selectList.size() == 0){
Toast.makeText(SelectPicActivity.this, "请上传图片视频。", Toast.LENGTH_SHORT).show();
return;
}
uploadFile();
}
});
//添加点击事件
gridImageAdapter2.setOnItemClickListener(new GridImageAdapter2.OnItemClickListener() {
@Override
public void onItemClick(View v, int position) {
if (selectList.size()>0){
LocalMedia media = selectList.get(position);
String pictureType = media.getMimeType();
int mediaType = PictureMimeType.getMimeType(pictureType);
switch (mediaType){
case 1 : //预览图片
PictureSelector.create(SelectPicActivity.this).externalPicturePreview(position,selectList,0);
break;
case 2 : //预览视频
PictureSelector.create(SelectPicActivity.this).externalPictureVideo(media.getPath());
break;
case 3 : //预览音频
PictureSelector.create(SelectPicActivity.this).externalPictureAudio(media.getPath());
break;
}
}
}
});
}
// 从相册返回结果
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK){
switch (requestCode){
case PictureConfig.CHOOSE_REQUEST:
selectList = PictureSelector.obtainMultipleResult(data);
gridImageAdapter2.setList(selectList);
gridImageAdapter2.notifyDataSetChanged();
break;
}
}
}
//把图片上传OSS
private String filePath;
private List<String> urls = new ArrayList<>();
private List<String> urlsCallback = new ArrayList<>();
private void uploadFile(){
OssService ossService = new OssService(this, 1);
ossService.initOSSClient();
urls.clear();
for (int i = 0; i<selectList.size();i++){
filePath = selectList.get(i).getRealPath();
// filePath = selectList.get(i).getAndroidQToPath();
urls.add(filePath);
}
/*if (mWaitDialog == null) {
mWaitDialog = new WaitDialog.Builder(PublishDynamicActivity.this)
// 消息文本可以不用填写
.setMessage("正在发布...")
.create();
}
if (!mWaitDialog.isShowing()) {
mWaitDialog.setCanceledOnTouchOutside(true);
mWaitDialog.show();
}*/
if (urls.size() == 0){
// mHandler.sendEmptyMessage(1); //走接口发布动态
return;
}
ossService.ossUploadDynamic(urls);
urlsCallback.clear();
ossService.setCallbackClickListener(new OssService.onCallbackClickListener() {
@Override
public void onCallBack(String imageurl, String json) {
if(imageurl.contains(".mp4")){
// type = "2"; //2为视频
}else{
// type = "1"; //其他都是1 包括图片 文字 音频
}
urlsCallback.add(imageurl);
if (urlsCallback.size() == selectList.size()){
// mHandler.sendEmptyMessage(1);//走接口发布动态
// 图片视频数据从urlsCallback这个列表里面取
}
}
});
}
private GridImageAdapter2.onAddPicClickListener onAddPicClickListener = new GridImageAdapter2.onAddPicClickListener() {
@Override
public void onAddPicClick() {
boolean mode = /*cb_mode.isChecked()*/true;
if (mode) {
// 进入相册 以下是例子:不需要的api可以不写
PictureSelector.create(SelectPicActivity.this)
.openGallery(PictureMimeType.ofAll())// 全部.PictureMimeType.ofAll()、图片.ofImage()、视频.ofVideo()、音频.ofAudio()
.imageEngine(GlideEngine.createGlideEngine())// 外部传入图片加载引擎,必传项
// .theme(themeId)// 主题样式设置 具体参考 values/styles 用法:R.style.picture.white.style v2.3.3后 建议使用setPictureStyle()动态方式
.isWeChatStyle(true)// 是否开启微信图片选择风格
.isUseCustomCamera(false)// 是否使用自定义相机
.setLanguage(LanguageConfig.TRADITIONAL_CHINESE)// 设置语言,默认中文
.isPageStrategy(false)// 是否开启分页策略 & 每页多少条;默认开启
// .setPictureStyle(mPictureParameterStyle)// 动态自定义相册主题
// .setPictureCropStyle(mCropParameterStyle)// 动态自定义裁剪主题
// .setPictureWindowAnimationStyle(mWindowAnimationStyle)// 自定义相册启动退出动画
.setRecyclerAnimationMode(AnimationType.ALPHA_IN_ANIMATION)// 列表动画效果
.isWithVideoImage(false)// 图片和视频是否可以同选,只在ofAll模式下有效
.isMaxSelectEnabledMask(true)// 选择数到了最大阀值列表是否启用蒙层效果
//.isAutomaticTitleRecyclerTop(false)// 连续点击标题栏RecyclerView是否自动回到顶部,默认true
//.loadCacheResourcesCallback(GlideCacheEngine.createCacheEngine())// 获取图片资源缓存,主要是解决华为10部分机型在拷贝文件过多时会出现卡的问题,这里可以判断只在会出现一直转圈问题机型上使用
//.setOutputCameraPath()// 自定义相机输出目录,只针对Android Q以下,例如 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + File.separator + "Camera" + File.separator;
//.setButtonFeatures(CustomCameraView.BUTTON_STATE_BOTH)// 设置自定义相机按钮状态
.maxSelectNum(9)// 最大图片选择数量
// .minSelectNum(1)// 最小选择数量
.maxVideoSelectNum(1) // 视频最大选择数量
//.minVideoSelectNum(1)// 视频最小选择数量
//.closeAndroidQChangeVideoWH(!SdkVersionUtils.checkedAndroid_Q())// 关闭在AndroidQ下获取图片或视频宽高相反自动转换
.imageSpanCount(4)// 每行显示个数
.isReturnEmpty(false)// 未选择数据时点击按钮是否可以返回
.closeAndroidQChangeWH(true)//如果图片有旋转角度则对换宽高,默认为true
.closeAndroidQChangeVideoWH(!SdkVersionUtils.checkedAndroid_Q())// 如果视频有旋转角度则对换宽高,默认为false
//.isAndroidQTransform(false)// 是否需要处理Android Q 拷贝至应用沙盒的操作,只针对compress(false); && .isEnableCrop(false);有效,默认处理
// .setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)// 设置相册Activity方向,不设置默认使用系统
.isOriginalImageControl(true)// 是否显示原图控制按钮,如果设置为true则用户可以自由选择是否使用原图,压缩、裁剪功能将会失效
// .bindCustomPlayVideoCallback(new MyVideoSelectedPlayCallback(getContext()))// 自定义视频播放回调控制,用户可以使用自己的视频播放界面
//.bindCustomCameraInterfaceListener(new MyCustomCameraInterfaceListener())// 提供给用户的一些额外的自定义操作回调
//.cameraFileName(System.currentTimeMillis() +".jpg") // 重命名拍照文件名、如果是相册拍照则内部会自动拼上当前时间戳防止重复,注意这个只在使用相机时可以使用,如果使用相机又开启了压缩或裁剪 需要配合压缩和裁剪文件名api
//.renameCompressFile(System.currentTimeMillis() +".jpg")// 重命名压缩文件名、 如果是多张压缩则内部会自动拼上当前时间戳防止重复
//.renameCropFileName(System.currentTimeMillis() + ".jpg")// 重命名裁剪文件名、 如果是多张裁剪则内部会自动拼上当前时间戳防止重复
.selectionMode(PictureConfig.MULTIPLE)// 多选 or 单选
// .isSingleDirectReturn(true)// 单选模式下是否直接返回,PictureConfig.SINGLE模式下有效
.isPreviewImage(true)// 是否可预览图片
.isPreviewVideo(true)// 是否可预览视频
//.querySpecifiedFormatSuffix(PictureMimeType.ofJPEG())// 查询指定后缀格式资源
.isEnablePreviewAudio(true) // 是否可播放音频
.isCamera(true)// 是否显示拍照按钮
//.isMultipleSkipCrop(false)// 多图裁剪时是否支持跳过,默认支持
//.isMultipleRecyclerAnimation(false)// 多图裁剪底部列表显示动画效果
.isZoomAnim(true)// 图片列表点击 缩放效果 默认true
//.imageFormat(PictureMimeType.PNG)// 拍照保存图片格式后缀,默认jpeg,Android Q使用PictureMimeType.PNG_Q
.isEnableCrop(false)// 是否裁剪
//.basicUCropConfig()//对外提供所有UCropOptions参数配制,但如果PictureSelector原本支持设置的还是会使用原有的设置
.isCompress(true)// 是否压缩
//.compressQuality(80)// 图片压缩后输出质量 0~ 100
.synOrAsy(true)//同步true或异步false 压缩 默认同步
//.queryMaxFileSize(10)// 只查多少M以内的图片、视频、音频 单位M
//.compressSavePath(getPath())//压缩图片保存地址
//.sizeMultiplier(0.5f)// glide 加载图片大小 0~1之间 如设置 .glideOverride()无效 注:已废弃
//.glideOverride(160, 160)// glide 加载宽高,越小图片列表越流畅,但会影响列表图片浏览的清晰度 注:已废弃
.withAspectRatio(0, 0)// 裁剪比例 如16:9 3:2 3:4 1:1 可自定义
.hideBottomControls(false)// 是否显示uCrop工具栏,默认不显示
.isGif(true)// 是否显示gif图片
.freeStyleCropEnabled(true)// 裁剪框是否可拖拽
.circleDimmedLayer(false)// 是否圆形裁剪
//.setCropDimmedColor(ContextCompat.getColor(getContext(), R.color.app_color_white))// 设置裁剪背景色值
//.setCircleDimmedBorderColor(ContextCompat.getColor(getApplicationContext(), R.color.app_color_white))// 设置圆形裁剪边框色值
//.setCircleStrokeWidth(3)// 设置圆形裁剪边框粗细
.showCropFrame(true)// 是否显示裁剪矩形边框 圆形裁剪时建议设为false
.showCropGrid(true)// 是否显示裁剪矩形网格 圆形裁剪时建议设为false
.isOpenClickSound(false)// 是否开启点击声音
.selectionData(gridImageAdapter2.getData())// 是否传入已选图片
//.isDragFrame(false)// 是否可拖动裁剪框(固定)
//.videoMinSecond(10)// 查询多少秒以内的视频
//.videoMaxSecond(15)// 查询多少秒以内的视频
//.recordVideoSecond(10)//录制视频秒数 默认60s
//.isPreviewEggs(true)// 预览图片时 是uploadGetPolicy否增强左右滑动图片体验(图片滑动一半即可看到上一张是否选中)
//.cropCompressQuality(90)// 注:已废弃 改用cutOutQuality()
.cutOutQuality(90)// 裁剪输出质量 默认100
.minimumCompressSize(100)// 小于100kb的图片不压缩
//.cropWH()// 裁剪宽高比,设置如果大于图片本身宽高则无效
//.cropImageWideHigh()// 裁剪宽高比,设置如果大于图片本身宽高则无效
//.rotateEnabled(false) // 裁剪是否可旋转图片
//.scaleEnabled(false)// 裁剪是否可放大缩小图片
//.videoQuality()// 视频录制质量 0 or 1
.forResult(PictureConfig.CHOOSE_REQUEST);//结果回调onActivityResult code
// .forResult(new MyResultCallback(gridImageAdapter2));
}
}
};
}
xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#1a1a1a">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_dynamic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/tv_upload"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:text="上传"
android:textColor="@color/white"
android:textSize="20sp"
android:gravity="center"
android:background="@color/picture_color_blue"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="30dp"
/>
</RelativeLayout>
GridImageAdapter2:
package com.example.testcsdnproject.picdemo;
import android.content.Context;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.example.testcsdnproject.R;
import com.luck.picture.lib.config.PictureMimeType;
import com.luck.picture.lib.entity.LocalMedia;
import com.luck.picture.lib.tools.DateUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class GridImageAdapter2 extends
RecyclerView.Adapter<GridImageAdapter2.ViewHolder> {
public static final String TAG = "PictureSelector";
public static final int TYPE_CAMERA = 1;
public static final int TYPE_PICTURE = 2;
private LayoutInflater mInflater;
private List<LocalMedia> list = new ArrayList<>();
private int selectMax = 9;
/**
* 点击添加图片跳转
*/
private onAddPicClickListener mOnAddPicClickListener;
public interface onAddPicClickListener {
void onAddPicClick();
}
/**
* 删除
*/
public void delete(int position) {
try {
if (position != RecyclerView.NO_POSITION && list.size() > position) {
list.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, list.size());
}
} catch (Exception e) {
e.printStackTrace();
}
}
public GridImageAdapter2(Context context, onAddPicClickListener mOnAddPicClickListener) {
this.mInflater = LayoutInflater.from(context);
this.mOnAddPicClickListener = mOnAddPicClickListener;
}
public GridImageAdapter2(Context context, onAddPicClickListener mOnAddPicClickListener, int selectMax) {
this.mInflater = LayoutInflater.from(context);
this.mOnAddPicClickListener = mOnAddPicClickListener;
this.selectMax = selectMax;
}
public void setSelectMax(int selectMax) {
this.selectMax = selectMax;
}
public void setList(List<LocalMedia> list) {
this.list = list;
}
public List<LocalMedia> getData() {
return list == null ? new ArrayList<>() : list;
}
public void remove(int position) {
if (list != null && position < list.size()) {
list.remove(position);
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
ImageView mImg;
ImageView mIvDel;
TextView tvDuration;
public ViewHolder(View view) {
super(view);
mImg = view.findViewById(R.id.fiv);
mIvDel = view.findViewById(R.id.iv_del);
tvDuration = view.findViewById(R.id.tv_duration);
}
}
@Override
public int getItemCount() {
if (list.size() < selectMax) {
return list.size() + 1;
} else {
return list.size();
}
}
@Override
public int getItemViewType(int position) {
if (isShowAddItem(position)) {
return TYPE_CAMERA;
} else {
return TYPE_PICTURE;
}
}
/**
* 创建ViewHolder
*/
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = mInflater.inflate(R.layout.gv_filter_image,
viewGroup, false);
final ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
private boolean isShowAddItem(int position) {
int size = list.size() == 0 ? 0 : list.size();
return position == size;
}
/**
* 设置值
*/
@Override
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
//少于8张,显示继续添加的图标
if (getItemViewType(position) == TYPE_CAMERA) {
viewHolder.mImg.setImageResource(R.mipmap.ic_add_image);
viewHolder.mImg.setOnClickListener(v -> mOnAddPicClickListener.onAddPicClick());
viewHolder.mIvDel.setVisibility(View.INVISIBLE);
} else {
viewHolder.mIvDel.setVisibility(View.VISIBLE);
viewHolder.mIvDel.setOnClickListener(view -> {
int index = viewHolder.getAdapterPosition();
// 这里有时会返回-1造成数据下标越界,具体可参考getAdapterPosition()源码,
// 通过源码分析应该是bindViewHolder()暂未绘制完成导致,知道原因的也可联系我~感谢
if (index != RecyclerView.NO_POSITION && list.size() > index) {
list.remove(index);
notifyItemRemoved(index);
notifyItemRangeChanged(index, list.size());
}
});
LocalMedia media = list.get(position);
if (media == null
|| TextUtils.isEmpty(media.getPath())) {
return;
}
int chooseModel = media.getChooseModel();
String path;
if (media.isCut() && !media.isCompressed()) {
// 裁剪过
path = media.getCutPath();
} else if (media.isCompressed() || (media.isCut() && media.isCompressed())) {
// 压缩过,或者裁剪同时压缩过,以最终压缩过图片为准
path = media.getCompressPath();
} else {
// 原图
path = media.getPath();
}
Log.i(TAG, "原图地址::" + media.getPath());
if (media.isCut()) {
Log.i(TAG, "裁剪地址::" + media.getCutPath());
}
if (media.isCompressed()) {
Log.i(TAG, "压缩地址::" + media.getCompressPath());
Log.i(TAG, "压缩后文件大小::" + new File(media.getCompressPath()).length() / 1024 + "k");
}
if (!TextUtils.isEmpty(media.getAndroidQToPath())) {
Log.i(TAG, "Android Q特有地址::" + media.getAndroidQToPath());
}
if (media.isOriginal()) {
Log.i(TAG, "是否开启原图功能::" + true);
Log.i(TAG, "开启原图功能后地址::" + media.getOriginalPath());
}
long duration = media.getDuration();
viewHolder.tvDuration.setVisibility(PictureMimeType.isHasVideo(media.getMimeType())
? View.VISIBLE : View.GONE);
if (chooseModel == PictureMimeType.ofAudio()) {
viewHolder.tvDuration.setVisibility(View.VISIBLE);
viewHolder.tvDuration.setCompoundDrawablesRelativeWithIntrinsicBounds
(R.drawable.picture_icon_audio, 0, 0, 0);
} else {
viewHolder.tvDuration.setCompoundDrawablesRelativeWithIntrinsicBounds
(R.drawable.picture_icon_video, 0, 0, 0);
}
viewHolder.tvDuration.setText(DateUtils.formatDurationTime(duration));
if (chooseModel == PictureMimeType.ofAudio()) {
viewHolder.mImg.setImageResource(R.drawable.picture_audio_placeholder);
} else {
Glide.with(viewHolder.itemView.getContext())
.load(PictureMimeType.isContent(path) && !media.isCut() && !media.isCompressed() ? Uri.parse(path)
: path)
.centerCrop()
.placeholder(R.color.app_color_f6)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(viewHolder.mImg);
}
//itemView 的点击事件
if (mItemClickListener != null) {
viewHolder.itemView.setOnClickListener(v -> {
int adapterPosition = viewHolder.getAdapterPosition();
mItemClickListener.onItemClick(v, adapterPosition);
});
}
if (mItemLongClickListener != null) {
viewHolder.itemView.setOnLongClickListener(v -> {
int adapterPosition = viewHolder.getAdapterPosition();
mItemLongClickListener.onItemLongClick(viewHolder, adapterPosition, v);
return true;
});
}
}
//设置图片宽高比1:1
ViewGroup.LayoutParams layoutParams = viewHolder.mImg.getLayoutParams();
// layoutParams.width = (int) (Common.getScreenWidth(mContext)*0.95);
layoutParams.height = (int) (layoutParams.width );
viewHolder.mImg.setLayoutParams(layoutParams);
}
private OnItemClickListener mItemClickListener;
public interface OnItemClickListener {
void onItemClick(View v, int position);
}
public void setOnItemClickListener(OnItemClickListener l) {
this.mItemClickListener = l;
}
private OnItemLongClickListener mItemLongClickListener;
public void setItemLongClickListener(OnItemLongClickListener l) {
this.mItemLongClickListener = l;
}
}
gv_filter_image.xml:(小图标自己上千图网找)
<?xml version="1.0" encoding="utf-8"?>
<com.luck.picture.lib.widget.SquareRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_margin="5dp"
android:layout_centerInParent="true">
<ImageView
android:id="@+id/fiv"
android:layout_width="match_parent"
android:layout_height="100dp"
tools:src="#f6f6f6" />
<ImageView
android:id="@+id/iv_del"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/fiv"
android:layout_alignRight="@id/fiv"
android:paddingLeft="@dimen/dp_10"
android:paddingBottom="@dimen/dp_10"
android:scaleType="centerInside"
android:src="@mipmap/ic_delete_menu"
android:visibility="gone"
tools:visibility="visible" />
<TextView
android:id="@+id/tv_duration"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_alignLeft="@id/fiv"
android:layout_alignRight="@id/fiv"
android:layout_alignBottom="@id/fiv"
android:background="@mipmap/ps_ic_shadow_bg"
android:drawableLeft="@mipmap/ps_ic_video"
android:drawablePadding="5dp"
android:gravity="center_vertical"
android:paddingLeft="5dp"
android:paddingTop="8dp"
android:text="00:00"
android:textColor="#FFFFFF"
android:textSize="11sp"
android:visibility="gone"
tools:visibility="visible" />
</com.luck.picture.lib.widget.SquareRelativeLayout>
OnItemLongClickListener:
package com.example.testcsdnproject.picdemo;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
/**
* @author:luck
* @date:2020-01-13 17:58
* @describe:长按事件
*/
public interface OnItemLongClickListener {
void onItemLongClick(RecyclerView.ViewHolder holder, int position, View v);
}
GlideEngine:(加载引擎)
package com.example.testcsdnproject.picdemo;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.BitmapImageViewTarget;
import com.bumptech.glide.request.target.ImageViewTarget;
import com.example.testcsdnproject.R;
import com.luck.picture.lib.engine.ImageEngine;
import com.luck.picture.lib.listener.OnImageCompleteCallback;
import com.luck.picture.lib.tools.MediaUtils;
import com.luck.picture.lib.widget.longimage.ImageSource;
import com.luck.picture.lib.widget.longimage.ImageViewState;
import com.luck.picture.lib.widget.longimage.SubsamplingScaleImageView;
/**
* @author:luck
* @date:2019-11-13 17:02
* @describe:Glide加载引擎
*/
public class GlideEngine implements ImageEngine {
/**
* 加载图片
*
* @param context
* @param url
* @param imageView
*/
@Override
public void loadImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) {
Glide.with(context)
.load(url)
.into(imageView);
}
/**
* 加载网络图片适配长图方案
* # 注意:此方法只有加载网络图片才会回调
*
* @param context
* @param url
* @param imageView
* @param longImageView
* @param callback 网络图片加载回调监听 {link after version 2.5.1 Please use the #OnImageCompleteCallback#}
*/
@Override
public void loadImage(@NonNull Context context, @NonNull String url,
@NonNull ImageView imageView,
SubsamplingScaleImageView longImageView, OnImageCompleteCallback callback) {
Glide.with(context)
.asBitmap()
.load(url)
.into(new ImageViewTarget<Bitmap>(imageView) {
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
if (callback != null) {
callback.onShowLoading();
}
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
if (callback != null) {
callback.onHideLoading();
}
}
@Override
protected void setResource(@Nullable Bitmap resource) {
if (callback != null) {
callback.onHideLoading();
}
if (resource != null) {
boolean eqLongImage = MediaUtils.isLongImg(resource.getWidth(),
resource.getHeight());
longImageView.setVisibility(eqLongImage ? View.VISIBLE : View.GONE);
imageView.setVisibility(eqLongImage ? View.GONE : View.VISIBLE);
if (eqLongImage) {
// 加载长图
longImageView.setQuickScaleEnabled(true);
longImageView.setZoomEnabled(true);
longImageView.setPanEnabled(true);
longImageView.setDoubleTapZoomDuration(100);
longImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP);
longImageView.setDoubleTapZoomDpi(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER);
longImageView.setImage(ImageSource.bitmap(resource),
new ImageViewState(0, new PointF(0, 0), 0));
} else {
// 普通图片
imageView.setImageBitmap(resource);
}
}
}
});
}
/**
* 加载网络图片适配长图方案
* # 注意:此方法只有加载网络图片才会回调
*
* @param context
* @param url
* @param imageView
* @param longImageView
* @ 已废弃
*/
@Override
public void loadImage(@NonNull Context context, @NonNull String url,
@NonNull ImageView imageView,
SubsamplingScaleImageView longImageView) {
Glide.with(context)
.asBitmap()
.load(url)
.into(new ImageViewTarget<Bitmap>(imageView) {
@Override
protected void setResource(@Nullable Bitmap resource) {
if (resource != null) {
boolean eqLongImage = MediaUtils.isLongImg(resource.getWidth(),
resource.getHeight());
longImageView.setVisibility(eqLongImage ? View.VISIBLE : View.GONE);
imageView.setVisibility(eqLongImage ? View.GONE : View.VISIBLE);
if (eqLongImage) {
// 加载长图
longImageView.setQuickScaleEnabled(true);
longImageView.setZoomEnabled(true);
longImageView.setPanEnabled(true);
longImageView.setDoubleTapZoomDuration(100);
longImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP);
longImageView.setDoubleTapZoomDpi(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER);
longImageView.setImage(ImageSource.bitmap(resource),
new ImageViewState(0, new PointF(0, 0), 0));
} else {
// 普通图片
imageView.setImageBitmap(resource);
}
}
}
});
}
/**
* 加载相册目录
*
* @param context 上下文
* @param url 图片路径
* @param imageView 承载图片ImageView
*/
@Override
public void loadFolderImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) {
Glide.with(context)
.asBitmap()
.load(url)
.override(180, 180)
.centerCrop()
.sizeMultiplier(0.5f)
.apply(new RequestOptions().placeholder(R.drawable.picture_image_placeholder))
.into(new BitmapImageViewTarget(imageView) {
@Override
protected void setResource(Bitmap resource) {
RoundedBitmapDrawable circularBitmapDrawable =
RoundedBitmapDrawableFactory.
create(context.getResources(), resource);
circularBitmapDrawable.setCornerRadius(8);
imageView.setImageDrawable(circularBitmapDrawable);
}
});
}
/**
* 加载gif
*
* @param context 上下文
* @param url 图片路径
* @param imageView 承载图片ImageView
*/
@Override
public void loadAsGifImage(@NonNull Context context, @NonNull String url,
@NonNull ImageView imageView) {
Glide.with(context)
.asGif()
.load(url)
.into(imageView);
}
/**
* 加载图片列表图片
*
* @param context 上下文
* @param url 图片路径
* @param imageView 承载图片ImageView
*/
@Override
public void loadGridImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) {
Glide.with(context)
.load(url)
.override(200, 200)
.centerCrop()
.apply(new RequestOptions().placeholder(R.drawable.picture_image_placeholder))
.into(imageView);
}
private GlideEngine() {
}
private static GlideEngine instance;
public static GlideEngine createGlideEngine() {
if (null == instance) {
synchronized (GlideEngine.class) {
if (null == instance) {
instance = new GlideEngine();
}
}
}
return instance;
}
}
AddArtWorksBean 测试数据的实体类:
package com.example.testcsdnproject.picdemo;
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
public class AddArtWorksBean implements Serializable {
@SerializedName("artId")
private Long artId;
@SerializedName("artSaleState")
private String artSaleState;
@SerializedName("audioBand")
private String audioBand;
@SerializedName("authorName")
private String authorName;
@SerializedName("authorPortrait")
private String authorPortrait;
@SerializedName("changeSpeech")
private Integer changeSpeech;
@SerializedName("copyright")
private Long copyright;
@SerializedName("copyrightDirectory")
private String copyrightDirectory;
@SerializedName("cover")
private String cover;
@SerializedName("creativeProcess")
private String creativeProcess;
@SerializedName("fodder")
private Integer fodder;
@SerializedName("gatherId")
private Integer gatherId;
@SerializedName("gatherSort")
private String gatherSort;
@SerializedName("gdmoaId")
private Integer gdmoaId;
@SerializedName("id")
private Long id;
@SerializedName("imageHeight")
private Long imageHeight;
@SerializedName("imageWidth")
private Long imageWidth;
@SerializedName("introduce")
private String introduce;
@SerializedName("isAuthor")
private Integer isAuthor;
@SerializedName("isFamous")
private String isFamous;
@SerializedName("isShare")
private Integer isShare;
@SerializedName("material")
private String material;
@SerializedName("name")
private String name;
@SerializedName("organizationUserid")
private Long organizationUserid;
@SerializedName("parentDirectory")
private String parentDirectory;
@SerializedName("publishTime")
private String publishTime;
@SerializedName("releaseSort")
private Integer releaseSort;
@SerializedName("releaseTime")
private String releaseTime;
@SerializedName("secondMaterial")
private String secondMaterial;
@SerializedName("share")
private Integer share;
@SerializedName("shareDirectory")
private Integer shareDirectory;
@SerializedName("speechUrl")
private String speechUrl;
@SerializedName("state")
private Integer state;
@SerializedName("type")
private Integer type;
@SerializedName("url")
private String url;
@SerializedName("userId")
private Long userId;
@SerializedName("videoTime")
private String videoTime;
@SerializedName("viewNumber")
private Integer viewNumber;
@SerializedName("workGiveLike")
private Integer workGiveLike;
@SerializedName("workHeight")
private Long workHeight;
@SerializedName("workTags")
private String workTags;
@SerializedName("workWidth")
private Long workWidth;
@SerializedName("workYear")
private String workYear;
public Long getArtId() {
return artId;
}
public void setArtId(Long artId) {
this.artId = artId;
}
public String getArtSaleState() {
return artSaleState;
}
public void setArtSaleState(String artSaleState) {
this.artSaleState = artSaleState;
}
public String getAudioBand() {
return audioBand;
}
public void setAudioBand(String audioBand) {
this.audioBand = audioBand;
}
public String getAuthorName() {
return authorName;
}
public void setAuthorName(String authorName) {
this.authorName = authorName;
}
public String getAuthorPortrait() {
return authorPortrait;
}
public void setAuthorPortrait(String authorPortrait) {
this.authorPortrait = authorPortrait;
}
public Integer getChangeSpeech() {
return changeSpeech;
}
public void setChangeSpeech(Integer changeSpeech) {
this.changeSpeech = changeSpeech;
}
public Long getCopyright() {
return copyright;
}
public void setCopyright(Long copyright) {
this.copyright = copyright;
}
public String getCopyrightDirectory() {
return copyrightDirectory;
}
public void setCopyrightDirectory(String copyrightDirectory) {
this.copyrightDirectory = copyrightDirectory;
}
public String getCover() {
return cover;
}
public void setCover(String cover) {
this.cover = cover;
}
public String getCreativeProcess() {
return creativeProcess;
}
public void setCreativeProcess(String creativeProcess) {
this.creativeProcess = creativeProcess;
}
public Integer getFodder() {
return fodder;
}
public void setFodder(Integer fodder) {
this.fodder = fodder;
}
public Integer getGatherId() {
return gatherId;
}
public void setGatherId(Integer gatherId) {
this.gatherId = gatherId;
}
public String getGatherSort() {
return gatherSort;
}
public void setGatherSort(String gatherSort) {
this.gatherSort = gatherSort;
}
public Integer getGdmoaId() {
return gdmoaId;
}
public void setGdmoaId(Integer gdmoaId) {
this.gdmoaId = gdmoaId;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getImageHeight() {
return imageHeight;
}
public void setImageHeight(Long imageHeight) {
this.imageHeight = imageHeight;
}
public Long getImageWidth() {
return imageWidth;
}
public void setImageWidth(Long imageWidth) {
this.imageWidth = imageWidth;
}
public String getIntroduce() {
return introduce;
}
public void setIntroduce(String introduce) {
this.introduce = introduce;
}
public Integer getIsAuthor() {
return isAuthor;
}
public void setIsAuthor(Integer isAuthor) {
this.isAuthor = isAuthor;
}
public String getIsFamous() {
return isFamous;
}
public void setIsFamous(String isFamous) {
this.isFamous = isFamous;
}
public Integer getIsShare() {
return isShare;
}
public void setIsShare(Integer isShare) {
this.isShare = isShare;
}
public String getMaterial() {
return material;
}
public void setMaterial(String material) {
this.material = material;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getOrganizationUserid() {
return organizationUserid;
}
public void setOrganizationUserid(Long organizationUserid) {
this.organizationUserid = organizationUserid;
}
public String getParentDirectory() {
return parentDirectory;
}
public void setParentDirectory(String parentDirectory) {
this.parentDirectory = parentDirectory;
}
public String getPublishTime() {
return publishTime;
}
public void setPublishTime(String publishTime) {
this.publishTime = publishTime;
}
public Integer getReleaseSort() {
return releaseSort;
}
public void setReleaseSort(Integer releaseSort) {
this.releaseSort = releaseSort;
}
public String getReleaseTime() {
return releaseTime;
}
public void setReleaseTime(String releaseTime) {
this.releaseTime = releaseTime;
}
public String getSecondMaterial() {
return secondMaterial;
}
public void setSecondMaterial(String secondMaterial) {
this.secondMaterial = secondMaterial;
}
public Integer getShare() {
return share;
}
public void setShare(Integer share) {
this.share = share;
}
public Integer getShareDirectory() {
return shareDirectory;
}
public void setShareDirectory(Integer shareDirectory) {
this.shareDirectory = shareDirectory;
}
public String getSpeechUrl() {
return speechUrl;
}
public void setSpeechUrl(String speechUrl) {
this.speechUrl = speechUrl;
}
public Integer getState() {
return state;
}
public void setState(Integer state) {
this.state = state;
}
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getVideoTime() {
return videoTime;
}
public void setVideoTime(String videoTime) {
this.videoTime = videoTime;
}
public Integer getViewNumber() {
return viewNumber;
}
public void setViewNumber(Integer viewNumber) {
this.viewNumber = viewNumber;
}
public Integer getWorkGiveLike() {
return workGiveLike;
}
public void setWorkGiveLike(Integer workGiveLike) {
this.workGiveLike = workGiveLike;
}
public Long getWorkHeight() {
return workHeight;
}
public void setWorkHeight(Long workHeight) {
this.workHeight = workHeight;
}
public String getWorkTags() {
return workTags;
}
public void setWorkTags(String workTags) {
this.workTags = workTags;
}
public Long getWorkWidth() {
return workWidth;
}
public void setWorkWidth(Long workWidth) {
this.workWidth = workWidth;
}
public String getWorkYear() {
return workYear;
}
public void setWorkYear(String workYear) {
this.workYear = workYear;
}
}
OssService :(OSS上传服务)
package com.example.testcsdnproject.picdemo; import android.content.Context; import android.net.Uri; import android.text.TextUtils; import android.util.Log; import com.alibaba.sdk.android.oss.ClientConfiguration; import com.alibaba.sdk.android.oss.ClientException; import com.alibaba.sdk.android.oss.OSS; import com.alibaba.sdk.android.oss.OSSClient; import com.alibaba.sdk.android.oss.ServiceException; import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback; import com.alibaba.sdk.android.oss.callback.OSSProgressCallback; import com.alibaba.sdk.android.oss.common.auth.OSSCredentialProvider; import com.alibaba.sdk.android.oss.common.auth.OSSPlainTextAKSKCredentialProvider; import com.alibaba.sdk.android.oss.internal.OSSAsyncTask; import com.alibaba.sdk.android.oss.model.PutObjectRequest; import com.alibaba.sdk.android.oss.model.PutObjectResult; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Random; public class OssService { private OSS oss; private Context context; private ProgressCallback progressCallback; public final static String endpoint = "http://oss-cn-shenzhen.aliyuncs.com"; public final static String accessKeyId = "自己填参数";// public final static String accessKeySecret = "自己申请参数"; public final static String bucketName = "testCsdn"; public int mType; public OssService(Context context, int type) { this.context = context; this.mType = type; } public void initOSSClient() { //OSSCredentialProvider credentialProvider = new OSSStsTokenCredentialProvider("<StsToken.AccessKeyId>", "<StsToken.SecretKeyId>", "<StsToken.SecurityToken>"); //这个初始化安全性没有Sts安全,如需要很高安全性建议用OSSStsTokenCredentialProvider创建(上一行创建方式)多出的参数SecurityToken为临时授权参数 final OSSCredentialProvider credentialProvider = new OSSPlainTextAKSKCredentialProvider(accessKeyId, accessKeySecret); final ClientConfiguration conf = new ClientConfiguration(); conf.setConnectionTimeout(15 * 1000); // 连接超时,默认15秒 conf.setSocketTimeout(15 * 1000); // socket超时,默认15秒 conf.setMaxConcurrentRequest(9); // 最大并发请求数,默认5个 conf.setMaxErrorRetry(2); // 失败后最大重试次数,默认2次 // oss为全局变量,endpoint是一个OSS区域地址 oss = new OSSClient(context, endpoint, credentialProvider, conf); } public void beginupload(final Context context, String filename, String path, String osspath, final String callbackbody, String houzhuiming) { //通过填写文件名形成objectname,通过这个名字指定上传和下载的文件 String objectname = filename; if (objectname == null || objectname.equals("")) { // ToastUtils.sh("文件名不能为空"); return; } //下面3个参数依次为bucket名,Object名,上传文件路径 Log.e("colin: ", callbackbody + "=========" + osspath + objectname + getRandomString() + houzhuiming); PutObjectRequest put = new PutObjectRequest(bucketName, osspath + objectname +getRandomString() + houzhuiming, path); if (path == null || path.equals("")) { return; } put.setCallbackParam(new HashMap<String, String>() { { put("callbackUrl", "拼接服务器域名"/*AppConstant.OSS_IMAGE_URL*/); put("callbackBody", callbackbody); } }); // 异步上传,可以设置进度回调 put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() { @Override public void onProgress(PutObjectRequest request, long currentSize, long totalSize) { // LogUtil.d("currentSize: " + currentSize + " totalSize: " + totalSize); double progress = currentSize * 1.0 / totalSize * 100.f; if (progressCallback != null) { progressCallback.onProgressCallback(progress); } } }); @SuppressWarnings("rawtypes") OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() { @Override public void onSuccess(PutObjectRequest request, PutObjectResult result) { Log.d("colin", "UploadSuccess"); // 只有设置了servercallback,这个值才有数据 String serverCallbackReturnJson = result.getServerCallbackReturnBody(); Log.d("colin", serverCallbackReturnJson); try { JSONObject jsonObject = new JSONObject(serverCallbackReturnJson); String avatar_url = ""; switch (mType) { case 1: avatar_url = jsonObject.optString("avatar_url"); break; case 14: avatar_url = jsonObject.optString("avatar_url"); break; case 3: avatar_url = jsonObject.optString("imageUrl"); break; case 6: avatar_url = jsonObject.optString("artShowCoverUrl"); break; case 9: avatar_url = jsonObject.optString("photoUrl"); break; case 11: avatar_url = jsonObject.optString("videoCoverUrl"); break; case 2: avatar_url = ""; break; } mOnCallbackClickListener.onCallBack(avatar_url,serverCallbackReturnJson); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) { // 请求异常 // if (clientExcepion != null) { // 本地异常如网络异常等 // LogUtil.e("UploadFailure:表示向OSS发送请求或解析来自OSS的响应时发生错误。\n" + // " *例如,当网络不可用时,这个异常将被抛出"); clientExcepion.printStackTrace(); } if (serviceException != null) { // 服务异常 // LogUtil.e("UploadFailure:表示在OSS服务端发生错误"); Log.e("ErrorCode", serviceException.getErrorCode()); Log.e("RequestId", serviceException.getRequestId()); Log.e("HostId", serviceException.getHostId()); Log.e("RawMessage", serviceException.getRawMessage()); } } }); //task.cancel(); // 可以取消任务 // task.waitUntilFinished(); // 可以等待直到任务完成 } public ProgressCallback getProgressCallback() { return progressCallback; } public void setProgressCallback(ProgressCallback progressCallback) { this.progressCallback = progressCallback; } public interface ProgressCallback { void onProgressCallback(double progress); } private onCallbackClickListener mOnCallbackClickListener; public interface onCallbackClickListener { void onCallBack(String imageurl,String json); } public void setCallbackClickListener(onCallbackClickListener clickListener) { this.mOnCallbackClickListener = clickListener; } /*---------------------- 分割线 -------------------------*/ private onCallbackVideoClickListener mOnCallbackVideoClickListener; public interface onCallbackVideoClickListener { void onCallBackVideo(String imageurl,String coverUrl,String json); } public void setCallbackVideoClickListener(onCallbackVideoClickListener clickListener) { this.mOnCallbackVideoClickListener = clickListener; } /*---------------------- 分割线 -------------------------*/ /** * 阿里云OSS上传(默认是异步多文件上传) * 布展上传封面 * @param urls */ public void ossUpload(final List<String> urls) { if (urls.size() <= 0) { // 文件全部上传完毕,这里编写上传结束的逻辑,如果要在主线程操作,最好用Handler或runOnUiThead做对应逻辑 return;// 这个return必须有,否则下面报越界异常,原因自己思考下哈 } final String url = urls.get(0); if (TextUtils.isEmpty(url)) { urls.remove(0); // url为空就没必要上传了,这里做的是跳过它继续上传的逻辑。 ossUpload(urls); return; } Uri uri = Uri.parse(url); File file = new File(uri.getPath()); if (null == file || !file.exists()) { urls.remove(0); // 文件为空或不存在就没必要上传了,这里做的是跳过它继续上传的逻辑。 ossUpload(urls); return; } // 文件后缀 String fileSuffix = ""; if (file.isFile()) { // 获取文件后缀名 fileSuffix = file.getName().substring(file.getName().lastIndexOf(".")); } // 文件标识符objectKey final String objectKey = "alioss_" + System.currentTimeMillis() + fileSuffix; final String objectKey1 = "image/" +System.currentTimeMillis()+getFileName(url)+"_"+ getRandomString()+"_"+urls.size()+ fileSuffix; // 下面3个参数依次为bucket名,ObjectKey名,上传文件路径 PutObjectRequest put = new PutObjectRequest(bucketName, objectKey1, url); // 设置进度回调 put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() { @Override public void onProgress(PutObjectRequest request, long currentSize, long totalSize) { // 进度逻辑 } }); // 异步上传 OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() { @Override public void onSuccess(PutObjectRequest request, PutObjectResult result) { // 上传成功 urls.remove(0); ossUpload(urls);// 递归同步效果 // 只有设置了servercallback,这个值才有数据 String serverCallbackReturnJson = result.getServerCallbackReturnBody(); mOnCallbackClickListener.onCallBack(objectKey1,serverCallbackReturnJson); } @Override public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) { // 上传失败 // 请求异常 if (clientExcepion != null) { // 本地异常如网络异常等 clientExcepion.printStackTrace(); } if (serviceException != null) { // 服务异常 Log.e("ErrorCode", serviceException.getErrorCode()); Log.e("RequestId", serviceException.getRequestId()); Log.e("HostId", serviceException.getHostId()); Log.e("RawMessage", serviceException.getRawMessage()); } } }); // task.cancel(); // 可以取消任务 // task.waitUntilFinished(); // 可以等待直到任务完成 } /** * 批量上传 包括视频 * @param urls */ public void ossUploadVideo(final List<AddArtWorksBean> urls) { //AddArtWorksBean自己定义的作品实体类,包括url跟类型 if (urls.size() <= 0) { // 文件全部上传完毕,这里编写上传结束的逻辑,如果要在主线程操作,最好用Handler或runOnUiThead做对应逻辑 return;// 这个return必须有,否则下面报越界异常,原因自己思考下哈 } final String url = urls.get(0).getUrl(); String cover = urls.get(0).getCover(); int type = 0; if (null!=urls.get(0).getType()){ type = urls.get(0).getType(); //文件类型 2视频 其他图片 } if (TextUtils.isEmpty(url)) { urls.remove(0); // url为空就没必要上传了,这里做的是跳过它继续上传的逻辑。 ossUploadVideo(urls); return; } Uri uri = Uri.parse(url); File file = new File(uri.getPath()); if (null == file || !file.exists()) { // if (null == file || uri==null) { urls.remove(0); // 文件为空或不存在就没必要上传了,这里做的是跳过它继续上传的逻辑。 ossUploadVideo(urls); return; } // 文件后缀 String fileSuffix = ""; if (file.isFile()) { // 获取文件后缀名 fileSuffix = file.getName().substring(file.getName().lastIndexOf(".")); } // 文件标识符objectKey final String objectKey = "alioss_" + System.currentTimeMillis() + fileSuffix; final String objectKey1 = "image/" +System.currentTimeMillis()+getFileName(url)+"_"+ getRandomString()+"_"+urls.size()+ fileSuffix; String objectKeyCover = null; if (cover!=null){ objectKeyCover = "cover/" +System.currentTimeMillis()+getFileName(cover)+"_"+ getRandomString()+"_"+urls.size()+".png" ; } // 下面3个参数依次为bucket名,ObjectKey名,上传文件路径 PutObjectRequest put = new PutObjectRequest(bucketName, objectKey1, url); PutObjectRequest putCover = new PutObjectRequest(bucketName, objectKeyCover, cover); // 设置进度回调 put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() { @Override public void onProgress(PutObjectRequest request, long currentSize, long totalSize) { // 进度逻辑 double progress = currentSize * 1.0 / totalSize * 100.f; if (progressCallback != null) { progressCallback.onProgressCallback(progress); } } }); putCover.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() { @Override public void onProgress(PutObjectRequest request, long currentSize, long totalSize) { double progress = currentSize * 1.0 / totalSize * 100.f; if (progressCallback != null) { progressCallback.onProgressCallback(progress); } } }); // 异步上传 if (type==2){ OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() { @Override public void onSuccess(PutObjectRequest request, PutObjectResult result) { // 上传成功 } @Override public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) { // 上传失败 if (clientExcepion != null) { clientExcepion.printStackTrace(); } if (serviceException != null) { Log.e("ErrorCode", serviceException.getErrorCode()); Log.e("RequestId", serviceException.getRequestId()); Log.e("HostId", serviceException.getHostId()); Log.e("RawMessage", serviceException.getRawMessage()); } } }); String finalObjectKeyCover = objectKeyCover; OSSAsyncTask taskCover = oss.asyncPutObject(putCover, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() { @Override public void onSuccess(PutObjectRequest request, PutObjectResult result) { // 上传成功 urls.remove(0); ossUploadVideo(urls);// 递归同步效果 // 只有设置了servercallback,这个值才有数据 String serverCallbackReturnJson = result.getServerCallbackReturnBody(); mOnCallbackVideoClickListener.onCallBackVideo(objectKey1, finalObjectKeyCover,serverCallbackReturnJson); } @Override public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) { // 上传失败 if (clientExcepion != null) { clientExcepion.printStackTrace(); } if (serviceException != null) { Log.e("ErrorCode", serviceException.getErrorCode()); Log.e("RequestId", serviceException.getRequestId()); Log.e("HostId", serviceException.getHostId()); Log.e("RawMessage", serviceException.getRawMessage()); } } }); }else{ String finalObjectKeyCover1 = objectKeyCover; OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() { @Override public void onSuccess(PutObjectRequest request, PutObjectResult result) { // 上传成功 urls.remove(0); ossUploadVideo(urls);// 递归同步效果 // 只有设置了servercallback,这个值才有数据 String serverCallbackReturnJson = result.getServerCallbackReturnBody(); // mOnCallbackClickListener.onCallBack(objectKey1,serverCallbackReturnJson); mOnCallbackVideoClickListener.onCallBackVideo(objectKey1, finalObjectKeyCover1,serverCallbackReturnJson); } @Override public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) { // 上传失败 if (clientExcepion != null) { clientExcepion.printStackTrace(); } if (serviceException != null) { Log.e("ErrorCode", serviceException.getErrorCode()); Log.e("RequestId", serviceException.getRequestId()); Log.e("HostId", serviceException.getHostId()); Log.e("RawMessage", serviceException.getRawMessage()); } } }); } } /** * 发布动态上传图片 * @param urls */ public void ossUploadDynamic(final List<String> urls) { if (urls.size() <= 0) { // 文件全部上传完毕,这里编写上传结束的逻辑,如果要在主线程操作,最好用Handler或runOnUiThead做对应逻辑 return;// 这个return必须有,否则下面报越界异常,原因自己思考下哈 } final String url = urls.get(0); if (TextUtils.isEmpty(url)) { urls.remove(0); ossUploadDynamic(urls); return; } Uri uri = Uri.parse(url); File file = new File(uri.getPath()); if (null == file || !file.exists()) { urls.remove(0); ossUploadDynamic(urls); return; } String fileSuffix = ""; if (file.isFile()) { fileSuffix = file.getName().substring(file.getName().lastIndexOf(".")); } if (url.contains("mp4")){ final String objectKeyVideo = "findDynamicVideo/" +System.currentTimeMillis()+ getFileName(url)+"_"+getRandomString()+"_"+urls.size()+ fileSuffix; PutObjectRequest put = new PutObjectRequest(bucketName, objectKeyVideo, url); put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() { @Override public void onProgress(PutObjectRequest request, long currentSize, long totalSize) { } }); OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() { @Override public void onSuccess(PutObjectRequest request, PutObjectResult result) { // 上传成功 urls.remove(0); ossUploadDynamic(urls);// 递归同步效果 // 只有设置了servercallback,这个值才有数据 String serverCallbackReturnJson = result.getServerCallbackReturnBody(); mOnCallbackClickListener.onCallBack(objectKeyVideo,serverCallbackReturnJson); } @Override public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) { // 上传失败 if (clientExcepion != null) { clientExcepion.printStackTrace(); } if (serviceException != null) { } } }); }else{ final String objectKeyImage = "findDynamicImage/" +System.currentTimeMillis()+ getFileName(url)+"_"+getRandomString()+"_"+urls.size()+ fileSuffix; PutObjectRequest put = new PutObjectRequest(bucketName, objectKeyImage, url); put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() { @Override public void onProgress(PutObjectRequest request, long currentSize, long totalSize) { } }); OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() { @Override public void onSuccess(PutObjectRequest request, PutObjectResult result) { // 上传成功 urls.remove(0); ossUploadDynamic(urls);// 递归同步效果 // 只有设置了servercallback,这个值才有数据 String serverCallbackReturnJson = result.getServerCallbackReturnBody(); mOnCallbackClickListener.onCallBack(objectKeyImage,serverCallbackReturnJson); } @Override public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) { // 上传失败 if (clientExcepion != null) { clientExcepion.printStackTrace(); } if (serviceException != null) { } } }); } } /** * 阿里云OSS上传(默认是异步多文件上传) * 只上传图片 * 在主页画作装裱中使用 * @param urls */ public void ossUploadSculptureSquare(final List<String> urls,String OssFileName) { if (urls.size() <= 0) { // 文件全部上传完毕,这里编写上传结束的逻辑,如果要在主线程操作,最好用Handler或runOnUiThead做对应逻辑 return;// 这个return必须有,否则下面报越界异常,原因自己思考下哈 } final String url = urls.get(0); if (TextUtils.isEmpty(url)) { urls.remove(0); // url为空就没必要上传了,这里做的是跳过它继续上传的逻辑。 ossUpload(urls); return; } Uri uri = Uri.parse(url); File file = new File(uri.getPath()); if (null == file || !file.exists()) { // if (null == file || uri==null) { urls.remove(0); // 文件为空或不存在就没必要上传了,这里做的是跳过它继续上传的逻辑。 ossUpload(urls); return; } // 文件后缀 String fileSuffix = ""; if (file.isFile()) { // 获取文件后缀名 fileSuffix = file.getName().substring(file.getName().lastIndexOf(".")); } // 文件标识符objectKey final String objectKey1 = OssFileName+System.currentTimeMillis()+ getFileName(url)+"_"+getRandomString()+"_"+urls.size()+ fileSuffix; // 下面3个参数依次为bucket名,ObjectKey名,上传文件路径 PutObjectRequest put = new PutObjectRequest(bucketName, objectKey1, url); // 设置进度回调 put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() { @Override public void onProgress(PutObjectRequest request, long currentSize, long totalSize) { // 进度逻辑 } }); // 异步上传 OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() { @Override public void onSuccess(PutObjectRequest request, PutObjectResult result) { // 上传成功 urls.remove(0); ossUpload(urls);// 递归同步效果 // 只有设置了servercallback,这个值才有数据 String serverCallbackReturnJson = result.getServerCallbackReturnBody(); mOnCallbackClickListener.onCallBack(objectKey1,serverCallbackReturnJson); } @Override public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) { // 上传失败 // 请求异常 if (clientExcepion != null) { // 本地异常如网络异常等 clientExcepion.printStackTrace(); } if (serviceException != null) { } } }); } public static String getRandomString() { String base = "abcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < base.length(); i++) { int number = random.nextInt(base.length()); sb.append(base.charAt(number)); } return sb.toString(); } public static String getFileName(String pathandname) { int start = pathandname.lastIndexOf("/"); int end = pathandname.lastIndexOf("."); if (start != -1 && end != -1) { return pathandname.substring(start + 1, end); } else { return null; } } }
这里就已经完成了正常的批量选择图片并上传了。
================分割线=========================
批量上传图片视频
MassUploadActivity:
package com.example.testcsdnproject.picdemo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.example.testcsdnproject.R;
import com.luck.picture.lib.PictureSelector;
import com.luck.picture.lib.config.PictureMimeType;
import com.luck.picture.lib.entity.LocalMedia;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import okhttp3.RequestBody;
/**
* 批量上传图片视频
*/
public class MassUploadActivity extends AppCompatActivity {
private RecyclerView rv_mass_upload;
private TextView tv_upload;
private TextView tv_count,tv_total;
private ProgressBar progressBar;
// private PicAdapter libAdapter; //返回图片适配器 自己写一个吧 就一个imageview 数据体是List<LocalMedia> list ,加载图片:list.get(0).getPath
private Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
tv_count.setText(uploadedCount+"");
tv_total.setText("/"+uploadedTotalCount);
progressBar.setMax(uploadedTotalCount);
progressBar.setProgress(uploadedCount);
break;
case 2:
addArtWorksBeans.clear();
for (int i = 0;i<backArtWorksBeans.size();i++){
AddArtWorksBean addArtWorksBean =new AddArtWorksBean();
addArtWorksBean.setUrl(paintingUrlsCallback.get(i));
addArtWorksBean.setName(paintingUrlsCallback.get(i));
addArtWorksBean.setImageHeight(Long.valueOf(albumSelectList.get(i).getHeight()));
addArtWorksBean.setImageWidth(Long.valueOf(albumSelectList.get(i).getWidth()));
// addArtWorksBean.setType(selectList.get(i).getPath().contains(".mp4")?2:1);
addArtWorksBean.setType(albumSelectList.get(i).getRealPath().contains("mp4")?2:1);
String url_cover = backArtWorksBeans.get(i).getCover()/*+"?spm=qipa250&x-oss-process=video/snapshot,t_1000,f_jpg,w_0,h_0,m_fast"*/;
addArtWorksBean.setCover(albumSelectList.get(i).getRealPath().contains("mp4")?url_cover:"");
addArtWorksBeans.add(addArtWorksBean);
}
//请求接口上传数据
/*String json = new Gson().toJson(addArtWorksBeans);
RequestBody requestBody = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"), json);
addArtWorksPresenter.addArtWorks(requestBody);*/
//上传成功后更新列表数据
// libAdapter.setNewData();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mass_upload);
rv_mass_upload = findViewById(R.id.rv_mass_upload);
tv_upload = findViewById(R.id.tv_upload);
tv_count = findViewById(R.id.tv_upload_count);
tv_total = findViewById(R.id.tv_upload_total);
progressBar = findViewById(R.id.upload_progress_bar);
// libAdapter = new PicAdapter(null);
// rv_mass_upload.setAdapter(libAdapter);
StaggeredGridLayoutManager libLayoutManager = new StaggeredGridLayoutManager(4,
StaggeredGridLayoutManager.VERTICAL);
rv_mass_upload.setLayoutManager(libLayoutManager);
openPic();
tv_upload.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
openPic();
}
});
}
//打开相册选择图片
private void openPic(){
PictureSelector.create(MassUploadActivity.this)
.openGallery(PictureMimeType.ofAll())
.imageEngine(GlideEngine.createGlideEngine())
// .loadImageEngine(GlideEngine.createGlideEngine())// 图片加载引擎 需要 implements ImageEngine接口
// .enableCrop(true)//是否开启裁剪
.rotateEnabled(false)//裁剪是否可旋转图片
.isMaxSelectEnabledMask(true)//选择条件达到阀时列表是否启用蒙层效果
// .isCamera(true)//列表是否显示拍照按钮
.maxSelectNum(20)// 最大图片选择数量
// .minSelectNum(1)// 最小选择数量
.maxVideoSelectNum(20) // 视频最大选择数量
// .queryMaxFileSize(10)// 只查多少M以内的图片、视频、音频 单位。
.isDragFrame(true)//是否可拖动裁剪框(固定)
.isWithVideoImage(true)//图片和视频是否可以同选,只在ofAll模式下有效
.imageSpanCount(6)// 每行显示个数
.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)// 设置相册Activity方向,不设置默认使用系统
.isOriginalImageControl(true)// 是否显示原图控制按钮,如果设置为true则用户可以自由选择是否使用原图,压缩、裁剪功能将会失效
// .withAspectRatio(1, 1)//裁剪比例
.isWeChatStyle(true)
.forResult(824);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == 824) { //新版上传相册作品
albumSelectList = PictureSelector.obtainMultipleResult(data);
calculationAverageProgress();
uploadPainting();
}
}
/** 等待对话框 */
// private BaseDialog mWaitUploadDialog;
boolean isCancelUpload;
private String filePaintingPath = "";
private List<String> paintingUrls = new ArrayList<>();
private List<String> paintingUrlsCallback = new ArrayList<>();
private List<AddArtWorksBean> addArtWorksBeans = new ArrayList<>();
private List<AddArtWorksBean> backArtWorksBeans = new ArrayList<>();
private List<LocalMedia> albumSelectList = new ArrayList<>();
double mAverageProgress;//每一个需要上传至oss的本地作品最大的进度值
int uploadedTotalCount = 0;//需要上传的所有数量
int uploadedCount = 0;//已上传数量
OssService ossService;
private List<AddArtWorksBean> toOssBeans = new ArrayList<>();
private void uploadPainting(){
uploadedCount = 0;
isCancelUpload = false;
/*OssService*/ ossService = new OssService(this, 1);
ossService.initOSSClient();
if (albumSelectList.size() == 0) {
Toast.makeText(MassUploadActivity.this,"请添加作品",Toast.LENGTH_SHORT).show();
return;
}
paintingUrls.clear();
toOssBeans.clear();
for (int i = 0; i<albumSelectList.size();i++){
// filePath = selectList.get(i).getPath();
filePaintingPath = albumSelectList.get(i).getRealPath();
paintingUrls.add(filePaintingPath);
AddArtWorksBean addArtWorksBean = new AddArtWorksBean();
addArtWorksBean.setUrl(filePaintingPath);
if (filePaintingPath.contains("mp4")){
addArtWorksBean.setType(2);
addArtWorksBean.setCover(saveBitmap2file(albumSelectList.get(i).getRealPath(),"artvr_"+System.currentTimeMillis()));
}else{
addArtWorksBean.setType(1);
}
toOssBeans.add(addArtWorksBean);
}
ossService.ossUploadVideo(toOssBeans);
/* if (mWaitUploadDialog == null) {
mWaitUploadDialog = new UploadProgressDialog.Builder(ExhibitActivity.this)
// 消息文本可以不用填写
// .setMessage("正在上传,请耐心等待("+uploadedCount+"/"+uploadedTotalCount+")")
.setMessage(uploadedCount+"",uploadedTotalCount+"")
.setUploadCloseListener(this)
.create();
}
if (!mWaitUploadDialog.isShowing()) {
mWaitUploadDialog.show();
}*/
paintingUrlsCallback.clear();
backArtWorksBeans.clear();
//上传回调
ossService.setCallbackVideoClickListener(new OssService.onCallbackVideoClickListener() {
@Override
public void onCallBackVideo(String imageurl, String coverUrl, String json) {
if (!isCancelUpload){
uploadedCount++;
mHandler.sendEmptyMessage(1);
Log.d("colinworkossVideo","imageUrl = "+ imageurl+" coverUrl : "+ coverUrl+ " JSONStr = "+json);
paintingUrlsCallback.add(imageurl);
AddArtWorksBean addArtWorksBean = new AddArtWorksBean();
addArtWorksBean.setUrl(imageurl);
addArtWorksBean.setCover(coverUrl);
backArtWorksBeans.add(addArtWorksBean);
if (backArtWorksBeans.size() == albumSelectList.size()){
mHandler.sendEmptyMessage(2);
}
}else{
ossService.initOSSClient();
}
}
});
}
/* @Override
public void CloseUploadDialog() {
if (mWaitUploadDialog.isShowing()){
mWaitUploadDialog.dismiss();
isCancelUpload = true;
mWaitUploadDialog = null;
}
}*/
/**
* 计算每一个作品的上传进度
*/
private void calculationAverageProgress() {
uploadedTotalCount = albumSelectList.size();
if (uploadedTotalCount == 0) {
return;
}
mAverageProgress = format(100d / uploadedTotalCount, 2);
}
/**
* 格式化精度
*
* @param v
* @param point 小数位数
* @return double
*/
public static Double format(double v, int point) {
BigDecimal b = new BigDecimal(v);
return b.setScale(point, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* 将bitmap转文件
* <p>
* // * @param bmp
* // * @param filename
*
* @return
*/
public String saveBitmap2file(String videoFilePath, String imagename) {
String imagepath = "";
//创建MediaMetadataRetriever对象
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
//设置资源位置
if (videoFilePath == null) {
return "";
}
try {
mmr.setDataSource(videoFilePath);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
//获取第一帧图像的bitmap对象
Bitmap bitmap = mmr.getFrameAtTime();
// File file = new File(Environment.getExternalStorageDirectory() + "/" + imagename + ".png");//将要保存图片的路径
File file = new File( getApplicationContext().getFilesDir() + "/" + imagename + ".png");//将要保存图片的路径
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
bos.flush();
bos.close();
// imagepath = Environment.getExternalStorageDirectory() + "/" + imagename + ".png";
imagepath = getApplicationContext().getFilesDir() + "/" + imagename + ".png";
//通知图库有更新
// MyApplication.mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + file.getPath())));
} catch (IOException e) {
e.printStackTrace();
}
return imagepath;
}
}
xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".picdemo.MassUploadActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_mass_upload"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/tv_upload"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:text="批量上传"
android:textColor="@color/white"
android:textSize="20sp"
android:gravity="center"
android:background="@color/picture_color_blue"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="30dp"
/>
<RelativeLayout
android:id="@+id/rl_loading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/tv_upload"
android:layout_marginBottom="20dp">
<LinearLayout
android:id="@+id/ll_upload_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="上传中"
android:textSize="16sp"
android:textColor="#040404"
/>
<TextView
android:id="@+id/tv_upload_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textSize="16sp"
android:textColor="#1B73E8"
android:layout_marginLeft="30dp"
/>
<TextView
android:id="@+id/tv_upload_total"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="/0"
android:textSize="16sp"
android:textColor="#000000"
/>
</LinearLayout>
<ProgressBar
android:id="@+id/upload_progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="6dp"
android:layout_below="@+id/ll_upload_amount"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp"
android:layout_marginLeft="40dp"
android:layout_marginRight="40dp"
android:layout_marginBottom="6dp"
android:progressDrawable="@drawable/progress_bar_blue_r3" />
</RelativeLayout>
</RelativeLayout>
在drawable中添加progress_bar_blue_r3.xml:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<!--<corners android:topRightRadius="20dp"
android:bottomRightRadius="20dp" />-->
<corners android:radius="3dp"/>
<solid android:color="#FFFFFF"/>
</shape>
</item>
<item android:id="@android:id/secondaryProgress">
<scale android:scaleWidth="100%">
<shape>
<corners android:radius="3dp"/>
<solid android:color="#FF392B"/>
</shape>
</scale>
</item>
<item android:id="@android:id/progress">
<scale android:scaleWidth="100%"
android:drawable="@drawable/progress_bar_ct" />
</item>
</layer-list>
progress_bar_ct.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- solid指定形状的填充色,只有android:color一个属性 -->
<solid android:color="#1B73E8" />
<!-- padding设置内容区域离边界的间距 -->
<!-- corners设置圆角,只适用于rectangle -->
<!--4个角都是圆角-->
<corners android:radius="3dp" />
<!--只需要右端圆角-->
<!--<corners android:bottomRightRadius="20dp"
android:topRightRadius="20dp"/>-->
</shape>
裁剪图片:
扫描二维码关注公众号,回复:
17554973 查看本文章

CutImageMoreSizeActivity 自定义多尺寸裁剪图片
package com.example.testcsdnproject.picdemo;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.listener.OnItemClickListener;
import com.example.testcsdnproject.R;
import com.theartofdev.edmodo.cropper.CropImageView;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import top.zibin.luban.CompressionPredicate;
import top.zibin.luban.Luban;
import top.zibin.luban.OnCompressListener;
/**
* 多尺寸裁剪图片
*/
public class CutImageMoreSizeActivity extends AppCompatActivity {
CropImageView civ_more_size;
RecyclerView rv_size;
TextView tv_back,tv_finish;
private CutImageSizeAdapter cutImageSizeAdapter;
private List<String> sizeData = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cut_image_more_size);
civ_more_size = findViewById(R.id.civ_more_size);
rv_size = findViewById(R.id.rv_size);
tv_back = findViewById(R.id.tv_back);
tv_finish = findViewById(R.id.tv_check_finish);
initView();
tv_back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
tv_finish.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (civ_more_size.getCroppedImage() == null) {
return;
}
String path = getPath() + "/"+currentTimeMillis() + ".jpg";
BitmapFactory.Options options = new BitmapFactory.Options();
/*Matrix matrix = new Matrix();
matrix.setScale(0.25f, 0.25f);*/
Bitmap bm = Bitmap.createBitmap(civ_more_size.getCroppedImage(), 0, 0, civ_more_size.getCroppedImage().getWidth(),
civ_more_size.getCroppedImage().getHeight(), null/*matrix*/, true);
// FileInfoUtils.saveBitmapFile(bm, path);
Log.d("colinimagewh: ", "保存文件大小"+bm.getByteCount() +" byte");
saveBitmap(bm, path);
Luban.with(CutImageMoreSizeActivity.this)
.load(path)
.ignoreBy(100) //不压缩的阈值 单位为k 80
.setTargetDir(getPath())
.filter(new CompressionPredicate() {
@Override
public boolean apply(String path) {
return true;
}
})
.setCompressListener(new OnCompressListener() {
@Override
public void onStart() {
// TODO 压缩开始前调用,可以在方法内启动 loading UI
}
@Override
public void onSuccess(File file) {
// TODO 压缩成功后调用,返回压缩后的图片文件
Log.d("Cutpath: ", "file = "+file.getPath());
Log.d("Cutpath: ", "file.size = "+file.length());
options.inJustDecodeBounds = true;
Log.e("colinimagewh: ", options.outHeight + "----" + options.outWidth);
Intent intent = new Intent();
intent.putExtra("cutimagepath", file.getPath());
setResult(RESULT_OK, intent);
finish();
}
@Override
public void onError(Throwable e) {
// TODO 当压缩过程出现问题时调用
}
}).launch();
}
});
rv_size.addOnItemTouchListener(new OnItemClickListener() {
@Override
public void onSimpleItemClick(BaseQuickAdapter adapter, View view, int position) {
switch (cutImageSizeAdapter.getItem(position)){
case "3":
civ_more_size.setAspectRatio(3,4);
break;
case "1":
civ_more_size.setAspectRatio(1,1);
break;
case "9":
civ_more_size.setAspectRatio(9,16);
break;
case "4":
civ_more_size.setAspectRatio(4,3);
break;
case "16":
civ_more_size.setAspectRatio(16,9);
break;
}
}
});
}
private void initView() {
imageCompress(getIntent().getStringExtra("imageurl"));
civ_more_size.setAspectRatio(3,4);
sizeData.clear();
sizeData.add("3");
sizeData.add("1");
// sizeData.add("9");
sizeData.add("4");
// sizeData.add("16");
cutImageSizeAdapter = new CutImageSizeAdapter(sizeData);
rv_size.setAdapter( cutImageSizeAdapter);
rv_size.setLayoutManager(new LinearLayoutManager(CutImageMoreSizeActivity.this,RecyclerView.HORIZONTAL,false));
}
/**
* 保存图片到指定路径
* @param bitmap
* @param path
*/
public static void saveBitmap(Bitmap bitmap,String path){
// File file = new File(Environment.getExternalStorageDirectory(), "ArtvrTwo"+System.currentTimeMillis() + ".jpg");
File file = new File(path);
Log.d("rockerTestCapture","截图保存路径 :" + file.getPath());
try {
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
// ShareZipToWX.shareWechatFriend(this,file); //不用分享
} catch (java.io.IOException e) {
e.printStackTrace();
}
}
public String currentTimeMillis() {
long time = System.currentTimeMillis();//获取系统时间的10位的时间戳
String str = String.valueOf(time);
return str;
}
private void imageCompress(String url) {
// File beforeFile = new File(url);
// Log.d("colinimagewh: ", "压缩前文件大小"+beforeFile.length() +" byte");
Luban.with(this)
.load(url)
.ignoreBy(100) //不压缩的阈值 单位为k 80
.setTargetDir(getPath())
.filter(new CompressionPredicate() {
@Override
public boolean apply(String path) {
return true;
}
})
.setCompressListener(new OnCompressListener() {
@Override
public void onStart() {
// TODO 压缩开始前调用,可以在方法内启动 loading UI
}
@Override
public void onSuccess(File file) {
// TODO 压缩成功后调用,返回压缩后的图片文件
// Log.d("colinimagewh: ", "压缩后文件大小"+file.length() +" byte");
// localImageUrl = file.getPath();
// glideImage( localImageUrl);
civ_more_size.setImageUriAsync(Uri.fromFile(file));
}
@Override
public void onError(Throwable e) {
// TODO 当压缩过程出现问题时调用
}
}).launch();
}
private String getPath(){
File file = new File(getFilesDir(),"/Crop");
if (!file.exists()) {
file.mkdirs();
}
return file.getPath();
}
/**
* 质量压缩方法
* @param image
* @return
*/
public static Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
int options = 90;
while (baos.toByteArray().length / 1024 > 100) { // 循环判断如果压缩后图片是否大于100kb,大于继续压缩
baos.reset(); // 重置baos即清空baos
image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 这里压缩options%,把压缩后的数据存放到baos中
options -= 10;// 每次都减少10
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片
return bitmap;
}
/**
* 压缩图片到目标大小以下
*
* @param file
* @param targetSize
*/
public void compressBmpFileToTargetSize(File file, long targetSize) {
// Log.d(TAG, String.format("compressBmpFileToTargetSize start file.length():%d", file.length()));
if (file.length() > targetSize) {
// 每次宽高各缩小一半
int ratio = 2;
// 获取图片原始宽高
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
int targetWidth = options.outWidth / ratio;
int targetHeight = options.outHeight / ratio;
// 压缩图片到对应尺寸
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int quality = 100;
Bitmap result = generateScaledBmp(bitmap, targetWidth, targetHeight, baos, quality);
// 计数保护,防止次数太多太耗时。
int count = 0;
while (baos.size() > targetSize && count <= 10) {
targetWidth /= ratio;
targetHeight /= ratio;
count++;
// 重置,不然会累加
baos.reset();
result = generateScaledBmp(result, targetWidth, targetHeight, baos, quality);
}
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// Log.d(TAG, String.format("compressBmpFileToTargetSize end file.length():%d", file.length()));
}
/**
* 图片缩小一半
*
* @param srcBmp
* @param targetWidth
* @param targetHeight
* @param baos
* @param quality
* @return
*/
private Bitmap generateScaledBmp(Bitmap srcBmp, int targetWidth, int targetHeight, ByteArrayOutputStream baos, int quality) {
Bitmap result = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Rect rect = new Rect(0, 0, result.getWidth(), result.getHeight());
canvas.drawBitmap(srcBmp, null, rect, null);
if (!srcBmp.isRecycled()) {
srcBmp.recycle();
}
result.compress(Bitmap.CompressFormat.JPEG, quality, baos);
return result;
}
}
xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#1a1a1a"
>
<RelativeLayout
android:id="@+id/rl_top"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="40dp">
<TextView
android:id="@+id/tv_back"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginLeft="16dp"
android:gravity="center"
android:text="返回"
android:textColor="@color/white"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="编辑图片"
android:textColor="#FFFFFFFF"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_check_finish"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:layout_marginRight="16dp"
android:textColor="@color/white"
android:textSize="16sp"
android:gravity="center"
android:text="完成"/>
</RelativeLayout>
<com.theartofdev.edmodo.cropper.CropImageView
android:id="@+id/civ_more_size"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/tv_tips"
android:layout_below="@+id/rl_top"
android:layout_centerHorizontal="true"
android:layout_marginBottom="20dp"
android:visibility="visible"
app:cropBorderCornerColor="@color/white"
app:cropBorderCornerOffset="0dp"
app:cropBorderCornerThickness="5dp"
app:cropBorderLineColor="@color/white"
app:cropBorderLineThickness="1dp"
app:cropGuidelines="on"
app:cropGuidelinesColor="@color/white"
app:cropInitialCropWindowPaddingRatio="0"
app:cropSnapRadius="0dp" />
<TextView
android:id="@+id/tv_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选择上传尺寸并裁剪"
android:textSize="12sp"
android:textColor="#FF848484"
android:layout_centerHorizontal="true"
android:layout_above="@+id/rv_size"
android:layout_marginBottom="15dp"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_size"
android:layout_width="match_parent"
android:layout_height="72dp"
android:layout_alignParentBottom="true"
android:layout_marginLeft="16dp"
android:layout_marginBottom="32dp"
/>
</RelativeLayout>
CutImageSizeAdapter:
package com.example.testcsdnproject.picdemo;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.example.testcsdnproject.R;
import java.util.List;
public class CutImageSizeAdapter extends BaseQuickAdapter<String, BaseViewHolder> {
// private ImageView iv_rectangle;
private TextView tv_size;
public CutImageSizeAdapter(@Nullable List<String> data) {
super(R.layout.item_cut_image_size,data);
}
@Override
protected void convert(BaseViewHolder helper, String item) {
// iv_rectangle = helper.getView(R.id.iv_rectangle);
tv_size = helper.getView(R.id.tv_size);
if (item.equals("3")){
// iv_rectangle.setImageResource(R.mipmap.ic_rectangle_3_4);
tv_size.setText("3:4");
}else if (item.equals("1")){
// iv_rectangle.setImageResource(R.mipmap.ic_rectangle_1_1);
tv_size.setText("1:1");
}else if (item.equals("9")){
// iv_rectangle.setImageResource(R.mipmap.ic_rectangle_9_16);
tv_size.setText("9:16");
}else if (item.equals("4")){
// iv_rectangle.setImageResource(R.mipmap.ic_rectangle_4_3);
tv_size.setText("4:3");
}else if (item.equals("16")){
// iv_rectangle.setImageResource(R.mipmap.ic_rectangle_16_9);
tv_size.setText("16:9");
}else{
// iv_rectangle.setImageResource(R.mipmap.ic_rectangle_3_4);
tv_size.setText("3:4");
}
}
}
xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="71dp"
android:layout_height="@dimen/dp_40"
android:background="#252525"
android:layout_marginRight="7dp"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_centerInParent="true">
<!-- 尺寸选中框 需要自己加
<ImageView
android:id="@+id/iv_rectangle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_rectangle_3_4"
android:layout_marginRight="@dimen/dp_8"
android:layout_gravity="center_vertical"
/>-->
<TextView
android:id="@+id/tv_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="3:4"
android:textSize="14sp"
android:textColor="#FFE5E5E5"
android:layout_gravity="center_vertical"
/>
</LinearLayout>
</RelativeLayout>
使用:
//带图片地址传过去
Intent intentCut = new Intent(this, CutImageMoreSizeActivity.class);
intentCut.putExtra("imageurl", afterAiPath);
startActivityForResult(intentCut, 530);
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case 530: //拿到裁剪好的图片操作
if (data == null){
return;
}
afterAiPathCut = data.getStringExtra("cutimagepath");
bitmapAfter = BitmapFactory.decodeFile(afterAiPathCut);
int cutWidth = bitmapAfter.getWidth();
int cutHeight = bitmapAfter.getHeight();
if (cutWidth == cutHeight){
cutWidth = 512;
cutHeight = 512;
}else if (cutWidth > cutHeight){
cutWidth = 800;
cutHeight = 600;
}else{
cutWidth = 600;
cutHeight = 800;
}
ViewGroup.LayoutParams layoutParams = iv_cut_ai_pic.getLayoutParams();
layoutParams.width = (int) (Common.getScreenWidth(this) *0.85);
layoutParams.height = (int) (layoutParams.width * cutHeight/cutWidth );
iv_cut_ai_pic.setLayoutParams(layoutParams);
/* BitmapDrawable bdAfter=new BitmapDrawable(bitmapAfter);
bv_before_after.setDrawableRight(bdAfter);*/
iv_cut_ai_pic.setImageBitmap(bitmapAfter);
iv_cut_ai_pic.setVisibility(View.VISIBLE);
bv_before_after.setVisibility(View.GONE);
break;
}
}
}