其实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){
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()
// 将 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()
// 将 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)
}
}