android 相册选择库,ByPhoto-秒开的安卓图片选择库

一、背景

ByPhoto是个安卓图片选择库, 在启动渲染速度上做了很多优化; 荣耀8真机测试,图库里有3000多张图片。 冷启动图片选择页渲染完成需800ms左右, 热启动(即第二次打开Activity)渲染需要300ms。 真正实现了秒开的用户体验。

36383c1818bc

设计背景.png

36383c1818bc

结果页.png

36383c1818bc

选中页.png

二、需求

1、支持图片预加载, 即将图库的前几张图片加载到内存中; 使用了Glide的preload;

2、数据库分段回调, 即图片有几千张图片时, 每查询一定数量时(例如10条)就通知UI补充数据; RecyclerView不会刷新屏幕外的图片,只是缓存了文件路径; PS:这里还可以再优化一下,例如列表向下滑动时预加载后半段数据; 但考虑到字符串占用内存不大,几兆的样子,暂未实现;

3、数据结构, 使用适当的数据结构Map、Set降低读写时间复杂度;

4、支持手指在屏幕滑动时自动勾选经过的图片;

5、勾选图片时只刷新选中图标, 不刷新item图片;避免纵向滑动时刷新闪烁的问题;

6、支持设置单行图片数量和最多选中数量;

36383c1818bc

改造点.png

三、核心代码

36383c1818bc

类图.png

在子线程查询数据库,并分批通知UI数据变化;

@Override protected List doInBackground(String... strings) {

...

while (cursor.moveToNext()) {

...

//每隔10个图片报一次, 即分段通知UI数据变化

if (i % Constants.PHOTO_COUNT_PER_TIME == 0 && i > 0) {

ImageItem[] segData = new ImageItem[Constants.PHOTO_COUNT_PER_TIME];

for (int k = 0; k < Constants.PHOTO_COUNT_PER_TIME; k++) {

segData[k] = itemList.get(i - Constants.PHOTO_COUNT_PER_TIME + k);

}

publishProgress(segData);

}

return null;

}

预加载前几张图片到Glide缓存中, 默认加载前15张;

public static void preloadData(final Context ctx) {

...

while (cursor.moveToNext() && i < Constants.MAX_PRELOAD_PHOTO_NUMS) {

final String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));

if (path != null && new File(path).exists()) {

Log.d("brycegao", "文件已存在:" + path);

}

sHandler.post(new Runnable() {

@Override public void run() {

Glide.with(ctx)

.load(new File(path))

.addListener(new RequestListener() {

@Override

public boolean onLoadFailed(@Nullable GlideException e, Object model,

Target target, boolean isFirstResource) {

return false;

}

@Override public boolean onResourceReady(Drawable resource, Object model,

Target target, DataSource dataSource, boolean isFirstResource) {

return false;

}

})

.preload(Constants.getScreenWidth(context), Constants.getScreenWidth(context));

}

});

i++;

}

...

}

在Activity的onCreate最开始启动线程加载数据, 注意:这里不会出现并发问题, 原因是子线程通过Handler执行UI线程的函数。 onCreate必须执行完成才可能响应子线程触发的回调;

protected void onCreate(Bundle savedInstanceState) {

//在子线程加载数据

initData();

super.onCreate(savedInstanceState);

...

}

重写RecyclerView并处理滑动事件, 逻辑是判断横向滑动时勾选经过的item, 纵向滑动时不做处理; 通过坐标(x,y)找到匹配的RecyclerView条目;

private boolean processTouchEvent(MotionEvent event) {

int x = (int) event.getX();

int y = (int) event.getY();

//记录点击屏幕时的初始坐标

if (event.getAction() == MotionEvent.ACTION_DOWN) {

mDownX = x;

mDownY = y;

}

//抬起手指时重置

if (event.getAction() == MotionEvent.ACTION_UP

|| event.getAction() == MotionEvent.ACTION_CANCEL) {

mDownY = 0;

mDownX = 0;

mLastX = 0;

mLastY = 0;

}

if (event.getAction() == MotionEvent.ACTION_MOVE) {

double distance = Math.sqrt(Math.abs(x - mLastX) * Math.abs(x - mLastX)

+ Math.abs(y - mLastY) * Math.abs(y - mLastY));

//如果是横向滑动且滑动距离超过阈值,则判断经过的item并勾选

if (distance > MIN_DISTANCE && Math.abs(x - mDownX) > Math.abs(y - mDownY)) {

mLastY = y;

mLastX = x;

doCheckSingleFinger(x, y);

}

}

return false;

}

控件:设置控件固定宽高, 减少测量时间;

四、总结

todo:手指滑动时取消勾选状态; 手指滑动时RecyclerView不动;

通过各种技术措施, 尽可能减少渲染时间; 目前最耗时的部分是Glide加载文件并绘制到控件;

监听DrawListener回调, 显示第一个图片(不是背景图,是目标图片)需要800ms左右(冷启动),UI体验看上去就是RecyclerView控件白了一下;

大概写了2天, 有一些收获。

欢迎技术交流~~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Android设备上打相册选择多张图片,可以使用以下步骤: 1. 创建一个活动(Activity)或片段(Fragment),用于显示相册选择图片的界面。 2. 在活动或片段的布局文件中添加一个按钮或图片视图,用于触发打相册的操作。 3. 在活动或片段的Java代码中,使用Intent对象启动系统相册应用。可以使用以下代码段: ```java Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); startActivityForResult(intent, PICK_IMAGE_REQUEST_CODE); ``` 其中,PICK_IMAGE_REQUEST_CODE是一个用户自定义的整数,用于在后续的代码中识别返回的结果。 4. 在活动或片段的Java代码中,覆盖 onActivityResult() 方法,并根据 PICK_IMAGE_REQUEST_CODE 处理相册选择的结果: ```java @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == PICK_IMAGE_REQUEST_CODE && resultCode == Activity.RESULT_OK) { if (data.getClipData() != null) { // 多张图片选择的情况 int count = data.getClipData().getItemCount(); for (int i = 0; i < count; i++) { Uri imageUri = data.getClipData().getItemAt(i).getUri(); // 处理选择图片 // ... } } else if (data.getData() != null) { // 单张图片选择的情况 Uri imageUri = data.getData(); // 处理选择图片 // ... } } } ``` 在上述代码中,我们通过检查 data.getClipData() 是否为空来确定用户是选择了多张图片还是单张图片。多张图片时,可以使用 data.getClipData().getItemCount() 获取选择图片数量,并逐个处理每张图片。对于单张图片,直接使用 data.getData() 获取图片的 Uri。 以上就是使用 Android相册选择多张图片的基本步骤。根据具体需求,还可以加入图片预览、剪裁等功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值