Android Glide自定义AppCompatImageView切分成若干小格子,每个小格子onDraw绘制Bitmap,Kotlin(1)
垂直方向的RecyclerView,每行一个AppCompatImageView,每个AppCompatImageView被均匀切割成n个小格子, 每个小格子通过Glide加载出来Bitmap,然后onDraw绘制整行。
XML
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
implementation("com.github.bumptech.glide:glide:4.16.0")
Kotlin
import android.content.Context
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class MainActivity : AppCompatActivity() {
companion object {
const val VIEW_TYPE = 0
const val TAG = "fly/MainActivity"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rv = findViewById<RecyclerView>(R.id.recycler_view)
val layoutManager = LinearLayoutManager(this)
layoutManager.orientation = LinearLayoutManager.VERTICAL
rv.layoutManager = layoutManager
val adapter = MyAdapter()
rv.adapter = adapter
lifecycleScope.launch(Dispatchers.IO) {
val items = readAllImage(this@MainActivity)
items.reverse()
val data = sliceDataList(items)
withContext(Dispatchers.Main) {
adapter.dataChanged(data)
}
}
}
private fun sliceDataList(data: ArrayList<MyData>): ArrayList<ArrayList<MyData>> {
var k: Int
val lists = ArrayList<ArrayList<MyData>>()
for (i in data.indices step BatchBitmapView.ROW_SIZE) {
val temp = ArrayList<MyData>()
k = 0
for (j in 0 until BatchBitmapView.ROW_SIZE) {
k = i + j
if (k >= data.size) {
break
}
temp.add(data[k])
}
lists.add(temp)
}
return lists
}
class MyAdapter : RecyclerView.Adapter<MyVH>() {
private var items = ArrayList<ArrayList<MyData>>()
fun dataChanged(items: ArrayList<ArrayList<MyData>>) {
this.items = items
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyVH {
Log.d(TAG, "onCreateViewHolder")
val view = BatchBitmapView(parent.context)
return MyVH(view)
}
override fun getItemCount(): Int {
return items.size
}
override fun getItemViewType(position: Int): Int {
return VIEW_TYPE
}
override fun onBindViewHolder(holder: MyVH, position: Int) {
Log.d(TAG, "onBindViewHolder $position")
val biv = (holder.itemView as? BatchBitmapView)
biv?.setRowBitmapData(items[position], position)
}
}
class MyVH(itemView: BatchBitmapView) : RecyclerView.ViewHolder(itemView) {
}
class MyData(var path: String, var index: Int)
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
while (cursor!!.moveToNext()) {
//路径 uri
val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA))
//图片名称
//val name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME))
//图片大小
//val size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE))
photos.add(MyData(path, index++))
}
cursor.close()
return photos
}
}
Kotlin
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.util.Log
import androidx.appcompat.widget.AppCompatImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
class BatchBitmapView @JvmOverloads constructor(
context: Context,
attributeSet: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatImageView(context, attributeSet, defStyleAttr) {
private val mData = mutableListOf<DataBean>()
private val mScreenWidth = resources.displayMetrics.widthPixels
private val mTargets = mutableListOf<CustomTarget<Bitmap>>()
companion object {
const val TAG = "fly/BatchBitmapView"
const val ROW_SIZE = 16 //一行多少个bitmap
const val IMAGE_SIZE = 80 //每个小格子图片的尺寸
}
fun setRowBitmapData(rows: ArrayList<MainActivity.MyData>?, position: Int) {
Log.d(TAG, "setRowBitmapData $position")
mData.clear()
Log.d(TAG, "mTargets.size=${mTargets.size}")
mTargets.forEach {
Glide.with(context).clear(it) //如果不清楚,会发生有些图错放位置。
}
mTargets.clear() //mTargets上下滑动列表会越来越大,清空,一直保持ROW_SIZE.
rows?.forEachIndexed { index, myData ->
val target = object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
val bean = DataBean(resource)
mData.add(bean)
postInvalidate()
}
override fun onLoadCleared(placeholder: Drawable?) {
}
}
Glide.with(context)
.asBitmap()
.centerCrop()
.override(IMAGE_SIZE)
.load(myData.path)
.into(target)
mTargets.add(target)
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(mScreenWidth, IMAGE_SIZE)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
mData.forEachIndexed { index, dataBean ->
canvas.save()
var left = (mScreenWidth / ROW_SIZE) * index
canvas.drawBitmap(dataBean.bitmap, left.toFloat(), 0f, null)
canvas.restore()
}
}
data class DataBean(val bitmap: Bitmap)
}
上面每一行加载16个bitmap绘图,速度很快,因为是canvas直接绘图。但是,如果使用下文的方式:
Android LinearLayout dynamic add child ImageView,Glide load,kotlin_zhangphil的博客-CSDN博客文章浏览阅读645次。【代码】Android Paging 3,kotlin(1)在实际的开发中,虽然Glide解决了快速加载图片的问题,但还有一个问题悬而未决:比如用户的头像,往往用户的头像是从服务器端读出的一个普通矩形图片,但是现在的设计一般要求在APP端的用户头像显示成圆形头像,那么此时虽然Glide可以加载,但加载出来的是一个矩形,如果要Glide_android 毛玻璃圆角。《Android图片加载与缓存开源框架:Android Glide》Android Glide是一个开源的图片加载和缓存处理的第三方框架。https://blog.csdn.net/zhangphil/article/details/132080406即每一行先初始化一个水平的线性布局,然后逐个添加16个ImageView,对比发现明显卡顿,因为是以ViewGroup的方式布局摆放子view形成View。