You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android自定义View保存状态时如何将Drawable加入Parcelable对象?

问题

我正在开发Android应用的自定义View,该View包含一个存储在res/drawable目录下的VectorDrawable对象。为保存View状态,我创建了SavedState类用于onSaveInstanceState和onRestoreInstanceState方法中,代码如下:

private static class SavedState extends BaseSavedState {
        Drawable picture;
        Float degree = 0f;

        public SavedState(Parcelable superState) {
            super(superState);
        }

        public SavedState(Parcel source) {
            super(source);
            degree = source.readFloat();
            picture = source.readParcelable(getClass().getClassLoader());
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeFloat(degree);
            out.writeParcelable((Parcelable) picture, flags);
        }

        public static final Parcelable.Creator<SavedState> CREATOR
                = new Parcelable.Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }

    @Nullable
    @Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState myState = new SavedState(superState);
        myState.degree = this.degree;
        myState.picture = this.picture;
        return myState;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        SavedState savedState = (SavedState) state;
        super.onRestoreInstanceState(savedState.getSuperState());
        this.degree = savedState.degree;
        this.picture = savedState.picture;
        invalidate();
    }

但启动应用并翻转屏幕时,出现错误:java.lang.ClassCastException: android.graphics.drawable.VectorDrawable cannot be cast to android.os.Parcelable。开发环境为Android API 32、Java 8。

我尝试将Drawable转为Bitmap:

protected Parcelable onSaveInstanceState() {
// do not change the rest of the code
   Bitmap bitmap = source.readParcelable(getClass().getClassLoader());
   picture = new BitmapDrawable(bitmap); 
}

public void writeToParcel(Parcel out, int flags) {
// do not change the rest of the code
   Bitmap bitmap = ((BitmapDrawable) picture).getBitmap();
   out.writeParcelable(bitmap, flags);
} 

但错误依然存在,此时出现Drawable无法转为BitmapDrawable的问题。

我还尝试保存Drawable的ID,但无法在静态上下文使用getResources()方法:

picture = getResources().getDrawable(id)

请问是否有其他保存Drawable对象的可行方法?

可行解决方案

方案一:保存Drawable资源ID(推荐轻量化方案)

既然Drawable来自res/drawable目录,直接保存资源ID是最稳妥的方式,同时解决静态上下文无法调用getResources()的问题:

  1. 修改SavedState类,只保存资源ID和旋转角度:
private static class SavedState extends BaseSavedState {
    int drawableResId;
    float degree = 0f;

    public SavedState(Parcelable superState) {
        super(superState);
    }

    public SavedState(Parcel source) {
        super(source);
        degree = source.readFloat();
        drawableResId = source.readInt();
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        super.writeToParcel(out, flags);
        out.writeFloat(degree);
        out.writeInt(drawableResId);
    }

    public static final Parcelable.Creator<SavedState> CREATOR
            = new Parcelable.Creator<SavedState>() {
        public SavedState createFromParcel(Parcel in) {
            return new SavedState(in);
        }

        public SavedState[] newArray(int size) {
            return new SavedState[size];
        }
    };
}
  1. 重写onSaveInstanceState,保存Drawable对应的资源ID:
@Nullable
@Override
protected Parcelable onSaveInstanceState() {
    Parcelable superState = super.onSaveInstanceState();
    SavedState myState = new SavedState(superState);
    myState.degree = this.degree;
    // 获取VectorDrawable的资源ID
    if (this.picture instanceof VectorDrawable) {
        myState.drawableResId = ((VectorDrawable) this.picture).getConstantState().getResourceId();
    }
    return myState;
}
  1. 重写onRestoreInstanceState,通过View自身的Resources重新加载Drawable:
@Override
protected void onRestoreInstanceState(Parcelable state) {
    SavedState savedState = (SavedState) state;
    super.onRestoreInstanceState(savedState.getSuperState());
    this.degree = savedState.degree;
    // 利用View的上下文加载Drawable,兼容静态环境问题
    this.picture = getResources().getDrawable(savedState.drawableResId, getContext().getTheme());
    invalidate();
}

方案二:将VectorDrawable转为Bitmap保存

如果Drawable是动态修改过的(无法通过资源ID恢复),可以将其转为Bitmap保存,避免类型转换问题:

  1. 修改SavedState类,保存Bitmap对象:
private static class SavedState extends BaseSavedState {
    Bitmap drawableBitmap;
    float degree = 0f;

    public SavedState(Parcelable superState) {
        super(superState);
    }

    public SavedState(Parcel source) {
        super(source);
        degree = source.readFloat();
        drawableBitmap = source.readParcelable(getClass().getClassLoader());
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        super.writeToParcel(out, flags);
        out.writeFloat(degree);
        out.writeParcelable(drawableBitmap, flags);
    }

    public static final Parcelable.Creator<SavedState> CREATOR
            = new Parcelable.Creator<SavedState>() {
        public SavedState createFromParcel(Parcel in) {
            return new SavedState(in);
        }

        public SavedState[] newArray(int size) {
            return new SavedState[size];
        }
    };
}
  1. 实现Drawable转Bitmap的工具方法:
private Bitmap drawableToBitmap(Drawable drawable) {
    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable) drawable).getBitmap();
    }

    // 处理VectorDrawable等非BitmapDrawable类型
    Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);
    return bitmap;
}
  1. 重写状态保存与恢复方法:
@Nullable
@Override
protected Parcelable onSaveInstanceState() {
    Parcelable superState = super.onSaveInstanceState();
    SavedState myState = new SavedState(superState);
    myState.degree = this.degree;
    myState.drawableBitmap = drawableToBitmap(this.picture);
    return myState;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    SavedState savedState = (SavedState) state;
    super.onRestoreInstanceState(savedState.getSuperState());
    this.degree = savedState.degree;
    this.picture = new BitmapDrawable(getResources(), savedState.drawableBitmap);
    invalidate();
}

方案三:直接使用Parcelable兼容(API 29+)

从Android API 29开始,Drawable实现了Parcelable接口,你的环境是API 32可以直接使用,但需注意版本兼容性:

private static class SavedState extends BaseSavedState {
    Drawable picture;
    float degree = 0f;

    public SavedState(Parcelable superState) {
        super(superState);
    }

    public SavedState(Parcel source) {
        super(source);
        degree = source.readFloat();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            picture = source.readParcelable(getClass().getClassLoader(), Drawable.class);
        }
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        super.writeToParcel(out, flags);
        out.writeFloat(degree);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            out.writeParcelable(picture, flags);
        }
    }

    public static final Parcelable.Creator<SavedState> CREATOR
            = new Parcelable.Creator<SavedState>() {
        public SavedState createFromParcel(Parcel in) {
            return new SavedState(in);
        }

        public SavedState[] newArray(int size) {
            return new SavedState[size];
        }
    };
}

此方案仅适用于API 29及以上版本,若需兼容更低版本,优先选择前两个方案。

内容的提问来源于stack exchange,提问作者sh_nadegda

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 05:16:06