Android Glide加载小gif和图片比较模糊

发布时间 2023-07-13 14:49:51作者: 勤奋的小铁

其实Glide加载还是可以将小图片加载的非常清晰的,

可以通过Glide转换为Bitmap

利用Drawable将setFilterBitmap为true

但是这玩意解决不了GIF。在没有找到库的情况下:我直接自定义view
通过

pl.droidsonroids.gif:android-gif-drawable

获取时间间隔并将gif解析成bitmap获取像素 再通过Choreographer进行页面刷新

/**
 * GIF支持类 兼容 图片
 * @author xiaotie https://www.cnblogs.com/xiao-tie/
 * @time  2023/7/11 15:14
 * @param widthNum 像素宽
 * @param heightNum 像素高
 * @param seekGif 时间戳间隔
 * @param pos 当前显示位置
 * @param data 像素数据组
 */
data class GifSupport(val widthNum:Int, val heightNum:Int, val seekGif:Long, var pos:Int = 0, val data: MutableList<IntArray>){
    fun next(){
        pos++
        if(pos >= data.size){
            pos = 0
        }
    }
}

/**
 * 以像素点加载gif支持图片默认以宽度为标准(以高度为标准暂时不需要 未开发)
 * @author xiaotie https://www.cnblogs.com/xiao-tie/
 * @time  2023/7/11 14:48
 */
class GIFView : View, Choreographer.FrameCallback{

