- import android.content.ContentValues
- import android.content.Context
- import android.graphics.Color
- import android.graphics.drawable.Drawable
- import android.net.Uri
- import android.os.Bundle
- import android.provider.MediaStore
- import android.text.TextUtils
- import android.view.LayoutInflater
- import android.view.View
- import android.view.ViewGroup
- import android.widget.TextView
- import androidx.appcompat.app.AppCompatActivity
- import androidx.appcompat.widget.AppCompatImageView
- import androidx.core.content.ContextCompat
- import androidx.core.view.setPadding
- import androidx.lifecycle.lifecycleScope
- import androidx.recyclerview.widget.GridLayoutManager
- import androidx.recyclerview.widget.LinearLayoutManager
- import androidx.recyclerview.widget.RecyclerView
- import kotlinx.coroutines.Dispatchers
- import kotlinx.coroutines.launch
- import kotlinx.coroutines.withContext
- import java.text.SimpleDateFormat
-
- class MainActivity : AppCompatActivity() {
- companion object {
- const val TAG = "glide-fly"
-
- const val SIZE = 450
-
- const val VIEW_TYPE = 0
- const val DATE_TYPE = 1
-
- const val SPAN_COUNT = 6
-
- const val PAD_SIZE = 1
-
- var FIRST_START = true
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
-
- val rv: RecyclerView = findViewById(R.id.rv)
-
- rv.setHasFixedSize(true)
- rv.setItemViewCacheSize(SPAN_COUNT * 10)
-
- /*
- rv.setRecyclerListener(object : RecyclerListener {
- override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
- if ((holder as MyVH).itemView is MyIV) {
- GlideApp.with(holder.itemView.context).clear(holder.itemView)
- }
- }
- })
- */
-
- val layoutManager = MyGridLayoutManager(this, SPAN_COUNT)
- layoutManager.orientation = LinearLayoutManager.VERTICAL
- rv.layoutManager = layoutManager
-
- val adapter = MyAdapter(this)
- rv.adapter = adapter
-
- lifecycleScope.launch(Dispatchers.IO) {
- val items = readAllImage(this@MainActivity)
-
- items.sortByDescending {
- it.dateModified
- }
-
- val lists = items.distinctBy {
- it.dateString
- }
-
- lists.forEach { it_lists ->
- val idx = items.indexOfFirst {
- it_lists.dateString == it.dateString
- }
-
- val data = MyData()
- data.type = DATE_TYPE
- data.dateString = it_lists.dateString
- items.add(idx, data) //不要直接加 it_Lists,这里面涉及到List的深拷贝/浅拷贝问题。
- }
-
- withContext(Dispatchers.Main) {
- adapter.dataChanged(items)
- }
- }
-
- layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
- override fun getSpanSize(position: Int): Int {
- return if (adapter.getItemViewType(position) == DATE_TYPE) {
- //group,标题
- SPAN_COUNT
- } else {
- //单个小格子
- 1
- }
- }
- }
- }
-
- class MyGridLayoutManager : GridLayoutManager {
- constructor(ctx: Context, cnt: Int) : super(ctx, cnt) {
-
- }
-
- override fun getExtraLayoutSpace(state: RecyclerView.State?): Int {
- return 1000
- }
- }
-
- class MyAdapter : RecyclerView.Adapter<MyVH> {
- private var mItems = arrayListOf
() -
- private var mContext: Context? = null
- private var mPlaceholder: Drawable? = null
- private var mError: Drawable? = null
-
- constructor(ctx: Context) {
- mContext = ctx
-
- mPlaceholder = ContextCompat.getDrawable(mContext!!, android.R.drawable.ic_menu_gallery)
- mError = ContextCompat.getDrawable(mContext!!, android.R.drawable.stat_notify_error)
- }
-
- fun dataChanged(items: ArrayList<MyData>) {
- this.mItems = items
- notifyDataSetChanged()
- }
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyVH {
- var v: View?
- if (viewType == VIEW_TYPE) {
- v = MyIV(mContext!!)
- } else {
- v = LayoutInflater.from(mContext!!).inflate(android.R.layout.simple_list_item_1, null)
- v.setBackgroundColor(Color.LTGRAY)
- }
-
- return MyVH(v!!)
- }
-
- override fun getItemCount(): Int {
- return mItems.size
- }
-
- override fun getItemViewType(position: Int): Int {
- return mItems[position].type
- }
-
- private fun getImageUri(context: Context, filePath: String): Uri? {
- val cursor = context.contentResolver.query(
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
- arrayOf(MediaStore.Images.Media._ID),
- MediaStore.Images.Media.DATA + "=? ",
- arrayOf(filePath),
- null
- )
-
- return if (cursor != null && cursor.moveToFirst()) {
- val id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID))
- val baseUri = Uri.parse("content://media/external/images/media")
- Uri.withAppendedPath(baseUri, "" + id)
- } else {
- val values = ContentValues()
- values.put(MediaStore.Images.Media.DATA, filePath)
- context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
- }
- }
-
- override fun onBindViewHolder(holder: MyVH, position: Int) {
- val type = getItemViewType(position)
- if (type == VIEW_TYPE) {
- val path = mItems[holder.adapterPosition].path
- val miv = holder.itemView as MyIV
-
- if (position > 50) {
- FIRST_START = false
- }
-
- if (FIRST_START && position < 50) {
- miv.setImageDrawable(mPlaceholder)
-
- val uri = getImageUri(mContext!!, path!!)
- QuickLoader.Instance().start(mContext!!, uri!!, miv)
- } else {
- GlideApp.with(mContext!!)
- .asBitmap()
- .load(path)
- .centerCrop()
- .override(SIZE)
- .placeholder(mPlaceholder)
- .error(mError)
- .into(miv)
- }
- } else if (type == DATE_TYPE) {
- holder.itemView.findViewById
(android.R.id.text1).text = "${mItems[position].dateString}" - }
- }
- }
-
- class MyVH : RecyclerView.ViewHolder {
- constructor(itemView: View) : super(itemView) {
-
- }
- }
-
- class MyIV : AppCompatImageView {
- constructor(ctx: Context) : super(ctx) {
- setPadding(PAD_SIZE)
- }
-
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec)
- val w = MeasureSpec.getSize(widthMeasureSpec)
- val h = MeasureSpec.getSize(heightMeasureSpec)
- val size = Math.max(w, h) //取w,h的最大值。
- setMeasuredDimension(size, size) //使得ImageView为正方形。
- }
- }
-
- class MyData {
- var type = VIEW_TYPE
-
- var dateModified: Long? = 0L
- var dateString: String? = null
- var path: String? = null
- var index: Int? = null
-
- override fun toString(): String {
- return "MyData(type=$type, dateModified=$dateModified, dateString=$dateString, path=$path, index=$index)"
- }
- }
-
- private fun readAllImage(context: Context): ArrayList
{ - val photos = ArrayList
() -
- //读取所有图片
- val cursor = context.contentResolver.query(
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null
- )
-
- var index = 0
- val sdf = SimpleDateFormat("yyyy-MM-dd")
- while (cursor!!.moveToNext()) {
- //路径 uri
- val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA))
- if (TextUtils.isEmpty(path)) {
- continue
- }
-
- val dateModified = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_MODIFIED))
-
- //图片名称
- //val name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME))
- //图片大小
- //val size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE))
-
- val dateStr = sdf.format(dateModified?.toLong()!! * 1000)
-
- val data = MyData()
- data.type = VIEW_TYPE
- data.path = path
- data.dateModified = dateModified.toLong()
- data.dateString = dateStr
- data.index = index++
-
- photos.add(data)
- }
- cursor.close()
-
- return photos
- }
- }
- import android.content.Context
- import android.net.Uri
- import android.os.Handler
- import android.os.Looper
- import android.os.Message
- import android.util.Log
- import android.util.Size
- import androidx.appcompat.app.AppCompatActivity
- import java.util.concurrent.Executors
-
-
- class QuickLoader {
- private var mHandler: MsgHandler? = null
-
- companion object {
- const val THREAD_NUMBER = 4
-
- const val WHAT = 0xf01
-
- private val inst by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { QuickLoader() }
- fun Instance() = inst
- }
-
- private constructor() {
- Log.d(MainActivity.TAG, "QuickLoader 初始化")
- mHandler = MsgHandler()
- }
-
- fun start(ctx: Context, uri: Uri, miv: MainActivity.MyIV) {
- val msg = mHandler?.obtainMessage(WHAT)
- msg?.obj = LoadTask(ctx, uri, miv)
- mHandler?.sendMessage(msg!!)
- }
-
- private class MsgHandler : Handler {
- private var mExecutorService = Executors.newFixedThreadPool(THREAD_NUMBER)
-
- constructor() : super(Looper.getMainLooper()) {
- Log.d(MainActivity.TAG, "MsgHandler 初始化")
- }
-
- override fun handleMessage(msg: Message) {
- mExecutorService.execute(msg.obj as LoadTask)
- }
-
- fun destroy() {
- removeMessages(WHAT)
-
- mExecutorService.shutdownNow()
- mExecutorService = null
- }
- }
-
- class LoadTask(private val ctx: Context, private val uri: Uri, private val miv: MainActivity.MyIV) : Runnable {
- override fun run() {
- Log.d(MainActivity.TAG, "run $uri")
-
- val t = System.currentTimeMillis()
- val bmp = ctx.contentResolver?.loadThumbnail(uri, Size(MainActivity.SIZE, MainActivity.SIZE), null)
- Log.d(MainActivity.TAG, "LoadTask 耗时:${System.currentTimeMillis() - t} $uri")
-
- (ctx as AppCompatActivity).runOnUiThread {
- miv.setImageBitmap(bmp)
- }
- }
- }
-
- fun destroy() {
- mHandler?.destroy()
- mHandler = null
- }
- }
Android Glide a simple way of load quickly when the number of pictures is large,Kotlin(3)-CSDN博客文章浏览阅读689次,点赞30次,收藏14次。kotlin用object实现单例模式,companion object与java静态_zhangphil的博客-CSDN博客。【代码】Android Paging 3,kotlin(1)在实际的开发中,虽然Glide解决了快速加载图片的问题,但还有一个问题悬而未决:比如用户的头像,往往用户的头像是从服务器端读出的一个普通矩形图片,但是现在的设计一般要求在APP端的用户头像显示成圆形头像,那么此时虽然Glide可以加载,但加载出来的是一个矩形,如果要Glide_android 毛玻璃圆角。https://blog.csdn.net/zhangphil/article/details/139495147Android缩略图ThumbnailUtils.createImageThumbnail耗时约为contentResolver.loadThumbnail的2-10倍,Kotlin-CSDN博客文章浏览阅读812次,点赞17次,收藏5次。android根据图片资源的drawable id转化为Uri,java_android drawable 转uri-CSDN博客。Android 13手机图片存储File路径转Uri,Java_android file 转uri-CSDN博客。文章浏览阅读690次。Android Uri转File path路径,Kotlin_android uri 转 file-CSDN博客。Android 13手机图片存储File路径转Uri,Java_android file 转uri-CSDN博客。
https://blog.csdn.net/zhangphil/article/details/139663146