运行时错误,AnimatedVectorDrawableCompat不能转型为AnimatedVectorDrawable

使用androidx.appcompat库的APP,在小米TV上闪退,原因是res/drawable目录下使用了矢量动画资源<animated-vector>,当View获得焦点会开始矢量动画。View去获取getDrawable() 转型为 AnimatedVectorDrawable时报错。

AnimatedVectorDrawable avd = (AnimatedVectorDrawable) getDrawable()

自Android 5.0(API 21)开始,Vectordrawable(矢量图像)正式得到了支持,可以通过VectorDrawable和AnimatedVectorDrawable来实现矢量图像和动画。这个APP最低支持Android 5.0的,而小米TV是Android 6.0, 应该原生就支持android.graphics.drawable.AnimatedVectorDrawable, 所以代码里使用的就是AnimatedVectorDrawable类,没有用AnimatedVectorDrawableCompat,但却发生了AnimatedVectorDrawableCompat不能转型为AnimatedVectorDrawable的错误,在Sony(Android 9)上就没问题,所以怀疑还是Android版本问题。

如果是使用AnimatedVectorDrawableCompat,有两种方式:

1、xml布局文件中ImageView通过android:srcCompat引用

2、代码里setImageResource()引用

APP中我们是通过方式2设置的<animated-vector>资源,所以从这里开始排查。

布局文件中的ImageView在inflate的时候会被实例化为AppCompatImageView

public AppCompatImageView(
        @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(TintContextWrapper.wrap(context), attrs, defStyleAttr);

    ThemeUtils.checkAppCompatTheme(this, getContext());

    mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
    mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr);

    mImageHelper = new AppCompatImageHelper(this);
    mImageHelper.loadFromAttributes(attrs, defStyleAttr);
}

@Override
public void setImageResource(@DrawableRes int resId) {
    if (mImageHelper != null) {
        // Intercept this call and instead retrieve the Drawable via the image helper
        mImageHelper.setImageResource(resId);
    }
}

AppCompatImageView.setImageResource(resId)调用了mImageHelper.setImageResource(resId)

//androidx.appcompat.widget.AppCompatImageHelper
public void setImageResource(int resId) {
    if (resId != 0) {
        final Drawable d = AppCompatResources.getDrawable(mView.getContext(), resId);
        if (d != null) {
            DrawableUtils.fixDrawable(d);
        }
        mView.setImageDrawable(d);
    } else {
        mView.setImageDrawable(null);
    }

    applySupportImageTint();
}

 继续看final Drawable d = AppCompatResources.getDrawable(mView.getContext(), resId);

//androidx.appcompat.content.res.AppCompatResources
public static Drawable getDrawable(@NonNull Context context, @DrawableRes int resId) {
    return ResourceManagerInternal.get().getDrawable(context, resId);
}

//androidx.appcompat.widget.ResourceManagerInternal
public static synchronized ResourceManagerInternal get() {
    if (INSTANCE == null) {
        INSTANCE = new ResourceManagerInternal();
        installDefaultInflateDelegates(INSTANCE);
    }
    return INSTANCE;
}

第一次创建ResourceManagerInternal时候会添加加载对应xml资源文件代理对象,这里就是关键,

private static void installDefaultInflateDelegates(@NonNull ResourceManagerInternal manager) {
    // This sdk version check will affect src:appCompat code path.
    // Although VectorDrawable exists in Android framework from Lollipop, AppCompat will use
    // (Animated)VectorDrawableCompat before Nougat to utilize bug fixes & feature backports.
    if (Build.VERSION.SDK_INT < 24) {
        manager.addDelegate("vector", new VdcInflateDelegate());
        manager.addDelegate("animated-vector", new AvdcInflateDelegate());
        manager.addDelegate("animated-selector", new AsldcInflateDelegate());
        manager.addDelegate("drawable", new DrawableDelegate());
    }
}

根据上面的源码,可以知道,Android API < 24 (Android O)都会使用VectorDrawableCompat以及AnimatedVectorDrawableCompat等,也就是ImageView.setImageResource(R.drawable.xxxx)后,资源文件解析后生成的实际上是AnimatedVectorDrawableCompat对象,所以才会出现在小米电视上

(AnimatedVectorDrawable) getDrawable() 返回 AnimatedVectorDrawableCompat, 强制转型为AnimatedVectorDrawable的错误。

在Android API >= 21 (Android P),不管是使用(AnimatedVectorDrawableCompat) getDrawable()或是(AnimatedVectorDrawable) getDrawable() 都存在问题, 因为AppCompat库本身就可能会返回这两种类型的对象,我们只能做兼容处理。

比如

Drawable drawable = getDrawable();

if (drawable instanceof AnimatedVectorDrawableCompat) {

 ………

} else if (drawable instanceof AnimatedVectorDrawable) {

……..

}

又或者直接转为Animatable启动动画

Animatable animatable = (Animatable) getDrawable()

animatable.start()

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值