    constructor(context: Context?):super(context)
    constructor(context: Context?, attrs: AttributeSet?):super(context, attrs)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int):super(context, attrs, defStyleAttr)

    private var gifSupport: GifSupport? = null
    private var size = 0
    private var margin = 0f
    private val paint = Paint()
    private val choreographer: Choreographer = Choreographer.getInstance()
    private var isRendering = false

    ///是否加网格
    var isGrid = true
        set(value) {
            field = value
            invalidate()
        }

    var widthDigit = 16
        set(value) {
            field = value
            requestLayout()
        }
    var heightDigit = 16
        set(value) {
            field = value
            requestLayout()
        }
    var path = Path()

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val measuredWidth = MeasureSpec.getSize(widthMeasureSpec)
        size = measuredWidth / (gifSupport?.widthNum?:widthDigit)
        margin = (MeasureSpec.getSize(widthMeasureSpec).toFloat() - size * (gifSupport?.widthNum?:widthDigit)) / 2
        val measuredHeight = size * (gifSupport?.heightNum?:heightDigit) + margin*2
        setMeasuredDimension(measuredWidth, measuredHeight.toInt())
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        startRendering()
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        stopRendering()
    }

    @SuppressLint("DrawAllocation")
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        gifSupport?.apply {
            paint.reset()
            for (y in 0 until heightNum){
                for (x in 0 until widthNum){
                    paint.color = data[pos][x + widthNum*y]
                    val left = margin + x * size
                    val top = margin + y * size
                    canvas?.drawRect(Rect(left.toInt(), top.toInt(),
                        (left+size).toInt(), (top+size).toInt()),paint)
                }
            }
            if(isGrid){
                path.reset()
                for (y in 0 until heightNum){
                    path.moveTo(margin,margin + y * size)
                    path.lineTo(width-margin, margin + y * size)
                }
                for (x in 0 until widthNum){
                    path.moveTo(margin + x * size, margin)
                    path.lineTo(margin + x * size, height-margin)
                }
                paint.color = Color.parseColor("#66000000")
                paint.strokeWidth = 1f
                paint.style = Paint.Style.STROKE
                canvas?.drawPath(path,paint)
            }
        }
    }

    override fun doFrame(frameTimeNanos: Long) {
        gifSupport?.next()
        gifSupport?.let {
            invalidate() // 强制重绘视图
        }?: kotlin.run {
            stopRendering()
        }
        if (isRendering) {
            choreographer.postFrameCallbackDelayed(this, gifSupport?.seekGif?:100) // 每 200 毫秒刷新一次
        }
    }

    private fun startRendering() {
        if (!isRendering) {
            isRendering = true
            choreographer.postFrameCallback(this)
        }
    }

    private fun stopRendering() {
        invalidate()
        if (isRendering) {
            isRendering = false
            choreographer.removeFrameCallback(this)
        }
    }

    private suspend fun getHttpToStream(url:String) = withContext(Dispatchers.IO) {
        val client = OkHttpClient()
        val request: Request = Request.Builder()
            .url(url)
            .build()
        try {
            val response = client.newCall(request).execute()
            response.body!!.byteStream()
        }catch (e:Exception){
            e.printStackTrace()
            null
        }
    }
    private suspend fun getFileToStream(filePath:String) = withContext(Dispatchers.IO) {
        val file = File(filePath)
        try {
            val inputStream = FileInputStream(file) as InputStream
            inputStream
        }catch (e:Exception){
            e.printStackTrace()
            null
        }
    }

    /**
     * 颜色填充
     * @author xiaotie https://www.cnblogs.com/xiao-tie/
     * @time  2023/7/12 15:06
     */
    fun setColorData(@ColorInt colorInt: Int) {
        val colors:IntArray = Array(widthDigit * heightDigit) { colorInt }.toIntArray()
        gifSupport = GifSupport(widthDigit,heightDigit,10,0, mutableListOf(colors))
        invalidate()
    }

    /**
     * 资源链接 支持本地链接和图片链接
     * @author xiaotie https://www.cnblogs.com/xiao-tie/
     * @time  2023/7/12 15:06
     */
    fun setUrlOrFile(link :String?) {
        link?:return
        gifSupport = null
        GlobalScope.launch {
            if(link.contains("http")){
                getHttpToStream(link)
            }else{
                getFileToStream(link)
            }?.also {
                if(link.contains(".gif")){
                    val bis = BufferedInputStream(it)
                    val resource =  GifDrawable( bis )
                    val width = resource.intrinsicWidth
                    val height = resource.intrinsicHeight
                    val bitmapsData = mutableListOf<IntArray>()
                    // 将 GIF 每帧的像素数据写入 ByteBuffer
                    for (i in 0 until resource.numberOfFrames) {
                        val frame = resource.seekToFrameAndGet(i)
                        val framePixels = IntArray(width * height)
                        frame.getPixels(framePixels, 0, width, 0, 0, width, height)
                        bitmapsData.add(framePixels)
                    }
                    gifSupport = GifSupport(width,height,resource.duration.toLong()/resource.numberOfFrames, 0,bitmapsData)
                    post {
                        widthDigit = width
                        heightDigit = height
                    }
                    startRendering()
                    resource.recycle()
                }else if(link.contains(".png")){
                    val frame = BitmapFactory.decodeStream(it)
                    setBitmap(frame)
                    it.close()
                }
            }
        }
    }

    /**
     * 设置图片
     * @author xiaotie https://www.cnblogs.com/xiao-tie/
     * @time  2023/7/13 10:05
     */
    fun setBitmap(frame:Bitmap){
        val width = frame.width
        val height = frame.height
        val framePixels = IntArray(width * height)
        frame.getPixels(framePixels, 0, width, 0, 0, width, height)
        gifSupport = GifSupport(width,height,10,0, mutableListOf(framePixels))
        post {
            widthDigit = width
            heightDigit = height
        }
        invalidate()
    }

    /**
     * 设置gif数据
     * @author xiaotie https://www.cnblogs.com/xiao-tie/
     * @time  2023/7/13 10:06
     * @param byte Byte数组
     */
    fun setRawGifBytes(byte: ByteArray){
        val resource =  GifDrawable( byte )
        val width = resource.intrinsicWidth
        val height = resource.intrinsicHeight
        val bitmapsData = mutableListOf<IntArray>()
        // 将 GIF 每帧的像素数据写入 ByteBuffer
        for (i in 0 until resource.numberOfFrames) {
            val frame = resource.seekToFrameAndGet(i)
            val framePixels = IntArray(width * height)
            frame.getPixels(framePixels, 0, width, 0, 0, width, height)
            bitmapsData.add(framePixels)
        }
        gifSupport = GifSupport(width,height,resource.duration.toLong()/resource.numberOfFrames, 0,bitmapsData)
        widthDigit = width
        heightDigit = height
        startRendering()
        resource.recycle()
    }

    /**
     * 设置图片数据
     * @author xiaotie https://www.cnblogs.com/xiao-tie/
     * @time  2023/7/13 10:06
     * @param byte Byte数组
     */
    fun setRawPictureBytes(byte: ByteArray) {
        val frame = BitmapFactory.decodeByteArray(byte,0,byte.size)
        setBitmap(frame)
    }
}