Material过渡+Glide显示问题踩坑

作者:苍王 时间:2018.7.12 以下是我这个系列的相关文章,有兴趣可以参考一下,可以给个喜欢或者关注我的文章。

[Android]如何做一个崩溃率少于千分之三噶应用app--章节列表

Android组件化架构热卖中

这里说的坑的情况是使用了Material过渡+Glide显示图片的情况。

1.使用了Material过渡+Glide的图片显示Bitmap能正常过渡的。

2.如果Glide加载图片加入圆角的Transform,此时glide过渡到新的显示页没问题,back键返回首先显示原图(不带圆角)然后再显示圆角,这是感觉会被刷新了一下。这里可以直接用圆角的ImageView来处理,用shape是无效的。

3.如果glide加载的是gif图片,这里使用是要使用asGif提示Glide加载的是gif图。使用material过渡效果,会发现,gif图会被卡住不显示动图,只显示当前帧,使用调试后却了显示动图的情况。如果延迟显示也是能正常显示动图,但是中间会有一段时间黑屏需要用loading处理。如果gif图加上圆角,不要使用自定义扩展ImageView作为圆角View,这样gif图会播放不了,最好使用原生的Glide Transform+ImageView。

4.如果使用glide加载mp4,能正常显示首帧,但是如果使用了material过渡,使用google的exoplayer会发现开始的一秒视频无法正常播放,拉回去发现是可以播放出来的,这里估计是绘制繁忙导致的,这里还是得自行延迟处理或者换回正常的过渡方式了。

5.使用获取视频首帧的问题,这里如果是网络视频最好是上传信息时,已经将首帧的图上传服务器,不然需要在线获取视频的首帧资源。如果视频经过加密处理,那么只能先缓存到本地才能完成,所以这种折腾太过耗时,请设计协议时一定要设计上传视频预览图。

/**
     * 通过url去获取视频的第一帧
     * Android 原生给我们提供了一个MediaMetadataRetriever类
     * 提供了获取url视频第一帧的方法,返回Bitmap对象
     *
     * @param videoUrl
     * @return
     */
    fun getPreviewFromUri(uri: String?, result: (url: String?, previewPath: String?) -> Unit) {
        if (uri == null) {
            return result(null, null)
        }
        val tmpPath = Environment.getExternalStorageDirectory().toString() + File.separator + "XXX/cache/" + EncryptUtils.encryptMD5ToString(uri)  //这里用Sring转md5作为唯一标识
        if (File(tmpPath).exists()) {   //判断图片是否存在
            return result(uri, tmpPath)
        }

        Observable.create(ObservableOnSubscribe<String> {
            var bitmap: Bitmap? = null
            val retriever = MediaMetadataRetriever()  //原生取帧图
            try {
                val resourceUri = uri.trim()
                if (resourceUri.startsWith("http", true)) {
                    retriever.setDataSource(uri, HashMap())
                } else {
                    retriever.setDataSource(uri)
                }
                bitmap = retriever.frameAtTime    //获取第一帧关键帧
            } catch (e: IllegalArgumentException) {
                e.printStackTrace()
            } finally {
                retriever.release()
            }


            var path: String? = null
            if (null != bitmap) {
                path = saveBitmap2File(bitmap, EncryptUtils.encryptMD5ToString(uri))  //保存到本地
            }

            if (null != path) {
                it.onNext(path)
            } else {
                it.onError(Exception("loading bitmap failed"))
            }
            it.onComplete()
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({
                    result(uri, it)
                }, {
                    result(uri, null)
                })
    }

    /**
     * 获取本地视频的第一帧
     *
     * @param uri 本地uri地址
     * @return 预览图地址
     */
    fun getLocalVideoBitmap(context: Context, uri: Uri?, result: (uri: Uri?, previewPath: String?) -> Unit) {
        if (uri == null) {
            return result(null, null)
        }
        val tmpPath = Environment.getExternalStorageDirectory().toString() + File.separator + "XXX/cache/" + EncryptUtils.encryptMD5ToString(uri.toString())
        if (File(tmpPath).exists()) {
            return result(uri, tmpPath)
        }

        val videoPath = FileUtils.getFileAbsolutePath(context, uri)
        var bitmap: Bitmap? = null
        val retriever = MediaMetadataRetriever()
        try {
            //根据文件路径获取缩略图
            retriever.setDataSource(videoPath)
            //获得第一帧图片
            bitmap = retriever.frameAtTime
        } catch (e: IllegalArgumentException) {
            e.printStackTrace()
        } finally {
            retriever.release()
        }
        var imgPath: String? = null
        if (null != bitmap) {
            imgPath = saveBitmap2File(bitmap, EncryptUtils.encryptMD5ToString(uri.toString()))
        }

        return result(uri, imgPath)
    }

//保存图片到本地
    fun saveBitmap2File(bitmap: Bitmap, imgName: String): String? {
        val dir = Environment.getExternalStorageDirectory().toString() + File.separator + "XXX/cache/"
        if (!File(dir).exists()) {
            File(dir).mkdirs()
        }
        val path = dir + imgName
        val file = File(path)
        var out: FileOutputStream? = null
        try {
            out = FileOutputStream(file)
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
            return path
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            try {
                out?.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
        return null
    }
复制代码

6.如果上传的图片是二进制流格式,需要使用读取文件的前几个字节来判断图片的格式。

 /**
     * 判断文件类型
     */
    @Throws(IOException::class)
    fun getType(filePath: String): FileType? {

        var fileHead: String? = getFileHeader(filePath)
        if (fileHead == null || fileHead.isEmpty()) {
            return null
        }
        fileHead = fileHead.toUpperCase()
        val fileTypes = FileType.values()
        for (type in fileTypes) {
            if (fileHead.startsWith(type.value)) {
                return type
            }
        }
        return null
    }

enum class FileType constructor(value: String) {
        JPEG("FFD8FF"),
        PNG("89504E47"),
        GIF("47494638"),
        TIFF("49492A00"),
        RTF("7B5C727466"),
        DOC("D0CF11E0"),
        XLS("D0CF11E0"),
        MDB("5374616E64617264204A"),
        BMP("424D"),
        DWG("41433130"),
        PSD("38425053"),
        XML("3C3F786D6C"),
        HTML("68746D6C3E"),
        PDF("255044462D312E"),
        ZIP("504B0304"),
        RAR("52617221"),
        WAV("57415645"),
        AVI("41564920");

        var value = ""
        init {
            this.value = value
        }
 }
复制代码

7.glide的override指定图片大小和imageView指定大小,最后会是imageView最后生效,对图片有拉伸的效果。如果图片是在RecylverView且图片适配使用wrap_content的情况,RecylerView有可能调整被移动。

如果有其他情况,将会在这里补充,遇到其他问题的童孩,也可以留言,谢谢。

天星技术团QQ:557247785。

猜你喜欢

转载自juejin.im/post/5b473e28f265da0fa8672d93