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


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

     

     

    1. import android.content.ContentValues
    2. import android.content.Context
    3. import android.graphics.Color
    4. import android.graphics.drawable.Drawable
    5. import android.net.Uri
    6. import android.os.Bundle
    7. import android.provider.MediaStore
    8. import android.text.TextUtils
    9. import android.view.LayoutInflater
    10. import android.view.View
    11. import android.view.ViewGroup
    12. import android.widget.TextView
    13. import androidx.appcompat.app.AppCompatActivity
    14. import androidx.appcompat.widget.AppCompatImageView
    15. import androidx.core.content.ContextCompat
    16. import androidx.core.view.setPadding
    17. import androidx.lifecycle.lifecycleScope
    18. import androidx.recyclerview.widget.GridLayoutManager
    19. import androidx.recyclerview.widget.LinearLayoutManager
    20. import androidx.recyclerview.widget.RecyclerView
    21. import kotlinx.coroutines.Dispatchers
    22. import kotlinx.coroutines.launch
    23. import kotlinx.coroutines.withContext
    24. import java.text.SimpleDateFormat
    25. class MainActivity : AppCompatActivity() {
    26. companion object {
    27. const val TAG = "glide-fly"
    28. const val SIZE = 450
    29. const val VIEW_TYPE = 0
    30. const val DATE_TYPE = 1
    31. const val SPAN_COUNT = 6
    32. const val PAD_SIZE = 1
    33. var FIRST_START = true
    34. }
    35. override fun onCreate(savedInstanceState: Bundle?) {
    36. super.onCreate(savedInstanceState)
    37. setContentView(R.layout.activity_main)
    38. val rv: RecyclerView = findViewById(R.id.rv)
    39. rv.setHasFixedSize(true)
    40. rv.setItemViewCacheSize(SPAN_COUNT * 10)
    41. /*
    42. rv.setRecyclerListener(object : RecyclerListener {
    43. override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
    44. if ((holder as MyVH).itemView is MyIV) {
    45. GlideApp.with(holder.itemView.context).clear(holder.itemView)
    46. }
    47. }
    48. })
    49. */
    50. val layoutManager = MyGridLayoutManager(this, SPAN_COUNT)
    51. layoutManager.orientation = LinearLayoutManager.VERTICAL
    52. rv.layoutManager = layoutManager
    53. val adapter = MyAdapter(this)
    54. rv.adapter = adapter
    55. lifecycleScope.launch(Dispatchers.IO) {
    56. val items = readAllImage(this@MainActivity)
    57. items.sortByDescending {
    58. it.dateModified
    59. }
    60. val lists = items.distinctBy {
    61. it.dateString
    62. }
    63. lists.forEach { it_lists ->
    64. val idx = items.indexOfFirst {
    65. it_lists.dateString == it.dateString
    66. }
    67. val data = MyData()
    68. data.type = DATE_TYPE
    69. data.dateString = it_lists.dateString
    70. items.add(idx, data) //不要直接加 it_Lists,这里面涉及到List的深拷贝/浅拷贝问题。
    71. }
    72. withContext(Dispatchers.Main) {
    73. adapter.dataChanged(items)
    74. }
    75. }
    76. layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
    77. override fun getSpanSize(position: Int): Int {
    78. return if (adapter.getItemViewType(position) == DATE_TYPE) {
    79. //group,标题
    80. SPAN_COUNT
    81. } else {
    82. //单个小格子
    83. 1
    84. }
    85. }
    86. }
    87. }
    88. class MyGridLayoutManager : GridLayoutManager {
    89. constructor(ctx: Context, cnt: Int) : super(ctx, cnt) {
    90. }
    91. override fun getExtraLayoutSpace(state: RecyclerView.State?): Int {
    92. return 1000
    93. }
    94. }
    95. class MyAdapter : RecyclerView.Adapter<MyVH> {
    96. private var mItems = arrayListOf()
    97. private var mContext: Context? = null
    98. private var mPlaceholder: Drawable? = null
    99. private var mError: Drawable? = null
    100. constructor(ctx: Context) {
    101. mContext = ctx
    102. mPlaceholder = ContextCompat.getDrawable(mContext!!, android.R.drawable.ic_menu_gallery)
    103. mError = ContextCompat.getDrawable(mContext!!, android.R.drawable.stat_notify_error)
    104. }
    105. fun dataChanged(items: ArrayList<MyData>) {
    106. this.mItems = items
    107. notifyDataSetChanged()
    108. }
    109. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyVH {
    110. var v: View?
    111. if (viewType == VIEW_TYPE) {
    112. v = MyIV(mContext!!)
    113. } else {
    114. v = LayoutInflater.from(mContext!!).inflate(android.R.layout.simple_list_item_1, null)
    115. v.setBackgroundColor(Color.LTGRAY)
    116. }
    117. return MyVH(v!!)
    118. }
    119. override fun getItemCount(): Int {
    120. return mItems.size
    121. }
    122. override fun getItemViewType(position: Int): Int {
    123. return mItems[position].type
    124. }
    125. private fun getImageUri(context: Context, filePath: String): Uri? {
    126. val cursor = context.contentResolver.query(
    127. MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
    128. arrayOf(MediaStore.Images.Media._ID),
    129. MediaStore.Images.Media.DATA + "=? ",
    130. arrayOf(filePath),
    131. null
    132. )
    133. return if (cursor != null && cursor.moveToFirst()) {
    134. val id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID))
    135. val baseUri = Uri.parse("content://media/external/images/media")
    136. Uri.withAppendedPath(baseUri, "" + id)
    137. } else {
    138. val values = ContentValues()
    139. values.put(MediaStore.Images.Media.DATA, filePath)
    140. context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
    141. }
    142. }
    143. override fun onBindViewHolder(holder: MyVH, position: Int) {
    144. val type = getItemViewType(position)
    145. if (type == VIEW_TYPE) {
    146. val path = mItems[holder.adapterPosition].path
    147. val miv = holder.itemView as MyIV
    148. if (position > 50) {
    149. FIRST_START = false
    150. }
    151. if (FIRST_START && position < 50) {
    152. miv.setImageDrawable(mPlaceholder)
    153. val uri = getImageUri(mContext!!, path!!)
    154. QuickLoader.Instance().start(mContext!!, uri!!, miv)
    155. } else {
    156. GlideApp.with(mContext!!)
    157. .asBitmap()
    158. .load(path)
    159. .centerCrop()
    160. .override(SIZE)
    161. .placeholder(mPlaceholder)
    162. .error(mError)
    163. .into(miv)
    164. }
    165. } else if (type == DATE_TYPE) {
    166. holder.itemView.findViewById(android.R.id.text1).text = "${mItems[position].dateString}"
    167. }
    168. }
    169. }
    170. class MyVH : RecyclerView.ViewHolder {
    171. constructor(itemView: View) : super(itemView) {
    172. }
    173. }
    174. class MyIV : AppCompatImageView {
    175. constructor(ctx: Context) : super(ctx) {
    176. setPadding(PAD_SIZE)
    177. }
    178. override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    179. super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    180. val w = MeasureSpec.getSize(widthMeasureSpec)
    181. val h = MeasureSpec.getSize(heightMeasureSpec)
    182. val size = Math.max(w, h) //取w,h的最大值。
    183. setMeasuredDimension(size, size) //使得ImageView为正方形。
    184. }
    185. }
    186. class MyData {
    187. var type = VIEW_TYPE
    188. var dateModified: Long? = 0L
    189. var dateString: String? = null
    190. var path: String? = null
    191. var index: Int? = null
    192. override fun toString(): String {
    193. return "MyData(type=$type, dateModified=$dateModified, dateString=$dateString, path=$path, index=$index)"
    194. }
    195. }
    196. private fun readAllImage(context: Context): ArrayList {
    197. val photos = ArrayList()
    198. //读取所有图片
    199. val cursor = context.contentResolver.query(
    200. MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null
    201. )
    202. var index = 0
    203. val sdf = SimpleDateFormat("yyyy-MM-dd")
    204. while (cursor!!.moveToNext()) {
    205. //路径 uri
    206. val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA))
    207. if (TextUtils.isEmpty(path)) {
    208. continue
    209. }
    210. val dateModified = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_MODIFIED))
    211. //图片名称
    212. //val name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME))
    213. //图片大小
    214. //val size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE))
    215. val dateStr = sdf.format(dateModified?.toLong()!! * 1000)
    216. val data = MyData()
    217. data.type = VIEW_TYPE
    218. data.path = path
    219. data.dateModified = dateModified.toLong()
    220. data.dateString = dateStr
    221. data.index = index++
    222. photos.add(data)
    223. }
    224. cursor.close()
    225. return photos
    226. }
    227. }

     

     

     

    1. import android.content.Context
    2. import android.net.Uri
    3. import android.os.Handler
    4. import android.os.Looper
    5. import android.os.Message
    6. import android.util.Log
    7. import android.util.Size
    8. import androidx.appcompat.app.AppCompatActivity
    9. import java.util.concurrent.Executors
    10. class QuickLoader {
    11. private var mHandler: MsgHandler? = null
    12. companion object {
    13. const val THREAD_NUMBER = 4
    14. const val WHAT = 0xf01
    15. private val inst by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { QuickLoader() }
    16. fun Instance() = inst
    17. }
    18. private constructor() {
    19. Log.d(MainActivity.TAG, "QuickLoader 初始化")
    20. mHandler = MsgHandler()
    21. }
    22. fun start(ctx: Context, uri: Uri, miv: MainActivity.MyIV) {
    23. val msg = mHandler?.obtainMessage(WHAT)
    24. msg?.obj = LoadTask(ctx, uri, miv)
    25. mHandler?.sendMessage(msg!!)
    26. }
    27. private class MsgHandler : Handler {
    28. private var mExecutorService = Executors.newFixedThreadPool(THREAD_NUMBER)
    29. constructor() : super(Looper.getMainLooper()) {
    30. Log.d(MainActivity.TAG, "MsgHandler 初始化")
    31. }
    32. override fun handleMessage(msg: Message) {
    33. mExecutorService.execute(msg.obj as LoadTask)
    34. }
    35. fun destroy() {
    36. removeMessages(WHAT)
    37. mExecutorService.shutdownNow()
    38. mExecutorService = null
    39. }
    40. }
    41. class LoadTask(private val ctx: Context, private val uri: Uri, private val miv: MainActivity.MyIV) : Runnable {
    42. override fun run() {
    43. Log.d(MainActivity.TAG, "run $uri")
    44. val t = System.currentTimeMillis()
    45. val bmp = ctx.contentResolver?.loadThumbnail(uri, Size(MainActivity.SIZE, MainActivity.SIZE), null)
    46. Log.d(MainActivity.TAG, "LoadTask 耗时:${System.currentTimeMillis() - t} $uri")
    47. (ctx as AppCompatActivity).runOnUiThread {
    48. miv.setImageBitmap(bmp)
    49. }
    50. }
    51. }
    52. fun destroy() {
    53. mHandler?.destroy()
    54. mHandler = null
    55. }
    56. }

     

     

     

     

     

    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

     

  • 相关阅读:
    Zigbee安全概述
    SpringBoot+EasyExcel设置excel样式
    前端三刺客---CSS
    vue防抖和限流
    自定义表单验证规则 (自定义一个方法)
    【Database System Concept 7th】Chapter 2读书笔记
    Go中rune类型浅析
    2022-12-04一周学习
    苹果手机的祛魅时刻,国产厂商的颠覆征程
    MySQL笔记之一致性视图与MVCC实现
  • 原文地址:https://blog.csdn.net/zhangphil/article/details/139743546