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