Android自定义View:如何让Drawable在屏幕旋转后保留?
这确实是Android自定义View开发里挺头疼的一个问题——配置变更(比如屏幕旋转)时,Activity重建导致自定义View的Drawable实例没法直接保存,毕竟通用Drawable既不实现Parcelable也不支持Serializable。我来分享几个实战中验证过的解决方案:
核心思路是区分用户设置Drawable的方式:如果是通过资源ID设置的,就直接保存ID;如果是传入的Drawable实例,就把它转换成可Parcelable的Bitmap来保存。
具体实现步骤:
在自定义View中维护两个状态变量:
private int mDrawableResId = -1; // 标记资源ID,-1表示未设置 private Bitmap mDrawableBitmap; // 保存Drawable转成的Bitmap提供两个重载的设置Drawable方法,分别处理资源ID和实例:
public void setDrawable(int resId) { mDrawableResId = resId; mDrawableBitmap = null; updateViewDrawable(); } public void setDrawable(Drawable drawable) { mDrawableResId = -1; if (drawable != null) { mDrawableBitmap = drawableToBitmap(drawable); } else { mDrawableBitmap = null; } updateViewDrawable(); }实现Drawable转Bitmap的工具方法:
private Bitmap drawableToBitmap(Drawable drawable) { // 如果是BitmapDrawable直接取Bitmap if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } // 其他Drawable类型,创建对应尺寸的Bitmap并绘制 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; }重写状态保存与恢复方法:
@Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); // 先保存父类的状态 bundle.putParcelable("super_state", super.onSaveInstanceState()); bundle.putInt("drawable_res_id", mDrawableResId); bundle.putParcelable("drawable_bitmap", mDrawableBitmap); return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; mDrawableResId = bundle.getInt("drawable_res_id"); mDrawableBitmap = bundle.getParcelable("drawable_bitmap"); // 恢复父类状态 super.onRestoreInstanceState(bundle.getParcelable("super_state")); updateViewDrawable(); return; } super.onRestoreInstanceState(state); }实现更新View Drawable的方法:
private void updateViewDrawable() { Drawable targetDrawable = null; if (mDrawableResId != -1) { targetDrawable = getContext().getResources().getDrawable(mDrawableResId); } else if (mDrawableBitmap != null) { targetDrawable = new BitmapDrawable(getContext().getResources(), mDrawableBitmap); } // 根据你的View需求设置Drawable,比如作为背景或前景 setBackground(targetDrawable); }
注意:这个方法对VectorDrawable也适用,但要注意低版本API(<21)的兼容处理;如果Drawable尺寸很大,保存Bitmap可能会占用较多内存,需要根据场景权衡。
ViewModel的生命周期不受配置变更影响,所以可以把Drawable实例托管给ViewModel,避免随Activity重建丢失。
具体实现步骤:
创建一个自定义ViewModel类:
public class CustomViewViewModel extends ViewModel { // 用弱引用避免内存泄漏 private WeakReference<Drawable> mDrawableRef; public void setDrawable(Drawable drawable) { mDrawableRef = new WeakReference<>(drawable); } public Drawable getDrawable() { return mDrawableRef != null ? mDrawableRef.get() : null; } }在Activity/Fragment中绑定ViewModel,并同步Drawable状态:
// 获取ViewModel CustomViewViewModel viewModel = new ViewModelProvider(this).get(CustomViewViewModel.class); // 设置Drawable时同步到ViewModel Drawable myCustomDrawable = ...; myCustomView.setDrawable(myCustomDrawable); viewModel.setDrawable(myCustomDrawable);在自定义View中恢复Drawable:
@Override protected void onFinishInflate() { super.onFinishInflate(); // 从ViewModel中获取Drawable(要求Context是ViewModelStoreOwner,比如Activity/Fragment) CustomViewViewModel viewModel = new ViewModelProvider((ViewModelStoreOwner) getContext()) .get(CustomViewViewModel.class); Drawable savedDrawable = viewModel.getDrawable(); if (savedDrawable != null) { setDrawable(savedDrawable); } }
注意:这个方法仅适用于View所在的Context是ViewModelStoreOwner的场景(比如Activity或Fragment);用弱引用是为了避免Drawable持有Context导致内存泄漏。
如果你使用AndroidX,可以结合SavedStateHandle来保存状态,它既拥有ViewModel的生命周期优势,又能在进程被杀死后恢复状态,同样需要把Drawable转成Bitmap保存。
具体实现步骤:
创建带SavedStateHandle的ViewModel:
public class CustomViewSavedStateViewModel extends ViewModel { private final SavedStateHandle mSavedStateHandle; private static final String KEY_RES_ID = "drawable_res_id"; private static final String KEY_BITMAP = "drawable_bitmap"; public CustomViewSavedStateViewModel(SavedStateHandle savedStateHandle) { mSavedStateHandle = savedStateHandle; } public void saveDrawableState(int resId, Bitmap bitmap) { mSavedStateHandle.set(KEY_RES_ID, resId); mSavedStateHandle.set(KEY_BITMAP, bitmap); } public int getDrawableResId() { return mSavedStateHandle.get(KEY_RES_ID, -1); } public Bitmap getDrawableBitmap() { return mSavedStateHandle.get(KEY_BITMAP); } }在自定义View中使用这个ViewModel来保存和恢复状态,逻辑和方案一类似,只是把状态存储从View的Bundle转移到
SavedStateHandle中即可。
- 如果你的View大多通过资源ID设置Drawable,优先选方案一,简单直接兼容性好;
- 如果View和Activity/Fragment强绑定,方案二更优雅,避免Bitmap转换的内存开销;
- 追求现代Android开发最佳实践,方案三是AndroidX推荐的方式,兼顾生命周期和持久化能力。
内容的提问来源于stack exchange,提问作者Roberto Leinardi

