Reputation: 703
I have a canvas with a background image. I need to know if it's possible to clear the paint from this canvas for redraw without clearing its background image. Here is my example and my results so far.
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>
So the code above loads an image into my Canvas
. Then each time I touch the Canvas
it draws a circle and eventually a path between the circles. However when I start adding more circles and paths, it's redrawing over itself, which looks terrible.
Now I can clear the paint from my canvas with this line.
mCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
However I also lose the Bitmap
I set as it's background, and now my background is black. So, how do I keep my background image but clear my paint? Is this possible or do I have to use a work around such as two Canvas
's on top of each other?
I have already tried the following, and several variations of the same.
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);
Any help is appreciated
Upvotes: 4
Views: 4247
Reputation: 703
I finally figured it out. I was able to use two ImageViews
stacked in a RelativeLayout
. Stack the ImageViews
on top of each other and then set both ImageViews
with the same picture but separate Bitmaps
. The top one now acts as your foreground and your bottom acts as your background. Now you can draw on the top one, while clearing the background and never effecting the bottom one. The key is the attribute yourImageView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
which turns off hardware acceleration and allows your background to actually be rendered transparent. Without this attribute applied to your view you will have a black or white background that completely covers your bottom ImageView
which makes it completely useless.
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
}
}
Hopefully this saves someone else a ton of time.
Upvotes: 2
Reputation: 466
Just if you aren't using imageView's there's another, even simplier solution:
I've got myself a new Bitmap(mBackgroundBitmap); new Canvas(mBackgorundCanvas), asigned mBackgroundBitmap to mBackgroundCanvas, and on every onDraw method firstly i dar my background bitmap:
canvas.drawBitmap(mBackgroundBitmap, offSetX, offSetY, mBitmapPaint);
and just then I start drawing my actual bitmap:
canvas.drawBitmap(mBitmap, offSetX, offSetY, mBitmapPaint);
I guess it is not memory-efficient solution (I mean having two bitmaps isnt very cool or pleasant, but then many of us doesnt use A big images, but instead (at least me) create my background from a fragment and then Tile it in X and Y directions.
If my answer is ridiculous don't judge me, I am just a rookie.
Upvotes: 1
Reputation: 8866
As far as I know, Canvas doesn't include layers. So you can't remove one layer (paint) while leaving the other (background). I believe you should be using either two canvases, or even the paint canvas and an ImageView to hold the background. Then when you want to export the image combine the canvas contents with the background.
Upvotes: 3