在 Android 上,如何清除画布上的颜色而不清除背景图片?

4
我需要在一个带有背景图片的画布上清除绘制,以便进行重新绘制。请问是否可能仅清除绘制而不清除背景图片?以下是我的示例和目前的结果。
JAVA
public void setCanvas() {
    if(mFile != null && mFile.exists()) {
        mPictureBitmap = BitmapFactory.decodeFile(mFile.getAbsolutePath());
        mBitmap = Bitmap.createScaledBitmap(mPictureBitmap, mImageView.getWidth(), mImageView.getHeight(), false);
        mBitmap = mBitmap.copy(Bitmap.Config.ARGB_8888, true);
        mCanvas = new Canvas(mBitmap);

        mImageView.setImageBitmap(mBitmap);
        mImageView.setOnTouchListener(this);
    }
}

private void draw() {
    mCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); // THIS LINE CLEARS

    int lastIndex = mCordHashMap.size() - 1;
    int index = 0;

    for (LinkedHashMap.Entry<String, ArrayList<Circle>> entry : mCordHashMap.entrySet()) {
        ArrayList<Circle> coords = new ArrayList<Circle>();
        coords = entry.getValue();
        Path path = new Path();
        String key = entry.getKey();
        String surface = getSurfaceFromKey(key);
        changePaint(surface);

        if (coords.size() < 3) {
            for (Circle c : coords) {
                mCanvas.drawCircle(c.getmX(), c.getmY(), RADIUS, mCirclePaint);
            }
        } else {
            for (Circle c : coords) {
                if (c == coords.get(0)) {
                    path.moveTo(c.getmX(), c.getmY());
                } else {
                    path.lineTo(c.getmX(), c.getmY());
                }
            }

            path.close();
            mCanvas.drawPath(path, mPaint);
            mCanvas.drawPath(path, mStrokePaint);

        }

        if (index == lastIndex && !mSpecificPathSelected) {
            for (Circle c : coords) {
                mCanvas.drawCircle(c.getmX(), c.getmY(), RADIUS, mCirclePaint);
                mCanvas.drawCircle(c.getmX(), c.getmY(), RADIUS, mCircleStrokePaint);
            }
        }
        mImageView.invalidate();
        index++;
    }
}

XML

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <ExpandableListView
        android:id="@+id/surfaceList"
        android:background="@color/light_grey"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight=".20" />

    <ImageView
        android:id="@+id/drawContainer"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight=".80" 
        android:contentDescription="@string/app_name"/>

</LinearLayout>

上面的代码将一张图片加载到我的Canvas中。然后每次触摸Canvas时,它会绘制一个圆形,最终在圆之间绘制路径。但是当我开始添加更多的圆和路径时,它会重复绘制,看起来很糟糕。

现在,我可以使用这行代码清除画布上的绘画。

mCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);

然而,我也失去了设置为背景的位图,并且现在我的背景是黑色的。那么,我如何保留背景图片但清除绘画?这是可能的吗,还是我必须使用一些变通方法,例如在彼此之上的两个画布?我已经尝试了以下操作,以及几种类似的操作。
1) mCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);

2) mCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
   mCanvas.drawBitmap(mBitmap, 0, 0, mPaint);

3) mBitmap.eraseColor(Color.TRANSPARENT);
   mCanvas.drawBitmap(mBitmap, 0, 0, mPaint);

非常感谢你提供的帮助


答案:https://dev59.com/6lfUa4cB1Zd3GeqPEyCA#24889558 - yajnesh
3个回答

3
据我所知,Canvas不包含图层。因此,在保留其他图层(背景)的同时,您无法删除其中一个图层(绘画)。我认为您应该使用两个画布,甚至是绘画画布和ImageView来保存背景。然后在导出图像时,将画布内容与背景组合在一起。

