最近需要做一个深度定制的图库的功能,于是,如何获取相册列表,以及如何获取相册里面的图片信息成为了重中之重,本文只是封装了一些代码。其实获取图片以及视频信息只要你知道其数据库如何设计,那么一切都会变得非常简单。好了,我们开始吧。
首先,我们需要获取的信息分为:图片相册、图片相册里面有那些图片,视频相册,视频相册里有那些视频。于是,封装了两个类:
1、BucketModel.kt
class BucketModel { var displayName: String? = null var bucketId: String? = null }
2、MediaModel.kt
class MediaModel { var imageUri: String? = null var imageId: Int? = null }
最后,我们封装一个工具类,用于获取相册以及相册里有那些媒体。
3、MediaUtils.kt
import android.net.Uri import android.provider.MediaStore import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async import top.kpromise.ibase.IApplication import top.kpromise.igallery.model.BucketModel import top.kpromise.igallery.model.MediaModel import java.lang.StringBuilder object MediaUtils { private const val bucketId = "bucket_id" private const val id = "_id" private const val data = "_data" private const val dateTime = "datetaken" private val imageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI private val videoUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI fun listImageBucketAsync(): Deferred<ArrayList<BucketModel>> { return GlobalScope.async(Dispatchers.IO) { listMediaBucket(imageUri) } } fun listVideoBucketAsync(): Deferred<ArrayList<BucketModel>> { return GlobalScope.async(Dispatchers.IO) { listMediaBucket(videoUri) } } private fun listMediaBucket(uri: Uri): ArrayList<BucketModel> { val displayName = "bucket_display_name" val projection = arrayOf(displayName, bucketId, id, "count ($bucketId) as mediaCount") val cur = IApplication.app?.contentResolver?.query(uri, projection, "0=0) group by ($bucketId", null, "$dateTime desc") val result = ArrayList<BucketModel>() cur ?: return result while (cur.moveToNext()) { val bucketModel = BucketModel() bucketModel.displayName = cur.getString(cur.getColumnIndex(displayName)) bucketModel.bucketId = cur.getString(cur.getColumnIndex(bucketId)) bucketModel.coverMediaId = cur.getInt(cur.getColumnIndex(id)) bucketModel.mediaCount = cur.getInt(cur.getColumnIndex("mediaCount")) result.add(bucketModel) } cur.close() return result } private fun listMediaIdInBucket(bucketId: String?, uri: Uri, from: Int, size: Int): ArrayList<Int> { val projection = arrayOf(id) val selection = if (bucketId == null) null else "${this@MediaUtils.bucketId} = ?" val selectionArgs = if (selection == null) null else arrayOf(bucketId) val sortOrder = "$id desc" val pageUri = uri.buildUpon().encodedQuery("limit=$from,$size").build() val cur = IApplication.app?.contentResolver?.query(pageUri, projection, selection, selectionArgs, sortOrder) val result = ArrayList<Int>() cur ?: return result while (cur.moveToNext()) { val imageId = cur.getInt(cur.getColumnIndex(id)) result.add(imageId) } cur.close() return result } fun listImageInBucketAsync(bucketId: String?, from: Int, size: Int): Deferred<ArrayList<MediaModel>> { return GlobalScope.async(Dispatchers.IO) { listImageInBucket(bucketId, imageUri, from, size) } } fun listVideoInBucketAsync(bucketId: String?, from: Int, size: Int): Deferred<ArrayList<MediaModel>> { return GlobalScope.async(Dispatchers.IO) { listImageInBucket(bucketId, videoUri, from, size) } } fun listImageByIdsAsync(ids: ArrayList<Int>): Deferred<ArrayList<MediaModel>> { return GlobalScope.async(Dispatchers.IO) { listMediaByIds(ids, imageUri) } } fun listVideoByIdsAsync(ids: ArrayList<Int>): Deferred<ArrayList<MediaModel>> { return GlobalScope.async(Dispatchers.IO) { listMediaByIds(ids, videoUri) } } private fun listImageInBucket(bucketId: String?, uri: Uri, from: Int, size: Int): ArrayList<MediaModel> { val list = listMediaIdInBucket(bucketId, uri, from, size) return listMediaByIds(list, uri) } private fun listMediaByIds(list: ArrayList<Int>, uri: Uri): ArrayList<MediaModel> { val selection: String? val result = ArrayList<MediaModel>() val selectionArgs: Array<String?>? if (list.isNullOrEmpty()) { return result } else { val stringBuilder = StringBuilder() val size = list.size stringBuilder.append("$id in (?") for (index in 1 until size) { stringBuilder.append(",").append("?") } stringBuilder.append(")") selection = stringBuilder.toString() selectionArgs = arrayOfNulls(size) list.forEachIndexed { index, i -> selectionArgs[index] = "$i" } } val projection = arrayOf(data, id) val cur = IApplication.app?.contentResolver?.query(uri, projection, selection, selectionArgs, "$dateTime desc") cur ?: return result while (cur.moveToNext()) { val mediaModel = MediaModel() mediaModel.imageUri = cur.getString(cur.getColumnIndex(data)) mediaModel.imageId = cur.getInt(cur.getColumnIndex(id)) result.add(mediaModel) } cur.close() return result } }
4、调用的时候可以这样:
private fun listGallery() = GlobalScope.launch(Dispatchers.Main) { val resultImage = MediaUtils.listImageBucketAsync().await() val firstImage = MediaUtils.listImageInBucketAsync(resultImage[0].bucketId, from, size).await() ILog.e("===image===", IJson.toPrettyFormat(IJson.toJson(firstImage, ArrayList::class.java))) val videoResult = MediaUtils.listVideoBucketAsync().await() val firstVideo = MediaUtils.listVideoInBucketAsync(videoResult[0].bucketId, from, size).await() ILog.e("===video===", IJson.toPrettyFormat(IJson.toJson(firstVideo, ArrayList::class.java))) }
为什么要分页呢?因为 sqlite in 后面的 array 最大是999,即一次最大能查询 999 条记录,再多就崩溃了。另外, 这里,ILog 以及 IJson 是我之前封装的类,就不赘述了。