Bootstrap

Android Glide批量加载Bitmap,拼接组装大Bitmap,更新单个AppCompatImageView,Kotlin(2)

Android Glide批量加载Bitmap,拼接组装大Bitmap,更新单个AppCompatImageView,Kotlin(2)

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

import android.content.Context
import android.os.Bundle
import android.provider.MediaStore
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 TAG = "fly"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val rv1 = findViewById<RecyclerView>(R.id.rv1)

        val layoutManager = LinearLayoutManager(this)
        layoutManager.orientation = LinearLayoutManager.VERTICAL
        val adapter = ImageAdapter(this, 0)
        rv1.adapter = adapter
        rv1.layoutManager = layoutManager

        rv1.setItemViewCacheSize(120)
        rv1.recycledViewPool.setMaxRecycledViews(0, 120)

        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 MyData(var path: String, var index: Int)

    private fun readAllImage(ctx: Context): ArrayList<MyData> {
        val photos = ArrayList<MyData>()

        //读取所有图片
        val cursor = ctx.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
    }
}

import android.content.Context
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView

class ImageAdapter : RecyclerView.Adapter<ImageHolder> {
    private var mCtx: Context? = null

    constructor(ctx: Context, type: Int) : super() {
        mCtx = ctx
    }

    private var items = ArrayList<ArrayList<MainActivity.MyData>>()

    fun dataChanged(items: ArrayList<ArrayList<MainActivity.MyData>>) {
        this.items = items
        notifyDataSetChanged()
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageHolder {
        val view = BatchBitmapView(mCtx!!)
        return ImageHolder(view)
    }

    override fun getItemCount(): Int {
        return items.size
    }

    override fun onBindViewHolder(holder: ImageHolder, position: Int) {
        val bbv = (holder.itemView as? BatchBitmapView)
        bbv?.setRowBitmapData(items[position], position)
    }
}

class ImageHolder : RecyclerView.ViewHolder {
    constructor(itemView: BatchBitmapView) : super(itemView) {

    }
}

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Color
import android.util.AttributeSet
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target

class BatchBitmapView @JvmOverloads constructor(
    val mCtx: Context,
    attributeSet: AttributeSet? = null,
    defStyleAttr: Int = 0
) : AppCompatImageView(mCtx, attributeSet, defStyleAttr) {
    private val mData = mutableListOf<DataBean>()
    private val mTargets = mutableListOf<Target<Bitmap>>()

    companion object {
        const val TAG = "fly/BatchBitmapView"
        const val ROW_SIZE = 16 //一行多少个bitmap
        const val IMAGE_SIZE = 200 //每个小格子图片的尺寸

        var mScreenWidth: Int = 0
        var mScreenHeight: Int = 0

        //整数相除,精度损失的平衡因子
        const val BALANCE_FACTOR = 1
    }

    init {
        //scaleType = ScaleType.FIT_CENTER

        if (mScreenWidth == 0) {
            mScreenWidth = resources.displayMetrics.widthPixels
        }

        if (mScreenHeight == 0) {
            mScreenHeight = resources.displayMetrics.widthPixels / ROW_SIZE + BALANCE_FACTOR
        }
    }

    fun setRowBitmapData(rows: ArrayList<MainActivity.MyData>?, position: Int) {
        mData.clear()

        mTargets.forEach {
            //如果不清除,会发生有些图错放位置。
            Glide.with(mCtx).clear(it)
        }
        mTargets.clear()

        var loadCount = 0
        rows?.forEachIndexed { _, myData ->
            val target = Glide.with(mCtx)
                .asBitmap()
                .centerCrop()
                .override(IMAGE_SIZE)
                .load(myData.path).addListener(object : RequestListener<Bitmap> {
                    override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Bitmap>, isFirstResource: Boolean): Boolean {
                        loadCount++

                        val errorBmp = BitmapFactory.decodeResource(mCtx.resources, android.R.drawable.stat_notify_error)

                        refresh(loadCount, errorBmp)

                        return false
                    }

                    override fun onResourceReady(
                        resource: Bitmap,
                        model: Any,
                        target: Target<Bitmap>?,
                        dataSource: DataSource,
                        isFirstResource: Boolean
                    ): Boolean {
                        loadCount++

                        refresh(loadCount, resource)

                        return false
                    }
                })
                .preload(IMAGE_SIZE, IMAGE_SIZE)

            mTargets.add(target)
        }
    }

    fun refresh(loadCount: Int, bmp: Bitmap) {
        val bean = DataBean(bmp)
        mData.add(bean)

        if (loadCount == ROW_SIZE) {
            val jBmp = joinBitmap()
            (mCtx as AppCompatActivity).runOnUiThread {
                [email protected](jBmp)
            }
        }
    }

    private fun joinBitmap(): Bitmap {
        val bmp = Bitmap.createBitmap(IMAGE_SIZE * ROW_SIZE, IMAGE_SIZE, Bitmap.Config.ARGB_8888)
        val canvas = Canvas(bmp)
        canvas.drawColor(Color.LTGRAY)

        mData.forEachIndexed { idx, dataBean ->
            canvas.drawBitmap(dataBean.bitmap, IMAGE_SIZE * idx.toFloat(), 0f, null)
        }

        return bmp
    }

    data class DataBean(val bitmap: Bitmap)

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        setMeasuredDimension(mScreenWidth, mScreenHeight)
    }
}

Android Glide自定义AppCompatImageView切分成若干小格子,每个小格子onDraw绘制Bitmap,Kotlin(1)_android appcompatimageview-CSDN博客文章浏览阅读1.3k次,点赞18次,收藏21次。本文介绍了如何在Android应用中使用Glide库将AppCompatImageView分割成小格子,并在每个格子上异步加载Bitmap并利用Canvas进行绘制,以提高性能。同时讨论了与直接添加ImageView相比,使用GlideCustomTarget和线性布局动态添加子View时的性能差异以及如何处理圆形头像的需求。https://blog.csdn.net/zhangphil/article/details/134519527

;