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

Kotlin绘图应用切换Fragment后绘图消失问题求助

解决Android绘图Fragment切换后内容丢失的问题

Fragment切换时绘图内容消失,核心原因是Fragment被销毁重建,导致自定义绘图View的临时绘图数据丢失。以下是几种可靠的解决方案:

1. 用ViewModel持久化绘图数据

ViewModel的生命周期与宿主Activity绑定,不会随Fragment的销毁重建而消失,适合存储跨页面的绘图状态:

  • 定义ViewModel类存储绘图数据:

    class DrawingViewModel : ViewModel() {
        // 存储路径与对应画笔的配对数据
        val drawingHistory = mutableListOf<Pair<Path, Paint>>()
        // 可额外存储当前画笔颜色、宽度等状态
        var currentPaintColor = Color.BLACK
        var currentStrokeWidth = 5f
    }
    
  • 在绘图Fragment中获取ViewModel,每次绘制完成后同步数据:

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val viewModel = ViewModelProvider(this)[DrawingViewModel::class.java]
        val drawingView = view.findViewById<DrawingView>(R.id.drawing_view)
    
        // 页面重建时恢复绘图数据
        drawingView.restoreDrawing(viewModel.drawingHistory)
    
        // 监听绘图动作,实时更新ViewModel
        drawingView.onPathDrawn = { path, paint ->
            viewModel.drawingHistory.add(path to paint)
        }
    }
    

2. 让自定义绘图View支持状态保存

重写View的onSaveInstanceState和onRestoreInstanceState方法,直接在View层保存绘图状态(注意:Path和Paint无法直接序列化,需提取关键参数封装):

  • 先定义Parcelable类存储绘图关键信息:

    data class DrawingData(
        val pathPoints: List<PointF>,
        val color: Int,
        val strokeWidth: Float
    ) : Parcelable {
        constructor(parcel: Parcel) : this(
            parcel.createTypedArrayList(PointF.CREATOR)!!,
            parcel.readInt(),
            parcel.readFloat()
        )
    
        override fun writeToParcel(parcel: Parcel, flags: Int) {
            parcel.writeTypedList(pathPoints)
            parcel.writeInt(color)
            parcel.writeFloat(strokeWidth)
        }
    
        override fun describeContents(): Int {
            return 0
        }
    
        companion object CREATOR : Parcelable.Creator<DrawingData> {
            override fun createFromParcel(parcel: Parcel): DrawingData {
                return DrawingData(parcel)
            }
    
            override fun newArray(size: Int): Array<DrawingData?> {
                return arrayOfNulls(size)
            }
        }
    
        // 将数据转回Path和Paint
        fun toPathPaintPair(): Pair<Path, Paint> {
            val path = Path().apply {
                pathPoints.forEachIndexed { index, point ->
                    if (index == 0) moveTo(point.x, point.y)
                    else lineTo(point.x, point.y)
                }
            }
            val paint = Paint().apply {
                color = this@DrawingData.color
                strokeWidth = this@DrawingData.strokeWidth
                style = Paint.Style.STROKE
                isAntiAlias = true
            }
            return path to paint
        }
    }
    
  • 自定义绘图View重写状态保存方法:

    class DrawingView @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
    ) : View(context, attrs, defStyleAttr) {
        private val drawingPairs = mutableListOf<Pair<Path, Paint>>()
        var onPathDrawn: ((Path, Paint) -> Unit)? = null
    
        // 绘制逻辑(省略)
        override fun onDraw(canvas: Canvas) {
            super.onDraw(canvas)
            drawingPairs.forEach { (path, paint) -> canvas.drawPath(path, paint) }
        }
    
        // 恢复绘图数据
        fun restoreDrawing(pairs: List<Pair<Path, Paint>>) {
            drawingPairs.clear()
            drawingPairs.addAll(pairs)
            invalidate()
        }
    
        override fun onSaveInstanceState(): Parcelable? {
            val superState = super.onSaveInstanceState()
            val bundle = Bundle()
            // 将绘图数据转为DrawingData列表存入Bundle
            val dataList = drawingPairs.map { (path, paint) ->
                val points = mutableListOf<PointF>()
                // 提取Path中的点(这里简化处理,实际可通过PathMeasure遍历)
                DrawingData(points, paint.color, paint.strokeWidth)
            }
            bundle.putParcelableArrayList("drawing_data", ArrayList(dataList))
            return SavedState(superState, bundle)
        }
    
        override fun onRestoreInstanceState(state: Parcelable?) {
            var localState = state
            if (localState is SavedState) {
                val savedData = localState.bundle.getParcelableArrayList<DrawingData>("drawing_data")
                savedData?.let { list ->
                    drawingPairs.clear()
                    drawingPairs.addAll(list.map { it.toPathPaintPair() })
                }
                localState = localState.superState
            }
            super.onRestoreInstanceState(localState)
            invalidate()
        }
    
        private class SavedState(superState: Parcelable?, val bundle: Bundle) : BaseSavedState(superState) {
            constructor(source: Parcel) : this(source.readParcelable(null), source.readBundle(javaClass.classLoader)!!)
    
            override fun writeToParcel(out: Parcel, flags: Int) {
                super.writeToParcel(out, flags)
                out.writeBundle(bundle)
            }
    
            companion object {
                @JvmField
                val CREATOR: Parcelable.Creator<SavedState> = object : Parcelable.Creator<SavedState> {
                    override fun createFromParcel(source: Parcel): SavedState = SavedState(source)
                    override fun newArray(size: Int): Array<SavedState?> = arrayOfNulls(size)
                }
            }
        }
    }
    

3. 切换Fragment时避免销毁实例

如果你的页面切换不需要频繁重建Fragment,可以用hide()/show()替代replace(),保留Fragment实例:

// 切换Fragment的代码示例
val drawingFragment = supportFragmentManager.findFragmentByTag("DrawingFragment") ?: DrawingFragment()
val otherFragment = supportFragmentManager.findFragmentByTag("OtherFragment") ?: OtherFragment()

supportFragmentManager.beginTransaction().apply {
    if (!drawingFragment.isAdded) add(R.id.container, drawingFragment, "DrawingFragment")
    if (!otherFragment.isAdded) add(R.id.container, otherFragment, "OtherFragment")
    
    show(drawingFragment)
    hide(otherFragment)
    addToBackStack(null)
}.commit()

这种方式无需额外保存数据,但会占用更多内存,适合页面数量少的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 16:20:01