- 跟去年一样,又是到了年底才开始想起来补上几片记录文。都是很早前完成的功能,现在才想着要发出来记录下,说来也是很惭愧。
- 简述下这部分的功能,去年早些时候做了相册图片选择,但是其实一直不太满意,但是去年的加班又把我整个就堵住没有腾出手来去完善。直到今年年中才做了一些图片的仿微信的压缩优化,相册的大部分改版功能还是我司的安卓大姐大LCC同学完成的,这里偷一点出来。先写相册的部分,有时间的话会把整个图片的选择压缩上传放到gitHub,不过依照我的尿性,可能。。。
(果然不出我所料,前面这段话,和本局,以及最代码的结果出来,间隔了小一个月)
- 虽然代码里面我尽量多写&写清楚我的注释以及我在做相关功能时的思考;
- 但是实际操作中,我在考虑尽我所能来做一些性能优化的时候,实际又引入了几个中间变量进行操作,所以实际上可能理解起来可能还是会带来一些麻烦;
- 当我写完整个性能调整后,发现可能我所做的这些调整更多的是希望能给大家提供一个思路来进行相册部分的数据处理。
- 而现在我采用的方式可能还是有很多的不足&bug,所以也权当抛砖引玉,希望诸位能多多拍砖,给出一个更靠谱&逻辑更清晰明了的方案。
整个过程所涉及到的一些常规操作(具体的使用方式不再赘述)以及抄过的代码有:
- RxJava,此部分主要进行了线程操作,在后续图片压缩的过程中也会有更多功能的使用; Retrofit(后续的网络部分);真的建议没看Rxjava的赶紧看一下,真滴好用
- RxPermissions 权限获取
- 图片操作相关 PhotoView 图片缩放;Glide
- DiffUtil 进行recycleview的局部刷新处理,以节省性能和防止出现刷新过程中的闪烁(比如增加/取消一个图片选中,就不必使用全局刷新,只需刷新相关的几个改变的选中功能数字即可)
- 相机部分抄过好大一段别人的代码,比如在关于在取景框适配的部分,但是忘记原来抄的谁的了,无法实名制感谢了,很抱歉。项目部分的一部分代码由我们的大姐头LCC同学完成,表示感谢;在Diffutils,以及局部刷新相关,参考了参考文章,参考链接;表示感谢
OK,废话说了老多,开始正文:
效果图,原谅我脏乱差的相册
渣图片
1. 先写个相册加载的工具类:
/**
* Created by lcc on 2017/9/26.
*/
public class AlbumHelpler {
private ContentResolver mResolver;
private Context context;
public static String ALBUM_NAME = "最近照片";
public static String VIDEO_NAME = "所有视频";
private long MIN_PIC_SIZE = 1024 * 20;//图片最小尺寸
private long MAX_VIDEO_SIZE = 15 * 1024 * 1024;//视频的最大尺寸
private String MAX_VIDEO_TIME = "10000";//视频最大时长
private List<AlbumModel> albumsList = new ArrayList<>(); //照片的集合
public AlbumHelpler(Context context) {
this.context = context;
mResolver = context.getContentResolver();
}
/**
* 获得所有的照片,以及相册
*
* @return
*/
public List<String> getPics() {
List<String> picList = new ArrayList<>(); //照片的集合
HashMap<String, AlbumModel> temporary = new HashMap<>();
// return albumsList;
//照片的集合 picList
Uri mTable = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
//照片的限制大小
String imgSize = String.valueOf(MIN_PIC_SIZE);
//照片选取的列名 代表所有图片文件的列 地址 尺寸 时间
String[] mColumns = {MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.SIZE,MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME, MediaStore.Images.ImageColumns.DATE_ADDED};
//照片的筛选条件 多用途种类 控制尺寸
String mSelection = "(" + MediaStore.Images.ImageColumns.MIME_TYPE + "=? or " + MediaStore.Images.ImageColumns.MIME_TYPE + "=? or " + MediaStore.Images.ImageColumns.MIME_TYPE + "=? " + ") AND " + MediaStore.Images.ImageColumns.SIZE + ">?";
//筛选条件值,与?相对应 三种图片类型,可以考虑减少
String[] mSelectArgs = {"image/jpeg", "image/png", "image/webp", imgSize};
//根据ContentResolver进行图片的添加
Cursor mCursor = mResolver.query(mTable, mColumns, mSelection, mSelectArgs, MediaStore.Images.ImageColumns.DATE_ADDED + " DESC");
if (null == mCursor || mCursor.getCount() == 0) {
return picList;
}
if (null != mCursor && mCursor.getCount() != 0) {
mCursor.moveToFirst();
// 初始化一个相册的model
AlbumModel album1 = new AlbumModel(ALBUM_NAME, 0, mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)));
albumsList.add(0, album1);
// while (mCursor.moveToNext()) {
for (mCursor.moveToFirst(); !mCursor.isAfterLast(); mCursor.moveToNext()) {
String albumPath = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME));
album1.increaseCount();
if (temporary.containsKey(albumPath)) {
// 所在的相册相同就增加一个
AlbumModel albumBean = temporary.get(albumPath);
albumBean.increaseCount();
} else {
// 否则增加一个相册
AlbumModel albumBean = new AlbumModel(albumPath, 1, mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)));
albumsList.add(albumBean);
temporary.put(albumPath, albumBean);
}
String path = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA));
picList.add(path);
}
}
if (!mCursor.isClosed()) {
mCursor.close();
mCursor = null;
}
return picList;
}
/**
* 获得图片和视频的集合
*
* @return
*/
public List<DisplayInfoBean> getPicVideos() {
//照片的集合
List<DisplayInfoBean> picVideoList = new ArrayList<>();
Uri mTable = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
//照片的限制大小
String imgSize = String.valueOf(MIN_PIC_SIZE);
//照片选取的列名
String[] mColumns = {MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.SIZE, MediaStore.Images.ImageColumns.DATE_ADDED};
//照片的筛选条件
String mSelection = "(" + MediaStore.Images.ImageColumns.MIME_TYPE + "=?or " + MediaStore.Images.ImageColumns.MIME_TYPE + "=?or " + MediaStore.Images.ImageColumns.MIME_TYPE + "=?" + ")AND " + MediaStore.Images.ImageColumns.SIZE + ">?";
//筛选条件值,与?相对应
String[] mSelectArgs = {"image/jpeg", "image/png", "image/webp", imgSize};
//视频的筛选条件
String videoSize = String.valueOf(MAX_VIDEO_SIZE);
String mSelect = MediaStore.Video.Media.MIME_TYPE + "=? AND " + MediaStore.Video.Media.DURATION + "<? AND " + MediaStore.Video.Media.SIZE + "<?";
String[] mSelectVideoArgs = {"video/mp4", MAX_VIDEO_TIME, videoSize};
Cursor mCursor = mResolver.query(mTable, mColumns, mSelection, mSelectArgs, MediaStore.Images.ImageColumns.DATE_ADDED + " DESC");
if (mCursor == null || mCursor.getCount() == 0) {
return picVideoList;
}
if (mCursor != null && mCursor.getCount() != 0) {
while (mCursor.moveToNext()) {
String path = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA));
String addTime = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_ADDED));
picVideoList.add(new DisplayInfoBean(path, addTime));
}
}
Cursor cursor = mResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, null, mSelect, mSelectVideoArgs, MediaStore.Video.Media.DATE_ADDED + " DESC");
if (mCursor == null || mCursor.getCount() == 0) {
return picVideoList;
}
try {
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA)); // 路径
String addTime = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATE_ADDED));
picVideoList.add(new DisplayInfoBean(path, addTime));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
cursor.close();
cursor = null;
}
if (!mCursor.isClosed()) {
mCursor.close();
mCursor = null;
}
return picVideoList;
}
/**
* 获得所有图片的相册
*
* 把所有的注释放开依然可用,
* 现在是把相册以及图片的获取都放到了上面的方法,节省一次循环操作
* @return
*/
public List<AlbumModel> getAlbums() {
//照片的集合 albumsList
// List<AlbumModel> albumsList = new ArrayList<>();
// HashMap<String, AlbumModel> temporary = new HashMap<>();
// //照片大小的最小值
// String imgSize = String.valueOf(MIN_PIC_SIZE);
// Uri mTable = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
// //照片选取的列名
// String[] mColumns = {MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME, MediaStore.Images.ImageColumns.DATE_ADDED};
// String mSelection = "(" + MediaStore.Images.ImageColumns.MIME_TYPE + "=? or " + MediaStore.Images.ImageColumns.MIME_TYPE + "=? or " + MediaStore.Images.ImageColumns.MIME_TYPE + "=? " + ") AND " + MediaStore.Images.ImageColumns.SIZE + ">?";
// String[] mSelectArgs = {"image/jpeg", "image/png", "image/webp", imgSize};
// //照片的相册获得
// Cursor mCursor = mResolver.query(mTable, mColumns, mSelection, mSelectArgs, MediaStore.Images.ImageColumns.DATE_ADDED + " DESC");
// if (null == mCursor || mCursor.getCount() == 0) {
// return albumsList;
// }
// if (mCursor != null && mCursor.getCount() != 0) {
// mCursor.moveToFirst();
初始化一个相册的model
// AlbumModel album1 = new AlbumModel(ALBUM_NAME, 0, mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)));
// albumsList.add(0, album1);
// for (mCursor.moveToFirst(); !mCursor.isAfterLast(); mCursor.moveToNext()) {
while (mCursor.moveToNext()) {
相册的地址
// String albumPath = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME));
// album1.increaseCount();
// if (temporary.containsKey(albumPath)) {
所在的相册相同就增加一个
// AlbumModel albumBean = temporary.get(albumPath);
// albumBean.increaseCount();
// } else {
否则增加一个相册
// AlbumModel albumBean = new AlbumModel(albumPath, 1, mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)));
// albumsList.add(albumBean);
// temporary.put(albumPath, albumBean);
// }
// }
// }
// if (!mCursor.isClosed()) {
// mCursor.close();
// mCursor = null;
// }
return albumsList;
}
/**
* 获得视频相册
*/
public List<AlbumModel> getVideoAlbums() {
//照片的集合
List<AlbumModel> albumsList = new ArrayList<>();
String videoSize = String.valueOf(MAX_VIDEO_SIZE);
String mSelect = MediaStore.Video.Media.MIME_TYPE + "=? AND " + MediaStore.Video.Media.DURATION + "<=? AND " + MediaStore.Video.Media.SIZE + "<=?";
String[] mSelectVideoArgs = {"video/mp4", MAX_VIDEO_TIME, videoSize};
//视频的相册获得
Cursor cursor = mResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, null, mSelect, mSelectVideoArgs, MediaStore.Video.Media.DATE_ADDED + " DESC");
if (null == cursor || cursor.getCount() == 0) {
return albumsList;
}
cursor.moveToFirst();
AlbumModel VideoAlbum = new AlbumModel(VIDEO_NAME, 0, cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA)));
albumsList.add(VideoAlbum);
try {
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
VideoAlbum.increaseCount();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
cursor.close();
cursor = null;
}
return albumsList;
}
/**
* 获得相册中的照片
*
* @param albumName
* @return
*/
public List<String> getAlbumPic(String albumName) {
List<String> pics = new ArrayList<>();
Uri mTable = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
//照片的限制大小
String imgSize = String.valueOf(MIN_PIC_SIZE);
//照片选取的列名
String[] mColumns = {MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.SIZE, MediaStore.Images.ImageColumns.DATE_ADDED};
String mSelection = "(" + MediaStore.Images.ImageColumns.MIME_TYPE + "=? or " + MediaStore.Images.ImageColumns.MIME_TYPE + "=? or " + MediaStore.Images.ImageColumns.MIME_TYPE + "=? " + ")AND " + MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME + "=? AND " + MediaStore.Images.ImageColumns.SIZE + ">?";
String[] selectArgs = {"image/jpeg", "image/png", "image/webp", albumName, imgSize};
Cursor mCursor = mResolver.query(mTable, mColumns, mSelection, selectArgs, MediaStore.Images.ImageColumns.DATE_ADDED + " DESC");
if (mCursor == null || mCursor.getCount() == 0) {
return pics;
}
while (mCursor.moveToNext()) {
pics.add(mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)));
}
if (!mCursor.isClosed()) {
mCursor.close();
mCursor = null;
}
return pics;
}
/**
* 获得所有的视频
*/
public List<DisplayInfoBean> getVideos() {
List<DisplayInfoBean> videoLists = new ArrayList<>();
String videoSize = String.valueOf(MAX_VIDEO_SIZE);
String mSelect = MediaStore.Video.Media.MIME_TYPE + "=? AND " + MediaStore.Video.Media.DURATION + "<=? AND " + MediaStore.Video.Media.SIZE + "<=?";
String[] mSelectArgs = {"video/mp4", MAX_VIDEO_TIME, videoSize};
Cursor cursor = mResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, null, mSelect, mSelectArgs, MediaStore.Video.Media.DATE_ADDED + " DESC");
if (null == cursor || cursor.getCount() == 0) {
return videoLists;
}
try {
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA)); // 路径
String addTime = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION));
videoLists.add(new DisplayInfoBean(path, addTime));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
cursor.close();
cursor = null;
}
return videoLists;
}
}
复制代码
其实也很简单,根据Cursor进行整个手机的图片查找。之前是相册/图片分别获取的数据,现在把所有图片的查找和相册放到了一个Cursor循环里面了,在获取全部图片的时候同时获取了图片的相册,getAlbums这个方法就可以注释掉了,不过代码也还能用。
2. PhotoSelectorHelper图片选择类:
因为上述的图片遍历需要读本地存储,所以就统一用了rxjava进行线程控制。(给不懂rx的解释下,就是create里是工作线程,然后读取数据之后发送到subscribe中,subscribe中是UI线程)当图片遍历结束以后,用接口的方式回调回回去。
public class PhotoSelectorHelper {
private AlbumHelpler albumController;
public PhotoSelectorHelper(Context context) {
albumController = new AlbumHelpler(context);
}
/**
* 获得图片和视频
*
* @param listener
*/
public void getReccentPhotoList(final OnLoadPhotoListener listener) {
Observable.create(new Observable.OnSubscribe<List<String>>() {
@Override
public void call(Subscriber<? super List<String>> subscriber) {
List<String> picVideos = new ArrayList<String>();
List<DisplayInfoBean> photos = albumController.getPicVideos();
Collections.sort(photos, new Comparator<DisplayInfoBean>() {
@Override
public int compare(DisplayInfoBean o1, DisplayInfoBean o2) {
return (int) (Long.parseLong(o2.getAddTime()) - Long.parseLong(o1.getAddTime()));
}
});
for (int i = 0; i < photos.size(); i++) {
picVideos.add(photos.get(i).getPath());
}
subscriber.onNext(picVideos);
subscriber.onCompleted();
}
}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Subscriber<List<String>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
listener.onPhotoLoaded(new ArrayList<String>());
}
@Override
public void onNext(List<String> list) {
listener.onPhotoLoaded(list);
}
});
}
/**
* 获得所有的照片
*/
public void getAllPics(final OnLoadPhotoListener listener) {
Observable.create(new Observable.OnSubscribe<List<String>>() {
@Override
public void call(Subscriber<? super List<String>> subscriber) {
List<String> list = albumController.getPics();
subscriber.onNext(list);
subscriber.onCompleted();
}
}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Subscriber<List<String>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(List<String> list) {
listener.onPhotoLoaded(list);
}
});
}
/**
* 获取视频相册列表
*/
public void getVideoAlbumList(final OnLoadAlbumListener listener) {
Observable.create(new Observable.OnSubscribe<List<AlbumModel>>() {
@Override
public void call(Subscriber<? super List<AlbumModel>> subscriber) {
List<AlbumModel> albums = albumController.getVideoAlbums();
subscriber.onNext(albums);
subscriber.onCompleted();
}
}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Subscriber<List<AlbumModel>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(List<AlbumModel> albumModels) {
listener.onAlbumLoaded(albumModels);
}
});
}
/**
* 获取相册列表
*/
public void getAlbumList(final OnLoadAlbumListener listener) {
Observable.create(new Observable.OnSubscribe<List<AlbumModel>>() {
@Override
public void call(Subscriber<? super List<AlbumModel>> subscriber) {
List<AlbumModel> albums = albumController.getAlbums();
subscriber.onNext(albums);
subscriber.onCompleted();
}
}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Subscriber<List<AlbumModel>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(List<AlbumModel> albumModels) {
listener.onAlbumLoaded(albumModels);
}
}
);
}
/**
* 获取单个相册下的所有视频的信息
*/
public void getAlbumVideoList(final OnLoadPhotoListener listener) {
Observable.create(new Observable.OnSubscribe<List<DisplayInfoBean>>() {
@Override
public void call(Subscriber<? super List<DisplayInfoBean>> subscriber) {
List<DisplayInfoBean> videos = albumController.getVideos();
subscriber.onNext(videos);
subscriber.onCompleted();
}
}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Subscriber<List<DisplayInfoBean>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(List<DisplayInfoBean> displayInfoBeen) {
listener.onVideoLoaded(displayInfoBeen);
}
});
}
/**
* 获取单个相册下的所有照片信息
*/
public void getAlbumPhotoList(final String name, final OnLoadPhotoListener listener) {
Observable.create(new Observable.OnSubscribe<List<String>>() {
@Override
public void call(Subscriber<? super List<String>> subscriber) {
List<String> photos = albumController.getAlbumPic(name);
subscriber.onNext(photos);
subscriber.onCompleted();
}
}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Subscriber<List<String>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.d("======",e.toString());
}
@Override
public void onNext(List<String> albumModels) {
listener.onPhotoLoaded(albumModels);
}
});
}
/**
* 获取本地图库照片回调
*/
public interface OnLoadPhotoListener {
void onPhotoLoaded(List<String> photos);
void onVideoLoaded(List<DisplayInfoBean> videos);
}
/**
* 获取本地相册信息回调
*/
public interface OnLoadAlbumListener {
void onAlbumLoaded(List<AlbumModel> albums);
}
}
复制代码
3. 图片的选择部分
前面相册加载多余的就不再赘述了,上面从使用完相册加载工具后,拿到数据,通知Recycleview(Gridview)刷新展示即可,比较简单就不再过多说了,可能逻辑比较麻烦的地方在图片的选中/取消选中时的一些ui变化(比如当前选中功能 12345,取消中间的3,就变成了 12(3>空)(4>3)(5>4)),这时候刷新,如果直接使用notify(),比较浪费性能,而且会造成屏幕刷新时的白屏闪烁。所以这时候Diffutil算是目前的最优解了。
关于对一些问题的思考以及当前我能力所限所能想到的解决方案都写在了注释里,当然一定会有疏漏以及更好的解决方案,抛砖引玉了。
public class PhotoPickActivity extends AppCompatActivity implements PhotoSelectorHelper.OnLoadAlbumListener,
PhotoSelectorHelper.OnLoadPhotoListener, PopupWindow.OnDismissListener {
private PhotoSelectorHelper mHelper;
private RecyclerView mRecycleView;
private PhotoVideoAdapter photoVideoAdapter;
private TextView mCountText;
public static final String MAX_PICK_COUNT = "max_pick_count";
public static final String IS_SHOW_CAMERA = "isShowCamera";
public static final String SELECT_PHOTO_LIST = "select_photo_list";
private static final int TO_PICK_ALBUM = 1;
private static final int TO_PRIVIEW_PHOTO = 2;
private static final int TO_TAKE_PHOTO = 3;
private boolean isShowCamera;
private int maxPickCount;
private String mLastAlbumName;
//状态栏
private RelativeLayout headview;
private ImageView down;
private TextView album;
private TextView titleText;
//相册选择
private PopupWindow popupWindow;
private AlbumListAdapter albumListAdapter;
private TextView mCountText0;
private ArrayList<String> name;
private int mShowPicCount = 0;
private ArrayList<String> mPicList;
private ArrayList<DisplayInfoBean> videoList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_photo_pick);
mPicList = new ArrayList<>();
videoList = new ArrayList<>();
//图片选择的最大数量,默认是9张
mShowPicCount = getIntent().getIntExtra("picCount", 9);
/**
* 从发布动态进入
* 是否要添加集合的第一项占位(为了视频位置(添加视频或者显示已经添加的视频))
* select 两个值 1 pic 点击照片进入 2 video 点击视频进入
*/
isShowCamera = getIntent().getBooleanExtra("isShowCamera", false);
if (mShowPicCount == 0) {
maxPickCount = 9;
} else {
maxPickCount = mShowPicCount;
}
//相册列表的名字
name = new ArrayList<>();
mLastAlbumName = AlbumHelpler.ALBUM_NAME;
mCountText = (TextView) this.findViewById(R.id.tv_to_confirm);
mCountText0 = (TextView) this.findViewById(R.id.tv_to_confirm0);
mCountText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
selectDone();
}
});
//recycler
mRecycleView = (RecyclerView) this.findViewById(R.id.mp_galley_gridView);
GridLayoutManager mManagerDrawable = new GridLayoutManager(this, 3);
mRecycleView.setLayoutManager(mManagerDrawable);
mRecycleView.setAdapter(photoVideoAdapter =
new PhotoVideoAdapter(this, isShowCamera, maxPickCount));
//对标题栏的初始化
headview = (RelativeLayout) findViewById(R.id.photo_pick);
album = (TextView) findViewById(R.id.pic_head_view_commit);
down = (ImageView) findViewById(R.id.pic_head_view_down);
album.setText(getString(R.string.photo_pick_album));
titleText = (TextView) findViewById(R.id.pic_head_view_title);
titleText.setText(R.string.photo_pick_near);
ImageView backImg = (ImageView) findViewById(R.id.pic_head_view_back);
TextView backText = (TextView) findViewById(R.id.pic_head_view_back_t);
backImg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
setResult(RESULT_OK, intent);
finish();
}
});
backText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
setResult(RESULT_OK, intent);
finish();
}
});
//返回的选中的图片
ArrayList<String> list = getIntent().getStringArrayListExtra(SELECT_PHOTO_LIST);
if (list == null) {
list = new ArrayList<>();
}
if (list != null) {
photoVideoAdapter.setmSelectedImage(list);
}
//视频还是图片的显示逻辑
mHelper = new PhotoSelectorHelper(this);
mHelper.getAllPics(this);
//相册选择
album.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showPop();
}
});
if (maxPickCount >= 1) {
mCountText.setVisibility(View.VISIBLE);
mCountText0.setVisibility(View.VISIBLE);
} else {
mCountText.setVisibility(View.GONE);
mCountText0.setVisibility(View.GONE);
}
photoVideoAdapter.setOnDisplayImageAdapter(new DisplayImageViewAdapter<String>() {
@Override
public void onItemClick(Context context, int index, String path) {
//由地址是否为空来判断,空的话就
if (TextUtils.isEmpty(path)) {
//从发视频进入
//如果为第一个则跳到照相机,反之添加照片
if (Build.BRAND.equals("samsung")) {
//三星手机跳转系统相机
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
MyApp.setPath(Environment.getExternalStorageDirectory() + "/greatchef/"
+ RandomString.randomString(8) + ".jpg");
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(MyApp.getPath())));
startActivityForResult(intent, TO_TAKE_PHOTO);
} else {
Intent in = new Intent(PhotoPickActivity.this, YcCameraActivity.class);
startActivityForResult(in, TO_TAKE_PHOTO);
}
} else {
if (path.endsWith(".mp4")) {
//视频==因为只拿出来了mp4格式的视频
// Intent intent = new Intent(PhotoPickActivity.this, UploadPreVideoActivity.class);
Intent intent = new Intent(PhotoPickActivity.this, YcCameraActivity.class);
intent.putExtra("isShowDelect", false);
intent.putExtra("foodvideo", path);
intent.putExtra("islocal", true);
intent.putExtra("imgpass", "");
startActivity(intent);
} else {
Intent intent = new Intent(PhotoPickActivity.this, PhotoPreviewActivity.class);
Log.d("========>", index + " ");
intent.putExtra(PhotoPreviewActivity.PHOTO_PATH_IN_ALBUM, index);
intent.putExtra("isShowCamera", isShowCamera);
intent.putExtra(MAX_PICK_COUNT, maxPickCount);
intent.putExtra("album_name", mLastAlbumName);
intent.putStringArrayListExtra("select", (ArrayList<String>) photoVideoAdapter.getmSelectedImage());
startActivityForResult(intent, TO_PRIVIEW_PHOTO);
}
}
}
@Override
public void onDisplayImage(Context context, ImageView imageView, String s) {
}
@Override
public void onItemImageClick(Context context, int index, List<String> list) {
}
@Override
public void onImageCheckL(String path, boolean isChecked) {
///需要处理
// photoVideoAdapter.notifyDataSetChanged();
updateCountView();
}
});
updateCountView();
}
/**
* 显示相册列表
*/
private void showPop() {
//视频还是图片的显示逻辑
mHelper.getAlbumList(this);
down.setBackgroundResource(R.mipmap.photo_havepic_down);
ListView mListView;
View view = LayoutInflater.from(this).inflate(R.layout.activity_album_pick, null);
mListView = (ListView) view.findViewById(R.id.lv_show_album);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (popupWindow != null && popupWindow.isShowing()) {
popupWindow.dismiss();
}
}
});
albumListAdapter = new AlbumListAdapter(this, name);
mListView.setAdapter(albumListAdapter);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//相册的名字
mLastAlbumName = albumListAdapter.getItem(position).getName();
if (name.size() > 0) {
name.set(0, mLastAlbumName);
} else {
name.add(mLastAlbumName);
}
titleText.setText(mLastAlbumName);
if (AlbumHelpler.ALBUM_NAME.equals(mLastAlbumName)) {
mHelper.getAllPics(PhotoPickActivity.this);
} else if (AlbumHelpler.VIDEO_NAME.equals(mLastAlbumName)) {
mHelper.getAlbumVideoList((PhotoSelectorHelper.OnLoadPhotoListener) PhotoPickActivity.this);
} else {
mHelper.getAlbumPhotoList(mLastAlbumName, (PhotoSelectorHelper.OnLoadPhotoListener) PhotoPickActivity.this);
}
if (popupWindow != null) {
popupWindow.dismiss();
}
}
});
if (popupWindow == null) {
popupWindow = new PopupWindow(view, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT, true);
popupWindow.setBackgroundDrawable(new ColorDrawable(0xb0000000));
popupWindow.setOutsideTouchable(true);
popupWindow.setOnDismissListener(this);
}
if (!popupWindow.isShowing()) {
popupWindow.showAsDropDown(headview);
} else {
popupWindow.dismiss();
}
}
/**
* 图片加载完成
*
* @param photos
*/
@Override
public void onPhotoLoaded(List<String> photos) {
//顺序不能错,否则在photoVideoAdapter.notifyDataSetChanged(photos); 会对photos进行操作,假如一个站位放置拍摄
mPicList.clear();
mPicList.addAll(photos);
photoVideoAdapter.notifyDataSetChanged(photos);
}
@Override
public void onVideoLoaded(List<DisplayInfoBean> videos) {
photoVideoAdapter.notifyVideoDtChanged(videos);
}
/**
* 相册加载完成
*
* @param albums
*/
@Override
public void onAlbumLoaded(List<AlbumModel> albums) {
if (null != albums && albums.size() != 0) {
albumListAdapter.notifyDataSetChanged(albums);
} else {
albumListAdapter.notifyDataSetChanged(new ArrayList<AlbumModel>());
Toast.makeText(this, getString(R.string.photo_empty), Toast.LENGTH_SHORT).show();
popupWindow.dismiss();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != RESULT_OK) {
return;
}
switch (requestCode) {
case TO_PRIVIEW_PHOTO:
updateCountView();
///因为当前是通过跳转到下一个PhotoPreViewActivity来查看大图的,在这个页面会重新 mHelper.getAllPics,因此刷新adaoter的时候想到一种情况,有种比较特殊的情况,在相册页面,按home回桌面,自己拍摄照片一张,再进入预览
//就导致预览页面数据比较新,照片多了一张。这时候两种解决方式
// 1. 预览页面使用当前页面的数据,由于数据量可能很大,并不能用intent来传递,所以可以存放数据到第三方,比如放个sp啥的
// 2. 预览界面数量不同的时候,在此处返回数据总量,当数两个页面据总量不同时,更新当前页面数据。不过,依然存在的问题1,无法保证按下home键盘拍几张再删除同样的张数导致的问题。但是实在是懒得写了。
// 解决办法:在onRsume时候,更新相册数据。坏处是,照片数据量巨大的时候可能比较慢
// 依然存在的问题2,上述情况下删除了选中的照片,没有在更新中删除选中列表,暂时因为赶进度,会在下一版本进行优化
// (思路 onresume时,或者此时(就是刷新数据的时候),遍历当前选中的列表,查询是否存在于相册列表中)
// 3. 采用popwindow,在当前页面进行数据操作,优势明显,少一个页面,少一次查询,但是问题点在于会有比较多的数据问题,比如在预览的时候进行选中操作数据刷新的问题,
// 当前采用方法2,所以这段注释也写在了这个onActivityResult。。。
photoVideoAdapter.setmSelectedImage(data.getStringArrayListExtra("select"));
//这里需要把已选中的数据回传,然后set到Adapter中,以便Adapter对已选中的数据进行操作。
if (isShowCamera) {
if ((mPicList.size() - 1) != data.getIntExtra("count", 0)) {
mHelper.getAllPics(this);
} else {
photoVideoAdapter.notifyDataSetChanged(mPicList);
// photoVideoAdapter.notifyDataSetChanged();
}
} else {
if (mPicList.size() != data.getIntExtra("count", 0)) {
mHelper.getAllPics(this);
} else {
photoVideoAdapter.notifyDataSetChanged(mPicList);
// photoVideoAdapter.notifyDataSetChanged();
}
}
break;
case TO_TAKE_PHOTO:
// 三星手机去使用系统相机拍摄,因为角度当时没控制住,是偏转90度的,后来太懒了也就没改。。。。
if (Build.BRAND.equals("samsung")) {
File file = new File(MyApp.getPath());
PictureUtil.notifyGallery(this, MyApp.getPath());
photoVideoAdapter.getmSelectedImage().add(MyApp.getPath());
selectDone();
} else {
String url = data.getStringExtra("imgp");
PictureUtil.notifyGallery(this, url);
photoVideoAdapter.getmSelectedImage().add(url);
selectDone();
}
break;
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* 处理逻辑为压缩了的不在压缩,没压缩的压缩完替换之前存在集合中的数据
*/
//图库选择中选择的一些图片
ArrayList<String> list = new ArrayList<>();
//MyApp中保存的,之前最后一次选中后的数据
private void selectDone() {
PicCompress video;
if (MyApp.getApp().getPiclist() == null || MyApp.getApp().getPiclist().size() == 0) {
video = new PicCompress();
} else {
video = MyApp.getApp().getPiclist().get(0);
}
MyApp.getApp().getPiclist().clear();
if (photoVideoAdapter.getmSelectedImage() != null) {
for (int i = 0; i < photoVideoAdapter.getmSelectedImage().size(); i++) {
MyApp.getApp().getPiclist().add(
new PicCompress(photoVideoAdapter.getmSelectedImage().get(i), "", false));
list.add(photoVideoAdapter.getmSelectedImage().get(i));
}
// if (isShowCamera) {
// MyApp.getApp().getPiclist().add(0, video);
// }
}
Intent intent = new Intent();
intent.putStringArrayListExtra(SELECT_PHOTO_LIST, list);
setResult(RESULT_OK, intent);
finish();
}
public void selectVideo(String path) {
Intent intent = new Intent();
PicCompress compress = new PicCompress();
compress.setPicadress(path);
if (MyApp.getApp().getPiclist() != null && MyApp.getApp().getPiclist().size() != 0) {
MyApp.getApp().getPiclist().remove(0);
}
MyApp.getApp().getPiclist().add(0, compress);
intent.putExtra("videoPath", path);
setResult(RESULT_OK, intent);
finish();
}
private void updateCountView() {
if (photoVideoAdapter.getmSelectedImage().size() == 0) {
mCountText.setEnabled(false);
mCountText0.setEnabled(false);
} else {
mCountText.setEnabled(true);
mCountText0.setEnabled(true);
}
mCountText.setText(getString(R.string.head_welldone) + "(" + photoVideoAdapter.getmSelectedImage().size() + "/" + maxPickCount + ")");
}
@Override
public void onDismiss() {
down.setBackgroundResource(R.mipmap.photo_havepic_up);
}
}
复制代码
4. 相册的Adapter
这部分牵扯的逻辑较多,索然我尽力的尝试写清楚我的逻辑,但是好像还是看起来比较乱 主要处理选中的逻辑在holder.selectFlag.setOnClickListener中,代码注释中我写了关于这部分的一些思考。现有结果是,增加取消选中,只修改需要修改的item,比如增加取消蒙层,改变选中数字。主要的逻辑为:
- 增加选中图片,则只需要覆盖该图片灰色蒙层,选中数字为已经选中数量。
- 取消选中:
- 取消最后一位选中,只需要取消该图片灰色蒙层,选中数字隐藏。
- 非最后一位,取消当前位置的蒙层+数字,从该取消位置往后(比如取消第3张,那么1/2两张可以不变,只改变3后面的图),蒙层保持,数字减少1。
- 更改当前点击的图片的操作,可以在点击事件里,直接改变数字+蒙层等状态。其他需要调整的,则需要adapter.notify了,但是前面说了不希望notify(),以防止出现闪屏,所以采用的方式是,直接调用Recycleview的 notifyItemChanged(position, payload)/notifyItemRangeChanged(position, 1, payload) playload不为空的时候,就会掉用onBindViewHolder()三参的方法 参考链接
感兴趣的可以去先了解下Dffutils的工作原理,其实也是利用了这个三个参数的bindviewholder方法,可以重写下三个参数的方法,在payloads中携带一些信息,来进行操作。本文也使用了Diffutils的此部分功能进行局部刷新。
所以只需要在改动处,增加payload参数,进行三参方法改动即可。具体可以参考代码
/**
* 相册界面的adapter
* Created by cuiliubi
*/
public class PhotoVideoAdapter extends RecyclerView.Adapter<PhotoVideoAdapter.MyViewHolder> {
//用户选择的图片,存储为图片的完整路径
public ArrayList<String> mSelectedImage;//选中图片的list
private ArrayList<Integer> mSelectedImagePosition;//记录选中图片在整个mPiclist(当前相册的全部图片)中的位置
protected Context mContext;
protected ArrayList<Photobean> mPicList;//(当前相册的)全部图片的list
protected ArrayList<DisplayInfoBean> mVideoList;
private int maxCount;
private LayoutInflater mInflater;
private DisplayImageViewAdapter<String> mDisplayAdapter;
private boolean isShowCamera;
private PhotoVideoAdapter adapter;
public List<String> getmSelectedImage() {
return mSelectedImage;
}
/**
* 设置选中list,
* 设置mSelectedImagePosition 中选中的位置
*
* @param mSelectedImage
*/
public void setmSelectedImage(ArrayList<String> select) {
mSelectedImage.clear();
mSelectedImage.addAll(select);
if (mPicList != null && mPicList.size() > 0) {
mSelectedImagePosition.clear();
for (int i = 0; i < mPicList.size(); i++) {
if (select.contains(mPicList.get(i).getPath())) {
mPicList.get(i).setSelect(select.indexOf(mPicList.get(i).getPath()));
mSelectedImagePosition.add(i);
}
if (select.size() == mSelectedImagePosition.size()) {
// 当已经找到全部选中图片的位置就可以跳出循环了(此方法)
return;
}
}
}
}
public PhotoVideoAdapter(Context mContext, boolean isShowCamera, int maxCount) {
this.mContext = mContext;
this.maxCount = maxCount;
this.isShowCamera = isShowCamera;
mInflater = LayoutInflater.from(mContext);
mSelectedImage = new ArrayList<>();
mSelectedImagePosition = new ArrayList<>();
mPicList = new ArrayList<>();
if (isShowCamera) {
Photobean bean = new Photobean(0, "");
mPicList.add(0, bean);
}
adapter = this;
}
public PhotoVideoAdapter(Context mContext, boolean isShowCamera, int maxCount, ArrayList<String> mSelectedImage, ArrayList<Integer> mSelectedImagePosition,ArrayList<Photobean> mPicList) {
this.mContext = mContext;
this.maxCount = maxCount;
this.isShowCamera = isShowCamera;
mInflater = LayoutInflater.from(mContext);
this.mSelectedImage =mSelectedImage;
this.mSelectedImagePosition = mSelectedImagePosition;
this.mPicList=mPicList;
mPicList = new ArrayList<>();
if (isShowCamera) {
Photobean bean = new Photobean(0, "");
mPicList.add(0, bean);
}
setmSelectedImage(mSelectedImage);
adapter = this;
}
public void setOnDisplayImageAdapter(DisplayImageViewAdapter<String> adapter) {
this.mDisplayAdapter = adapter;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
PhotoVideoAdapter.MyViewHolder holder;
if (viewType == 1) {
holder = new PhotoVideoAdapter.MyViewHolder(mInflater.inflate(R.layout.grid_photo_item, parent, false));
} else {
holder = new PhotoVideoAdapter.MyViewHolder(mInflater.inflate(R.layout.grid_camera_item, parent, false));
}
return holder;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
if (mDisplayAdapter == null) {
return;
}
if (isShowCamera == true) {
if (position == 0) {
holder.textFirst.setText(mContext.getString(R.string.photo_take));
holder.imgFirst.setImageResource(R.mipmap.photo_icon_takephoto);
holder.camera.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mSelectedImage.size() < maxCount) {
mDisplayAdapter.onItemClick(mContext, 0, "");
} else {
Toast.makeText(mContext.getApplicationContext(),
"不能超过" + maxCount + "张图片", Toast.LENGTH_SHORT).show();
}
}
});
} else {
bindItemView(holder, position);
}
} else {
bindItemView(holder, position);
}
}
@Override
public void onBindViewHolder(final MyViewHolder holder, final int position, List<Object> payloads) {
//实际这里可以为了节省性能写的更简单一点
//整体的数据增删可以走diffutil来做,否则的话,比如只是选中状态,可以不用diffutil来比较数据,而是选择直接传递非空payloads进行notify处理
//不过带来的问题就是需要手动比较数据,每当取消选中的时候,最多需要调整9个(maxCount)个数据;
if (payloads.isEmpty()) {
onBindViewHolder(holder, position);
} else {
Bundle payload = (Bundle) payloads.get(0);
Photobean bean = mPicList.get(position);
for (String key : payload.keySet()) {
switch (key) {
case "SELECT":
//这里可以用payload里的数据,不过data也是新的 也可以用
if (bean.getSelect() == 0) {
// 未曾选中
holder.selectFlag.setSelected(false);
holder.img.setColorFilter(null);
holder.text.setVisibility(View.INVISIBLE);
} else {
// 选中
//已经被选中的照片的显示逻辑
holder.selectFlag.setSelected(true);
holder.img.setColorFilter(0x80000000);
holder.text.setVisibility(View.VISIBLE);
holder.text.setText(mPicList.get(position).getSelect() + "");
}
Glide.with(mContext).load(mPicList.get(position).getPath()).into(holder.img);
break;
case "PATH":
//小圆点的点击逻辑
break;
// case "CANCLE":
// 被取消选中
// break;
case "CHANGE":
// 改变数据
holder.selectFlag.setSelected(true);
holder.img.setColorFilter(0x80000000);
holder.text.setVisibility(View.VISIBLE);
holder.text.setText(mPicList.get(position).getSelect() + "");
break;
default:
break;
}
}
}
}
public void bindItemView(final MyViewHolder holder, final int position) {
//照片
holder.video_time.setVisibility(View.GONE);
holder.shader_text.setVisibility(View.GONE);
//图片上的小圆点的逻辑
if (maxCount >= 1) {
holder.selectFlag.setVisibility(View.VISIBLE);
} else {
holder.selectFlag.setVisibility(View.GONE);
}
//图片赋值
Glide.with(mContext).load(mPicList.get(position).getPath()).into(holder.img);
//已经被选中的照片的显示逻辑
if (mPicList.get(position).getSelect() > 0) {
holder.selectFlag.setSelected(true);
holder.img.setColorFilter(0x80000000);
holder.text.setVisibility(View.VISIBLE);
holder.text.setText(mPicList.get(position).getSelect() + "");
} else {
holder.selectFlag.setSelected(false);
holder.img.setColorFilter(null);
holder.text.setVisibility(View.INVISIBLE);
}
//小圆点的点击逻辑
holder.selectFlag.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean isChecked = mPicList.get(position).getSelect() > 0;
if (isChecked) {
holder.selectFlag.setSelected(false);
holder.img.setColorFilter(null);
holder.text.setVisibility(View.INVISIBLE);
/**
* 这三行// 中间的部分可以完全不用做操作,直接在mDisplayAdapter.onImageCheckL( 的回调进行 adapter.notify 就可以实现操作
*
*
* 但是为了避免增加/取消选中会带来的recycleview刷新全部数据导致的闪动,采用局部刷新。暂时想到两种方式。目前还没考虑好哪一种方式更节省性能
* 当然从逻辑理解上来说,毫无疑问方式1更简单方便,不用进行onBindViewHolder的操作。但是直觉上方式2能节省一丢丢性能,所以拿出来试一试,
* 有两种方式,1,遍历整个mPiclist,新建一个newList,然后把更新的数据给到新的 newList,然后使用下方的 diff方法进行比较
* 2,手动比较,单条刷新,使用notifyItemRangeChanged;此方式需要onBindViewHolder关注下操作。下述方式:
* 2.1,改进方式2,依然手动刷新,在setmSelectImg时,取得这几个选中在整体相册列表中的位置,进行调整,同样采用
*
*
*
* 现在采用方式2.1,虽然并不知道能节省多少性能。。。
* 总体思路已经在下面都加入了注释,总体按照如下思路进行操作
* 1 如果当前是最后一位选中取消,直接取消不用做任何操作
* 2 如果不是最后一位选中取消,那么取消选中的前几位不用操作,只需要操作取消位置取消选中状态,然后改变后续位置改变选中数字,刷新需要改动的数据部分即可
*
*/
int index = mSelectedImage.indexOf(mPicList.get(position).getPath());//变动位置
mPicList.get(position).setSelect(0);
// 在此处,所有的位置都需要进行调整,比如取消了第一个,那么后续所有的已选中都需要进行重新进行设置
// 进行判断,如果是最后一位,就不用判断,直接取消选中就好了
// 其他位置的话需要进行位置变更
if (index == mSelectedImage.size() - 1) {
// last position
// 最后一位,只需要刷新当前itemview即可
mPicList.get(mSelectedImagePosition.get(index)).setSelect(0);
mSelectedImage.remove(mPicList.get(position).getPath());
mSelectedImagePosition.remove(new Integer(position));
} else {
// 取消非末位的选中,考虑,不改变index之前的数据展示,只改变index之后,所以从index+1开始循环这个选中的mSelectImage
for (int i = index + 1; i < mSelectedImage.size(); i++) {
//取消选中之后的图片,需要调整其数字
//index位置是取消选中的位置,所以从下一位开始变动选中的数据,index往后的select数值减少1
mPicList.get(mSelectedImagePosition.get(i)).setSelect(i);//把ho
Bundle payload = new Bundle();
payload.putInt("CHANGE", mSelectedImagePosition.get(i));
//通知下一位置变化数据
adapter.notifyItemChanged(mSelectedImagePosition.get(i), payload);
}
// 取消的选中的图片位置
//在mPicList中,删除点数据
if (index >= 0) {
mPicList.get(mSelectedImagePosition.get(index)).setSelect(0);
}
mSelectedImage.remove(mPicList.get(position).getPath());
mSelectedImagePosition.remove(new Integer(position));
}
} else {
if (mSelectedImage.size() >= maxCount) {
Toast.makeText(mContext.getApplicationContext(),
"不能超过" + maxCount + "张图片", Toast.LENGTH_SHORT).show();
return;
}
mSelectedImage.add(mPicList.get(position).getPath());
mSelectedImagePosition.add(position);
mPicList.get(position).setSelect(mSelectedImage.size());
holder.text.setVisibility(View.VISIBLE);
holder.text.setText(mPicList.get(position).getSelect() + "");
holder.selectFlag.setSelected(true);
holder.img.setColorFilter(0x80000000);
}
mDisplayAdapter.onImageCheckL(mPicList.get(position).getPath(), !isChecked);
}
});
//添加点击事件
holder.img.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mDisplayAdapter.onItemClick(mContext,
mPicList.indexOf(mPicList.get(position)),
mPicList.get(position).getPath());
}
});
}
@Override
public int getItemViewType(int position) {
if (isShowCamera && position == 0) {
return 0;
} else {
return 1;
}
}
@Override
public int getItemCount() {
return mPicList == null ? 0 : mPicList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
TextView text;
TextView textFirst;
TextView video_time;
ImageView img;
ImageView shader_text;
ImageView imgFirst;
ImageView selectFlag;
SquareRelativeLayout camera;
//实际上是,两种布局,为了懒省劲儿共用了一个holder。。。不过现在选择视频部分被先去掉了,所以看起来冗余了一部分
public MyViewHolder(View itemView) {
super(itemView);
text = (TextView) itemView.findViewById(R.id.text);
video_time = (TextView) itemView.findViewById(R.id.video_time);
img = (ImageView) itemView.findViewById(R.id.ic_image_pick);
shader_text = (ImageView) itemView.findViewById(R.id.shader_text);
selectFlag = (ImageView) itemView.findViewById(R.id.ic_check_box);
//拍照/录像的view所包含的
imgFirst = (ImageView) itemView.findViewById(R.id.camera_pick);
camera = (SquareRelativeLayout) itemView.findViewById(R.id.camera_id);
textFirst = (TextView) itemView.findViewById(R.id.camera_text);
}
}
/**
* 更新数据
*/
public void notifyDataSetChanged(List<String> mList) {
ArrayList<Photobean> list = new ArrayList();
if (mList == null) {
mList = new ArrayList<>();
}
if (isShowCamera) {
if (mList.size()>0&&!TextUtils.isEmpty(mList.get(0))){
mList.add(0, "");
}
}
for (int i = 0; i < mList.size(); i++) {
//当相册发生变动的时候,需要更新 mSelectedImagePosition 数据
Photobean bean = new Photobean(0, "");
if (mSelectedImage.contains(mList.get(i))) {
bean.setSelect(mSelectedImage.indexOf(mList.get(i)) + 1);
mSelectedImagePosition.add(i);
bean.setPath(mList.get(i));
} else {
bean.setSelect(0);
bean.setPath(mList.get(i));
}
list.add(bean);
}
diff(list, mPicList, this);//可以用这个,进行局部刷新,也可以用下头三行,全局刷新
// mPicList.clear();
// mPicList.addAll(list);
// adapter.notifyDataSetChanged();
}
/**
* 更新视频数据
*/
public void notifyVideoDtChanged(List<DisplayInfoBean> mList) {
this.mVideoList.clear();
if (mList == null) {
mList = new ArrayList<>();
}
if (isShowCamera) {
mList.add(0, new DisplayInfoBean("", ""));
}
this.mVideoList.addAll(mList);
this.notifyDataSetChanged();
}
/**
* 比较数据进行局部刷新用,暂时先停掉了,效果不太理想
* @param newList
* @param oldList
* @param photoVideoAdapter
*/
private void diff(final ArrayList<Photobean> newList,
final ArrayList<Photobean> oldList, final PhotoVideoAdapter photoVideoAdapter) {
// 放到了工作线程做操作
rx.Observable.create(new rx.Observable.OnSubscribe<DiffUtil.DiffResult>() {
@Override
public void call(Subscriber<? super DiffUtil.DiffResult> subscriber) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffPhotoPickCallback(newList, oldList, 0));
subscriber.onNext(diffResult);
subscriber.onCompleted();
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<DiffUtil.DiffResult>() {
@Override
public void call(DiffUtil.DiffResult diffResult) {
diffResult.dispatchUpdatesTo(photoVideoAdapter);
mPicList.clear();
mPicList.addAll(newList);
}
});
}
}
复制代码
5. 关于Recycleview的局部刷新
其实这部分也出来挺久了,虽然早有二文,但是一直也没有非常认真的研究过代码,趁着这次总结这部分内容也算是给自己一个交代。。。这部分在别人的文章里面都有很详细的参考,我就不再赘述了
/**
* Created by cuiliubi on 18/1/12 星期四.
* 解决点击选中,需要刷新数据时候的全局刷新导致的recycleview闪
*/
public class DiffPhotoPickCallback extends DiffUtil.Callback {
protected ArrayList<Photobean> newPic, oldPic;
protected List<DisplayInfoBean> newVideo, oldVideo;
private int isType = 0; // 0图片,1 视频,暂时没有加入视频,貌似视频也不需要,因为点击以后就回传了,先放着吧暂时
@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
// 棒极了的操作,自己也在边学边做,
Photobean oldBean = oldPic.get(oldItemPosition);
Photobean newBean = newPic.get(newItemPosition);
//这里就不用比较核心字段了,一定相等
Bundle payload = new Bundle();
if (oldBean.getSelect() != newBean.getSelect()) {
payload.putInt("SELECT", newBean.getSelect());
}
if (!oldBean.getPath().equals(newBean.getPath())) {
payload.putString("PATH", newBean.getPath());
}
if (payload.size() == 0)//如果没有变化 就传空
return null;
return payload;//
}
public DiffPhotoPickCallback(ArrayList<Photobean> newPic, ArrayList<Photobean> oldPic, int isType) {
this.newPic = newPic;
this.oldPic = oldPic;
this.isType = isType;
}
public DiffPhotoPickCallback(List<DisplayInfoBean> newVideo, List<DisplayInfoBean> oldVideo, int isType) {
this.newVideo = newVideo;
this.oldVideo = oldVideo;
this.isType = isType;
}
@Override
public int getOldListSize() {
if (isType == 0) return oldPic != null ? oldPic.size() : 0;
else if (isType == 1) return oldVideo != null ? oldVideo.size() : 0;
return 0;
}
@Override
public int getNewListSize() {
if (isType == 0) return newPic != null ? newPic.size() : 0;
else if (isType == 1) return newVideo != null ? newVideo.size() : 0;
return 0;
}
//用来判断两个对象是否同一个item
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
if (isType == 0)//地址相同就可以认为是同一个item了
return newPic.get(newItemPosition).getPath().equals(oldPic.get(oldItemPosition).getPath());
else return newVideo.get(newItemPosition) == newVideo.get(oldItemPosition);
}
//用来判断两个item是否包含相同的数据
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
if (isType == 0)
return newPic.get(newItemPosition).getSelect() == oldPic.get(oldItemPosition).getSelect() && newPic.get(newItemPosition).getPath().equals(oldPic.get(oldItemPosition).getPath());
else
return newVideo.get(newItemPosition).getAddTime().equals(oldVideo.get(oldItemPosition).getAddTime()) && oldVideo.get(newItemPosition).getPath().equals(newVideo.get(oldItemPosition).getPath());
}
}
复制代码