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

相关推荐
修炼者1 小时前
Android Studio的技巧
android·android studio
雨白1 小时前
ARouter 入门指南:从基本跳转到对象传递
android
用户69371750013842 小时前
17.Kotlin 类:类的形态(四):枚举类 (Enum Class)
android·后端·kotlin
h***34632 小时前
MS SQL Server 实战 排查多列之间的值是否重复
android·前端·后端
用户69371750013842 小时前
16.Kotlin 类:类的形态(三):密封类 (Sealed Class)
android·后端·kotlin
年小个大4 小时前
优化App启动时间?startup-coroutine是什么?
性能优化·架构·kotlin
摆烂积极分子4 小时前
安卓开发学习-安卓版本
android·学习
n***26566 小时前
MySQL JSON数据类型全解析(JSON datatype and functions)
android·mysql·json
t***82116 小时前
mysql的主从配置
android·mysql·adb
YF02118 小时前
Frida如何稳定连接PC端跟Android手机端
android·mac·xposed