从Activity点击切换Fragment画布颜色时旧绘制线条丢失问题求助
看起来你遇到的核心问题是每次切换蜡笔颜色时都会新建一个Fragment,旧Fragment被移除后,它上面的绘制内容也跟着丢失了。毕竟每个Fragment都是独立的视图容器,销毁旧的自然带不走它的画布内容。这里给你几个实用的解决方案,按推荐程度排序:
方案1:复用同一个绘画Fragment,只切换画笔颜色
这是最简洁高效的思路——不要每次换颜色就重建Fragment,而是初始化一次Fragment后,通过方法调用直接更新它内部的画笔颜色。这样旧的绘制内容会一直保存在Fragment的画布上,不会消失。
核心代码示例
首先在宿主Activity中维护绘画Fragment的实例:
private DrawingFragment mDrawingFragment; // 蜡笔颜色点击事件处理 public void onCrayonColorSelected(int newColor) { // 如果Fragment还没创建,就初始化并添加到布局 if (mDrawingFragment == null) { mDrawingFragment = new DrawingFragment(); getSupportFragmentManager().beginTransaction() .add(R.id.your_container_id, mDrawingFragment) .commit(); } // 直接调用Fragment的方法切换画笔颜色 mDrawingFragment.updateBrushColor(newColor); }
然后在DrawingFragment内部实现画笔颜色更新和绘制逻辑:
public class DrawingFragment extends Fragment { private Paint mBrushPaint; private Bitmap mDrawBitmap; private Canvas mDrawCanvas; private DrawView mDrawView; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { mDrawView = new DrawView(requireContext()); // 初始化默认画笔 mBrushPaint = new Paint(); mBrushPaint.setColor(Color.BLUE); // 默认颜色 mBrushPaint.setStrokeWidth(12); mBrushPaint.setStyle(Paint.Style.STROKE); mBrushPaint.setAntiAlias(true); return mDrawView; } // 对外暴露的颜色更新方法 public void updateBrushColor(int newColor) { if (mBrushPaint != null) { mBrushPaint.setColor(newColor); } } // 自定义绘制View,负责处理触摸绘画和画布渲染 private class DrawView extends View { private Path mCurrentPath; public DrawView(Context context) { super(context); mCurrentPath = new Path(); setBackgroundColor(Color.TRANSPARENT); // 保持透明,不遮挡下层的GLSurfaceView } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // 创建和View尺寸一致的Bitmap,用来缓存绘制内容 mDrawBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); mDrawCanvas = new Canvas(mDrawBitmap); } @Override public boolean onTouchEvent(MotionEvent event) { float touchX = event.getX(); float touchY = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mCurrentPath.moveTo(touchX, touchY); break; case MotionEvent.ACTION_MOVE: mCurrentPath.lineTo(touchX, touchY); break; case MotionEvent.ACTION_UP: // 抬手时把当前路径画到缓存Bitmap里 mDrawCanvas.drawPath(mCurrentPath, mBrushPaint); mCurrentPath.reset(); break; } invalidate(); // 触发重绘 return true; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 先绘制缓存的所有历史内容 canvas.drawBitmap(mDrawBitmap, 0, 0, null); // 再绘制当前正在拖动的路径 canvas.drawPath(mCurrentPath, mBrushPaint); } } }
这个方案的优势在于:只创建一次Fragment,所有绘制内容都保存在Fragment内部的Bitmap中,切换颜色只是修改画笔属性,完全不会影响已有的绘制内容。
方案2:全局缓存绘制内容,跨Fragment共享
如果因为某些原因你必须要创建新的Fragment来处理不同颜色(比如业务逻辑限制),那可以在宿主Activity中维护一个全局的Bitmap,用来缓存所有的绘制内容。每次创建新Fragment时,先把这个全局Bitmap画到新Fragment的画布上,再继续绘制新内容;每次Fragment销毁前,把自己的绘制内容合并到全局Bitmap中。
核心思路代码
在Activity中定义全局缓存:
private Bitmap mGlobalDrawCache; // 合并Fragment的绘制内容到全局缓存 public void mergeDrawContent(Bitmap fragmentBitmap) { if (mGlobalDrawCache == null) { mGlobalDrawCache = Bitmap.createBitmap(fragmentBitmap.getWidth(), fragmentBitmap.getHeight(), Bitmap.Config.ARGB_8888); } Canvas cacheCanvas = new Canvas(mGlobalDrawCache); cacheCanvas.drawBitmap(fragmentBitmap, 0, 0, null); } // 获取全局缓存的Bitmap public Bitmap getGlobalDrawCache() { return mGlobalDrawCache; }
然后在Fragment的onDestroyView中调用合并方法,在onDraw中先绘制全局缓存:
// 在Fragment的自定义View的onDraw方法里 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 先画全局缓存的历史内容 Bitmap globalCache = ((YourActivity) requireActivity()).getGlobalDrawCache(); if (globalCache != null) { canvas.drawBitmap(globalCache, 0, 0, null); } // 再画当前的绘制内容 canvas.drawPath(mCurrentPath, mBrushPaint); canvas.drawBitmap(mDrawBitmap, 0, 0, null); } // 在Fragment销毁前合并内容 @Override public void onDestroyView() { super.onDestroyView(); if (mDrawBitmap != null) { ((YourActivity) requireActivity()).mergeDrawContent(mDrawBitmap); } }
这个方案能解决跨Fragment的内容保留问题,但相比方案1会多一些内存开销和合并操作,除非必要不推荐。
方案3:放弃Fragment,直接用自定义View作为绘画层
其实绘画层完全不需要用Fragment来实现——你可以直接在GLSurfaceView的上层添加一个自定义的DrawView(就像方案1里的内部类那样),把它作为固定的绘画层。这样既不用处理Fragment的创建销毁,也能更直接地控制画笔和绘制内容,是性能最优的方案。
只需要在布局文件中把这个自定义View放在GLSurfaceView的上层,设置为透明背景,然后在Activity中直接控制它的画笔颜色即可,逻辑和方案1的绘制部分完全一致。
内容的提问来源于stack exchange,提问作者Zim

