I'm trying to draw a Bitmap on a Surface view like this:
private void draw1() {
if (ourHolder.getSurface().isValid()) {
Log.i("View", "drawing...");
canvas = ourHolder.lockCanvas(null);
canvas.drawColor(Color.GREEN, PorterDuff.Mode.CLEAR);
canvas.drawBitmap(playerShip.getBitmap(), 50, 50, paint);
ourHolder.unlockCanvasAndPost(canvas);
} else {
Log.i("View", "surface is not valid");
}
}
But nothing appears. Just a black screen.
But if i add this:
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.GREEN, PorterDuff.Mode.CLEAR);
super.onDraw(canvas);
}
The bitmap is drawn.
Is it required that i clear the canvas, only from it's onDraw() method?
Also, i'm new to Android. Any advice on the style of writing, is welcome.
I am learning on canvas and bitmap with paths and currently working on a drawing app where user can draw paths freely on the extended view.
The app also allow allowing user to import bitmap as the background and draw on it.
Extending the View named DoodleView:
public DoodleView(Context context, AttributeSet attrs)
{
super(context, attrs); // pass context to View's constructor
this.context_new=context;
setFocusable(true);
setFocusableInTouchMode(true);
} // end DoodleView constructor
onDraw:
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawBitmap(bitmap, 0, 0, null);
for (Path p : paths)
{
paintLine.setColor(colorsMap.get(p));
canvas.drawPath(p, paintLine);
}
paintLine.setColor(selectedColor);
canvas.drawPath(mPath, paintLine);
if (ConvertCanvasToBitmap == true)
{
canvas.drawBitmap(bitmap, 0, 0, paintLine);
ConvertCanvasToBitmap = false;
}
}
FlipHorizontally:
public void flipImageHorizontally()
{
ConvertCanvasToBitmap = true;
invalidate();
Matrix flipHorizontalMatrix = new Matrix();
flipHorizontalMatrix.setScale(-1,1);
flipHorizontalMatrix.postTranslate(bitmap.getWidth(),0);
Bitmap HorizontalFlipped = Bitmap.createBitmap
(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), flipHorizontalMatrix, true);
bitmap = HorizontalFlipped;
invalidate();
}
Question:
My goal is that for the paths that are already drawn, when the user tries to flip the image, the paths drawn would also be flipped. (i.e. the paths become part of the image already, and user are disallows to undo the paths anymore).
However, I have tested using the above codes, when the flip button is pressed, the bitmap background can be flipped, yet the drawn would disappear. And then when further drawn on it, the paths will appear again, but stay unflipped.
In short, how to make the paths to become part of the bitmap when the flip button is pressed?
Thanks!
Edit:
Based on the Android 2.1 View's getDrawingCache() method always returns null, I have modified the onDraw with the following code, but got
02-22 21:38:34.685: E/AndroidRuntime(18617): java.lang.NullPointerException
02-22 21:38:34.685: E/AndroidRuntime(18617): at android.graphics.Bitmap.createBitmap(Bitmap.java:455)
02-22 21:38:34.685: E/AndroidRuntime(18617): at com.pearmak.drawing.DoodleView.onDraw(DoodleView.java:148)
Modified code:
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawBitmap(bitmap, 0, 0, null); // draw the background screen
for (Path p : paths)
{
paintLine.setColor(colorsMap.get(p));
paintLine.setStrokeWidth(widthMap.get(p));
canvas.drawPath(p, paintLine);
}
paintLine.setColor(selectedColor);
paintLine.setStrokeWidth(selectedWidth);
canvas.drawPath(mPath, paintLine);
if (ConvertCanvasToBitmap == true)
{
//Method 1
// RelativeLayout page = (RelativeLayout) findViewById(R.id.doodleView);
// Bitmap screenshot = Bitmap.createBitmap(page.getWidth(), page.getHeight(), Config.ARGB_8888);
// bitmap = screenshot;
// ConvertCanvasToBitmap = false;
//Method 2
Bitmap screenshot2;
layout(0, 0, DoodlzViewWidth, DoodlzViewHeight);
setDrawingCacheEnabled(true);
screenshot2 = Bitmap.createBitmap(getDrawingCache()); // LINE 148
setDrawingCacheEnabled(false);
bitmap = screenshot2;
}
}
you need to create the bitmap from that view
please refer to drawingcache like here
Android 2.1 View's getDrawingCache() method always returns null
now try to flip this bitmap
I'm trying to draw to a Bitmap so I can put my custom view inside an imageView.The code within the onDraw method is:
public void onDraw(Canvas canvas) {
Bitmap drawGraph = Bitmap.createBitmap(canvas.getWidth(),canvas.getHeight(),Bitmap.Config.ARGB_8888);
canvas.setBitmap(drawGraph);
canvas.drawBitmap(drawGraph, 0, 0, bgPaint);
My problem is that if I try to use a Bitmap in this way, I just get a black screen. I know that the rest of my code works as it displays if I don't try to draw to a bitmap.
If I comment out the line
canvas.setBitmap(drawGraph);
Then everything works perfectly, so this is the problem but I dont know why.
where am I going wrong?
Turns out I did have to create a second canvas. My working code is below just for anyone who might need it:
public void onDraw(Canvas canvas) {
Canvas singleUseCanvas = new Canvas();
drawGraph = Bitmap.createBitmap(canvas.getWidth(),canvas.getHeight(),Bitmap.Config.ARGB_8888);
singleUseCanvas.setBitmap(drawGraph);
canvas.drawBitmap(drawGraph, 100, 100, bgPaint);
I think is the canvas and canvas2 dichotomy. Try to use only canvas2 (the parameter) to draw.
AFAIK The most efficient way is to override drawable setters.
#Override
public void setImageBitmap(Bitmap bm) {
bmp = bm;
}
#Override
public void setImageDrawable(Drawable drawable) {
try {
bmp = ((BitmapDrawable) drawable).getBitmap();
} catch (Exception e){
log(e.toString());
}
}
I'm trying to figure out the best way to get the pixel color value at a given point on a View. There are three ways that I write to the View:
I set a background image with View.setBackgroundDrawable(...).
I write text, draw lines, etc., with Canvas.drawText(...), Canvas.drawLine(...), etc., to a Bitmap-backed Canvas.
I draw child objects (sprites) by having them write to the Canvas passed to the View's onDraw(Canvas canvas) method.
Here is the onDraw() method from my class that extends View:
#Override
public void onDraw(Canvas canvas) {
// 1. Redraw the background image.
super.onDraw(canvas);
// 2. Redraw any text, lines, etc.
canvas.drawBitmap(bitmap, 0, 0, null);
// 3. Redraw the sprites.
for (Sprite sprite : sprites) {
sprite.onDraw(canvas);
}
}
What would be the best way to get a pixel's color value that would take into account all of those sources?
How about load the view to a bitmap (at some point after all your drawing/sprites etc is done), then get the pixel color from the bitmap?
public static Bitmap loadBitmapFromView(View v) {
Bitmap b = Bitmap.createBitmap( v.getLayoutParams().width, v.getLayoutParams().height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
v.layout(0, 0, v.getLayoutParams().width, v.getLayoutParams().height);
v.draw(c);
return b;
}
then use getPixel(x,y) on the result?
http://developer.android.com/reference/android/graphics/Bitmap.html#getPixel%28int,%20int%29
In order to make a simple game, I used a template that draws a canvas with bitmaps like this:
private void doDraw(Canvas canvas) {
for (int i=0;i<8;i++)
for (int j=0;j<9;j++)
for (int k=0;k<7;k++) {
canvas.drawBitmap(mBits[allBits[i][j][k]], i*50 -k*7, j*50 -k*7, null); } }
(The canvas is defined in "run()" / the SurfaceView lives in a GameThread.)
My first question is how do I clear (or redraw) the whole canvas for a new layout?
Second, how can I update just a part of the screen?
// This is the routine that calls "doDraw":
public void run() {
while (mRun) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
if (mMode == STATE_RUNNING)
updateGame();
doDraw(c); }
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c); } } } }
Draw transparent color with PorterDuff clear mode does the trick for what I wanted.
Canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
How do I clear (or redraw) the WHOLE canvas for a new layout (= try at the game) ?
Just call Canvas.drawColor(Color.BLACK), or whatever color you want to clear your Canvas with.
And: how can I update just a part of the screen ?
There is no such method that just update a "part of the screen" since Android OS is redrawing every pixel when updating the screen. But, when you're not clearing old drawings on your Canvas, the old drawings are still on the surface and that is probably one way to "update just a part" of the screen.
So, if you want to "update a part of the screen", just avoid calling Canvas.drawColor() method.
Found this in google groups and this worked for me..
Paint clearPaint = new Paint();
clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawRect(0, 0, width, height, clearPaint);
This removes drawings rectangles etc. while keeping set bitmap..
use the reset method of Path class
Path.reset();
I tried the answer of #mobistry:
canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
But it doesn't worked for me.
The solution, for me, was:
canvas.drawColor(Color.TRANSPARENT, Mode.MULTIPLY);
Maybe some one has the same problem.
mBitmap.eraseColor(Color.TRANSPARENT);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawColor(Color.TRANSPARENT, Mode.MULTIPLY);
please paste below code on surfaceview extend class constructor.............
constructor coding
SurfaceHolder holder = getHolder();
holder.addCallback(this);
SurfaceView sur = (SurfaceView)findViewById(R.id.surfaceview);
sur.setZOrderOnTop(true); // necessary
holder = sur.getHolder();
holder.setFormat(PixelFormat.TRANSPARENT);
xml coding
<com.welcome.panelview.PanelViewWelcomeScreen
android:id="#+id/one"
android:layout_width="600px"
android:layout_height="312px"
android:layout_gravity="center"
android:layout_marginTop="10px"
android:background="#drawable/welcome" />
try above code...
Here is the code of a minimal example showing that you always have to redraw every pixel of the Canvas at each frame.
This activity draw a new Bitmap every second on the SurfaceView, without clearing the screen before.
If you test it, you will see that the bitmap is not always written to the same buffer, and the screen will alternate between the two buffers.
I tested it on my phone (Nexus S, Android 2.3.3), and on the emulator (Android 2.2).
public class TestCanvas extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new TestView(this));
}
}
class TestView extends SurfaceView implements SurfaceHolder.Callback {
private TestThread mThread;
private int mWidth;
private int mHeight;
private Bitmap mBitmap;
private SurfaceHolder mSurfaceHolder;
public TestView(Context context) {
super(context);
mThread = new TestThread();
mBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon);
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
mWidth = width;
mHeight = height;
mThread.start();
}
#Override
public void surfaceCreated(SurfaceHolder holder) {/* Do nothing */}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mThread != null && mThread.isAlive())
mThread.interrupt();
}
class TestThread extends Thread {
#Override
public void run() {
while (!isInterrupted()) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
c.drawBitmap(mBitmap, (int) (Math.random() * mWidth), (int) (Math.random() * mHeight), null);
}
} finally {
if (c != null)
mSurfaceHolder.unlockCanvasAndPost(c);
}
try {
sleep(1000);
} catch (InterruptedException e) {
interrupt();
}
}
}
}
}
For me calling Canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) or something similar would only work after I touch the screen. SO I would call the above line of code but the screen would only clear after I then touched the screen. So what worked for me was to call invalidate() followed by init() which is called at the time of creation to initialize the view.
private void init() {
setFocusable(true);
setFocusableInTouchMode(true);
setOnTouchListener(this);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(6);
mCanvas = new Canvas();
mPaths = new LinkedList<>();
addNewPath();
}
Erasing on Canvas in java android is similar erasing HTML Canvas via javascript with globalCompositeOperation. The logic was similar.
U will choose DST_OUT (Destination Out) logic.
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
Note: DST_OUT is more useful because it can erase 50% if the paint color have 50% alpha. So, to clear completely to transparent, the alpha of color must be 100%. Apply paint.setColor(Color.WHITE) is recommended. And make sure that the canvas image format was RGBA_8888.
After erased, go back to normal drawing with SRC_OVER (Source Over).
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
Update small area display literally will need to access graphic hardware, and it maybe not supported.
The most close for highest performance is using multi image layer.
With the following approach, you can clear the whole canvas or just a part of it.
Please do not forget to disable Hardware acceleration since PorterDuff.Mode.CLEAR doesn’t work with hardware acceleration and finally call setWillNotDraw(false) because we override the onDraw method.
//view's constructor
setWillNotDraw(false);
setLayerType(LAYER_TYPE_SOFTWARE, null);
//view's onDraw
Paint TransparentPaint = new Paint();
TransparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawRect(0, 0, width, height, TransparentPaint);
Don't forget to call invalidate();
canvas.drawColor(backgroundColor);
invalidate();
path.reset();
I found my solution.
PaintView class:
public void clear() {
mPath.reset();
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
paths.clear();
}
And MainActivity:
clear_canvas_.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
paintView.clear();
}
});
Your first requirement, how to clear or redraw whole canvas - Answer - use canvas.drawColor(color.Black) method for clearing the screen with a color of black or whatever you specify .
Your second requirement, how to update part of the screen - Answer - for example if you want to keep all other things unchanged on the screen but in a small area of screen to show an integer(say counter) which increases after every five seconds. then use canvas.drawrect method to draw that small area by specifying left top right bottom and paint. then compute your counter value(using postdalayed for 5 seconds etc., llike Handler.postDelayed(Runnable_Object, 5000);) , convert it to text string, compute the x and y coordinate in this small rect and use text view to display the changing counter value.
Try to remove the view at onPause() of an activity and add onRestart()
LayoutYouAddedYourView.addView(YourCustomView);
LayoutYouAddedYourView.removeView(YourCustomView);
The moment you add your view, onDraw() method would get called.
YourCustomView, is a class which extends the View class.
In my case, I draw my canvas into linearlayout.
To clean and redraw again:
LinearLayout linearLayout = findViewById(R.id.myCanvas);
linearLayout.removeAllViews();
and then, I call the class with the new values:
Lienzo fondo = new Lienzo(this,items);
linearLayout.addView(fondo);
This is the class Lienzo:
class Lienzo extends View {
Paint paint;
RectF contenedor;
Path path;
ArrayList<Items>elementos;
public Lienzo(Context context,ArrayList<Items> elementos) {
super(context);
this.elementos=elementos;
init();
}
private void init() {
path=new Path();
paint = new Paint();
contenedor = new RectF();
paint.setStyle(Paint.Style.FILL);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
contenedor.left = oneValue;
contenedor.top = anotherValue;
contenedor.right = anotherValue;
contenedor.bottom = anotherValue;
float angulo = -90; //starts drawing at 12 o'clock
//total= sum of all element values
for (int i=0;i<elementos.size();i++){
if (elementos.get(i).angulo!=0 && elementos.get(i).visible){
paint.setColor(elementos.get(i).backColor);
canvas.drawArc(contenedor,angulo,(float)(elementos.get(i).value*360)/total,true,paint);
angulo+=(float)(elementos.get(i).value*360)/total;
}
} //for example
}
}
In my case, creating canvas every time worked for me, even though it's not memory-friendly
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.image);
imageBitmap = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
canvas = new Canvas(imageBitmap);
canvas.drawBitmap(bm, 0, 0, null);
I had to use a separate drawing pass to clear the canvas (lock, draw and unlock):
Canvas canvas = null;
try {
canvas = holder.lockCanvas();
if (canvas == null) {
// exit drawing thread
break;
}
canvas.drawColor(colorToClearFromCanvas, PorterDuff.Mode.CLEAR);
} finally {
if (canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
The following worked for me:
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SCREEN);
Just call
canvas.drawColor(Color.TRANSPARENT)