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

相关推荐
ai2work4 小时前
ch15 加载模型与初始化上下文
kotlin
hai_android4 小时前
Kotlin / Android 常用函数使用示例手册
android·java·kotlin
hai_android6 小时前
Android MeasureSpec 详解
android·java·kotlin
又见情义7 小时前
RK3568 Android 13 本地 U 盘 OTA 升级实战:基于 RKUpdateService 的完整流程
android
Godikov8 小时前
Android10后台弹窗与APK自动更新
android
ai2work9 小时前
ch11 持久化:DataStore + Gson 多会话
kotlin
又见情义9 小时前
RK3568 Android 13 USB OTG/Host 切换调试经验分享
android
终端安全笔记10 小时前
iOS 27 之后「策略空转」:设备升级不报错,但旧策略不再管它
android·网络·安全·ios·智能手机
JMchen11 小时前
实战案例:实现120fps流畅的渐变进度条
android·kotlin·canvas
敲代码的瓦龙12 小时前
Jetpack?DataBinding!!!
android·java·开发语言·mysql·android-studio