有道理。如果你仔细看,我实际上是在使用一个 ImageView 并将位图设置为其 src 内容,但显然这对我没有任何好处。 “我认为你应该使用两个画布,或者甚至是绘画画布和一个 ImageView 来保存背景。然后当你想要导出图像时,将画布内容与背景组合在一起。” 如果您有时间,请提供其中任何一个的示例。 - Jawascript
好的,我一直在尝试寻找一个问题的解决方案。你有没有看到过有人成功地将ImageView用作背景,或者同时使用两个画布的链接? - Jawascript
抱歉,我在工作中。我只是在等待服务器部署时浏览了一下SO。今晚我会更深入地研究这个问题。 - Coeffect
谢谢,这个问题困扰了我很久。无论我尝试使用多个画布、图像视图、PorterDuff绘图模式,都会导致黑色画布背景覆盖我的ImageView并遮挡图像。 - Jawascript
今天又回来了,如果在我之前找到了什么,请告诉我。我正在研究一个自定义 ImageView,并覆盖 onDraw 方法,看看是否有帮助。 - Jawascript
想通了,看看我的答案。因为你指导了我正确的方向,所以我会给你点头示意的。祝你有美好的一天。 - Jawascript

2
我终于弄清楚了。我能够在RelativeLayout中叠放两个ImageViews。将两个ImageViews叠放在一起,然后使用不同的Bitmaps设置两个ImageViews相同的图片。上面的一个现在作为前景,底部作为背景。现在你可以在顶部图像上进行绘制,同时清除背景而不会影响底部图像。关键是属性yourImageView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);,它关闭硬件加速并允许您的背景实际上呈现透明。如果没有将此属性应用于您的视图,则会有黑色或白色的背景完全覆盖底部的ImageView,使其完全无用。 XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <ExpandableListView
        android:id="@+id/surfaceList"
        android:background="@color/light_grey"
        android:layout_width="250dp"
        android:layout_height="match_parent" />

    <ImageView
        android:id="@+id/background"
        android:layout_width="match_parent"
        android:layout_toRightOf="@+id/surfaceList"
        android:layout_height="match_parent"
        android:contentDescription="@string/app_name"/>

    <ImageView
        android:id="@+id/foreground"
        android:layout_width="match_parent"
        android:layout_toRightOf="@+id/surfaceList"
        android:layout_height="match_parent"
        android:contentDescription="@string/app_name"/>

</RelativeLayout>

JAVA

public void setCanvas() {
    if(mFile != null && mFile.exists()) { // check for null
        mFirstBitmap = BitmapFactory.decodeFile(mFile.getAbsolutePath()); // get bitmap from directory
        mSecondBitmap = Bitmap.createScaledBitmap(mPictureBitmap, mImageView.getWidth(), // size bitmap mImageView.getHeight(), false);
        mSecondBitmap = mSecondBitmap .copy(Bitmap.Config.ARGB_8888, true); // make bitmap mutable so it can be applied to the canvas
        mFirstBitmap = mSecondBitmap .copy(Bitmap.Config.ARGB_8888, true); // make second bitmap from copy, also mutable
        mCanvas = new Canvas(mSecondBitmap ); //create your canvas from 2nd bitmap
        mImageView.setImageBitmap(mSecondBitmap ); // set this imageview to 2nd bitmap
        mImageView2.setImageBitmap(mFirstBitmap); //set this imageview to 1st bitmap

        mImageView.setOnTouchListener(this); // set on touch listener
        mImageView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); // IMPORTANT set hardware acceleration off with this line. Otherwise your view's background will NOT be transparent
        mImageView.bringToFront(); // bring this imageview to the front, making it your foreground
    }
}

希望这篇文章能为其他人节省大量时间。

1

如果您没有使用imageView,还有另一种更简单的解决方案:

我创建了一个新的Bitmap(mBackgroundBitmap)和一个新的Canvas(mBackgorundCanvas),将mBackgroundBitmap分配给mBackgroundCanvas,并在每个onDraw方法中首先绘制我的背景位图:

canvas.drawBitmap(mBackgroundBitmap, offSetX, offSetY, mBitmapPaint);

而就在那时,我开始绘制我的实际位图:
canvas.drawBitmap(mBitmap, offSetX, offSetY, mBitmapPaint);

我猜这不是一个内存高效的解决方案(我的意思是拥有两个位图并不很酷或令人愉悦,但是我们中的许多人不使用大图片,而是(至少我)从片段中创建背景,然后在X和Y方向上平铺它。如果我的回答很荒谬,请不要评判我,我只是一个新手。

我喜欢你的回答,虽然从内存角度来看可能不是很高效,但它很简单明了。 - Jawascript

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接