Android Glide, first start based on loadThumbnail, Kotlin(一)

Android Glide, first start based on loadThumbnail, Kotlin(一)

Kotlin 复制代码
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<MyData>()

        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<TextView>(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<MyData> {
        val photos = ArrayList<MyData>()

        //读取所有图片
        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
    }
}
Kotlin 复制代码
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

相关推荐
灯火不休➴14 分钟前
安卓 ContentProvider 详解:跨应用数据共享的核心方案
android
Tang102444 分钟前
一次讲清楚 Kotlin 的 suspend 关键字到底做了什么?
kotlin
沐怡旸1 小时前
【底层机制】【Android】Android 系统的启动流程
android
limuyang21 小时前
【http3/quic】cronet 已经原生集成在Android内啦!还不快来开开眼!
android·http·google
乌萨奇也要立志学C++2 小时前
【Linux】Ext系列文件系统 从磁盘结构到文件存储的原理剖析
android·linux·缓存·1024程序员节
宋发元3 小时前
IPhone 17 Pro Max拍摄专业画质视频教程
android·gradle·iphone
出门吃三碗饭4 小时前
如何在LLM大语言模型上微调来优化数学推理能力?
android·人工智能·语言模型
shaominjin1235 小时前
Android访问OTG文件全解析:从连接到操作的完整指南Android系统访问U盘的实现机制与操作指南
android
游戏开发爱好者88 小时前
HTTPS 内容抓取实战 能抓到什么、怎么抓、不可解密时如何定位(面向开发与 iOS 真机排查)
android·网络协议·ios·小程序·https·uni-app·iphone
Tom4i10 小时前
Android 系统的进程模型
android