I am fairly new to Android, but why isn't my image showing on the canvas?
I know that it is working properly because the background color is black, which I changed in the same same method, onDraw . Can anyone help me? Thanks in advance!
public PongView(Context context) {
super(context);
paddle1 = BitmapFactory.decodeResource(getResources(), R.drawable.pongpaddle);
paddle2 = BitmapFactory.decodeResource(getResources(), R.drawable.pongpaddle);
}
protected void onDraw(Canvas canvas) {
xp1 = canvas.getWidth()/2;
xp2 = canvas.getWidth()/2;
yp1 = 25;
yp2 = 760;
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(paddle1, xp1,yp1, null);
canvas.drawBitmap(paddle2,xp2,yp2, null);
Paint white = new Paint();
white.setColor(Color.WHITE);
canvas.drawText("Score P1:"+ p1Score +" P2: " + p2Score , 700, 20,white );
}
Based on your comment above, I think what's happening is, android run time is drawing to the canvas, and your onDraw is not being called. You can avoid this by calling this.setWillNotDraw(false) in your class' constructor. Once you clear this flag, your onDraw() will be called.
Source: Android developer docs says if you override View's onDraw(), you have to clear this flag. Check setWillNotDraw
Related
I'm still searching for the right way to draw on canvas multiple times.
Wished result: ImageView set to visible, show first text, then second instead of first, finally third instead of second. Each text is visible for half a second
Idea: paint canvas with setColor(Color.WHITE) after each text, use synchronized block for setting backgroundcolor and text, use invalidate() with custom draw() method
I tryed different ways, this is the current version of my code:
initializeStimulus() - called in onCreate() of my Service
private void initializeStimulus(String stimulus) {
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
stimulusOverlay = new CustomImageView(this);
stimulusOverlay.setBackgroundColor(getResources().getColor(R.color.background_stimulus));
stimulusOverlay.setVisibility(View.VISIBLE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
);
// Position of stimuli in relation to screen size / 2
Point displaySize = new Point();
windowManager.getDefaultDisplay().getRealSize(displaySize);
int width = displaySize.x;
int height = displaySize.y;
params.x = 0;
params.y = 0;
windowManager.addView(stimulusOverlay, params);
imageBitmap = Bitmap.createBitmap((width/3)*2, height/5, Bitmap.Config.ARGB_8888);
p = new Paint();
Canvas canvas = new Canvas(imageBitmap);
p.setColor(Color.BLACK);
Typeface non_proportional_font = Typeface.createFromAsset(getAssets(), "fonts/luximr.ttf");
p.setTypeface(non_proportional_font);
float scale = getResources().getDisplayMetrics().density;
p.setTextSize(40 * scale);
fillTextPathArray();
stimulusOverlay.setImageBitmap(imageBitmap);
stimulusOverlay.draw(canvas);
}
private void fillTextPathArray(){
mTextPaths = new ArrayList<Path>(VOC_COUNT);
String[] text = new String[VOC_COUNT];
String stimulus = getStimulusFromPref(positionStimulus);
int stimulusLength = stimulus.length();
String masking = String.format("%0" + stimulusLength + "d", 0).replace('0', 'X');
text[0] = masking;
text[1] = stimulus;
text[2] = masking;
for (int i = 0; i < text.length; i++) {
Path path = new Path();
p.getTextPath(text[i], 0, masking.length(), imageBitmap.getWidth()/4, (imageBitmap.getHeight()/3)*2, path);
path.close(); // not required on 2.2/2.3 devices
mTextPaths.add(path);
}
}
This function made an ArrayList of paths, which I need to be displayed in the right order. So at the end, the user will be able to see my canvas changing from first text to second text to third text.
I started with tips like "redraw entire view" or "draw box with backgroundcolor over old text" (Android Canvas Drawing Text and Change Text afterwards), because canvas drawing couldn't be revesed. Didn't work out for me.
The big problem remains: the code runs through and everything will be shown in one canvas, one above the other. AND: Time delays like while loop or Thread.sleep(500) didn't have any impact on the canvas. The result was the same: The code was running through - with the given time delay - and at the end everything was painted, not step by step (first, second, third).
I even tryed to draw each text in separate methods.
So I tryed to implement each given new Canvas, new Paint and even new View - nothing changed. At the end everything was drawn instead of step by step.
Currently I have my custom ImageView (CustomImageView), where I'm working with the invalidate() method while using a synchronized block.
private class CustomImageView extends AppCompatImageView {
private boolean stimulusVisible = false;
private int countStimuli = 0;
public CustomImageView(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
if (!stimulusVisible) {
Object lock = new Object();
synchronized (lock) {
canvas.drawPath(mTextPaths.get(countStimuli), p);
stimulusVisible = true;
countStimuli++;
}
} else {
long startTime = System.currentTimeMillis();
while((System.currentTimeMillis()-startTime) < 500){}
canvas.drawColor(Color.CYAN);
stimulusVisible = false;
}
invalidate();
}
}
The while loop should stop everything for half a sec.
I really hope for your suppoert.
Is there a better way to implement this?
Am I missing something? I though invalidate() would redraw the canvas but it didn't have an impact. I'm trying for 2 days now, please help.
i want the eraser to erase smoothly but in my activity by default it erase the lines on touch draw and erase the lines on touch is not proper.my code what i had worked on.
public void Draw(){
count=1;
bmp = Bitmap.createBitmap(fullimage2.getWidth(), fullimage2.getHeight(), Config.ARGB_8888);
c = new Canvas(bmp);
fullimage2.draw(c);
if(mode==0){
pnt.setColor(Color.BLACK);
pnt.setStyle(Paint.Style.STROKE);
pnt.setStrokeJoin(Paint.Join.ROUND);
pnt.setStrokeCap(Paint.Cap.ROUND);
pnt.setStrokeWidth(8);
c.drawPath(path,pnt);
}
else{
pnt1.setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.CLEAR));
pnt1.setStrokeWidth(25);
pnt1.setStrokeCap(Paint.Cap.ROUND);
pnt1.setColor(Color.TRANSPARENT);
pnt1.setAlpha(0);
c.drawPath(path1,pnt1);
pnt1.setStyle(Style.STROKE);
pnt1.setMaskFilter(null);
pnt1.setAntiAlias(true);
}
Is the code you use to draw proper? Otherwise use the same code and for the erraser use the same color as your background. If you need better code to draw, ask!
I have a problem, I have tried resolve the problem but I haven't found a solution.
I have two columns of images. I want to join them through the midpoint of each image. The problem I have is that the attachment point moves down, like the image
I have a "main" class and I have the internal class: public class DrawView extends LinearLayout
with the atribute:
private Paint paint = new Paint();
and I set the next values:
paint.setColor(Color.BLACK);
paint.setStrokeWidth(6);
I use the next code for draw the lines:
public void onDraw(Canvas canvas) {
}
#SuppressLint("UseValueOf")
#Override
public void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (activateDraw) {
for (int i = 0; i < 5; i++) {
//I not include the color selection.
x1= Image[i].x + Image[i].width;
y1=Image[i].y+ (new Double(Image[i].height / 2).intValue()));
x2=ImagePr[i].x;
y2=ImagePr[i].y + (new Double((ImagePr[i].height) / 2).intValue()));
canvas.drawLine(x1, y1, x2, y2, paint);
}
activateDraw = false;
}
}
To set the x and y values I use the method:
public void setData(ImageView img) {
image = img;
int[] values = new int[2];
image.getLocationInWindow(values);
x = values[0];
y = values[1];
width = image.getWidth();
height = image.getHeight();
}
In the main class I have the atribute:
Canvas auxCanvas = new Canvas();
and I execute the onDraw(auxCanvas) method when I want draw the lines. Why the lines don't draw joining the "midpoints"?
Anyone can help me?Thanks!!
#Shaunak Sorry, it was a fail. I've removed it and it doesn't affect, the problem continues. Thank you!
#anthropomo I tried your change but the problem continues.
I don't understand why in the emulator seems to work fine, but not on the device.
SOLUTION:
(I thought I had written the answer, sorry)
The solution was very simple. The app is destinated to students that have 6-8 years, so I decided to hide the status bar and the above code works perfect without do changes!
Hide the status bar:
Hide Notification bar
How to hide the title bar for an Activity in XML with existing custom theme
If other people want to show the status bar, I suppose you need to subtract the status bar height.
reference: http://developer.android.com/reference/android/util/DisplayMetrics.html#density
does something like this work for you?:
float d = getResources().getDisplayMetrics().density;
canvas.drawLine(x1*d, y1*d, x2*d, y2*d, paint);
note: if the multiplication doesn't work try dividing by d... i can never remember what to do.
I'm implementing custom path effect for route on top of MapView and I came up with the problem how to make my beginning and ending of the path rounded (like Paint.setStrokeCap(Cap.ROUND) does). See screenshot - black lines - is my route I want to round at the end
Here is how I implemented my custom PathEffect:
public RouteOverlay(Context context)
{
mContext = context;
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(COLOR_DEFAULT);
mPaint.setAntiAlias(true);
mPaint.setStrokeCap(Cap.ROUND); // this one does not work...
mPaint.setStrokeJoin(Join.ROUND);
PathEffect e1 = new PathDashPathEffect(createRouteLineStyle(), 10, 3, PathDashPathEffect.Style.MORPH);
PathEffect e2 = new CornerPathEffect(10);
mPaint.setPathEffect(new ComposePathEffect(e1, e2));
}
private Path createRouteLineStyle()
{
Path p = new Path();
p.moveTo(-5, ROUTE_LINE_WIDTH/2);
p.lineTo(5,ROUTE_LINE_WIDTH/2);
p.lineTo(5,ROUTE_LINE_WIDTH/2-currentThickness);
p.lineTo(-5, ROUTE_LINE_WIDTH/2-currentThickness);
p.close();
p.moveTo(-5, -(ROUTE_LINE_WIDTH/2));
p.lineTo(5,-(ROUTE_LINE_WIDTH/2));
p.lineTo(5, -(ROUTE_LINE_WIDTH/2-currentThickness));
p.lineTo(-5, -(ROUTE_LINE_WIDTH/2-currentThickness));
return p;
}
#Override
public void draw(Canvas canvas, final MapView mapView, boolean shadow)
{
if(shadow) return;
if(mDrawEnabled)
{
synchronized(mPoints)
{
canvas.drawPath(mPath, mPaint);
}
}
}
As you can see on the screenshot, the ending of the line is not rounded (as well as beginning...). setStrokeCap(Cap.ROUND) doesn't help.
So the question is - how to add round cap to my custom path? I was thinking of using addArc() or addCircle() to the end (and beginning) of my path, but this doesn't seem right.
The reason why I need custom path effect - is that I need to draw the route around actual road - so route should be empty inside and have inner and outer stroke lines.
In case somebody knows how to make this kind of path effect in some other way - please let me know, because this solution has big cons I have to deal with..
I don't see any reason why that should not work unless you are running into the problem mentioned here http://code.google.com/p/android/issues/detail?id=24873.
I managed to find a solution for my problem.
So I got rid of my custom path effect and started to use usual stroke (where stroke cap works as expected). So I basically draw my path 2 times: at first I draw black line, after that I draw thiner transparent line to clear the center of previous black line.
The only trick in this approach is that I need to draw my path in a separate bitmap (using temp canvas) and when path bitmap is ready - render it to the main canvas.
#Override
public void draw(Canvas canvas, final MapView mapView, boolean shadow)
{
//Generate new bitmap if old bitmap doesn't equal to the screen size (f.i. when screen orientation changes)
if(pathBitmap == null || pathBitmap.isRecycled() || pathBitmap.getWidth()!=canvas.getWidth() || pathBitmap.getHeight()!=canvas.getHeight())
{
if(pathBitmap != null)
{
pathBitmap.recycle();
}
pathBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Config.ARGB_8888);
tempCanvas.setBitmap(pathBitmap);
}
//Render routes to the temporary bitmap
renderPathBitmap();
//Render temporary bitmap onto main canvas
canvas.drawBitmap(pathBitmap, 0, 0, null);
}
}
private void renderPath(Path path, Canvas canvas)
{
routePaint.setStrokeWidth(ROUTE_LINE_WIDTH);
routePaint.setColor(OUTER_COLOR);
routePaint.setXfermode(null);
canvas.drawPath(path, routePaint); //render outer line
routePaint.setStrokeWidth(ROUTE_LINE_WIDTH/1.7f);
routePaint.setColor(Color.TRANSPARENT);
routePaint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
canvas.drawPath(path, routePaint); //render inner line
}
So result looks like:
Here I need to remove paints.I did paints using surfaceview.inside erase button I use below code. Now when I click erase button the drawn paints all erased.But now again draw means paints not visible.please any one help me.
public void onClick(View view){
if(view==erasebtn)
{
if (!currentDrawingPath.isEmpty()) {
currentPaint .setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
action=true;
}
}
}
If you want to completely erase all drawing you have to fill it with the "empty" color.
Assuming you have a canvas in which you draw:
canvas.drawColor(Color.WHITE);
If you have drawn lines etc in a Canvas where you just add your drawings all the time then you need to create a way to restore an older version of that. Changing the Paint you have used to draw things will not change the things you have already drawn. It just affects how any future drawing is done.
There are several possibilities to do that e.g. the following should work:
Bitmap bitmap = Bitmap.createBitmap(400, 400, null);
Canvas canvas = new Canvas(bitmap);
ByteBuffer buffer = ByteBuffer.allocate(bitmap.getByteCount());
//save the state
bitmap.copyPixelsToBuffer(buffer);
// draw something
canvas.drawLine();
// restore the state
bitmap.copyPixelsFromBuffer(buffer):
That way you could go back 1 state. If you need to undo more steps think about saving Bitmaps to disk since it will consume quite a lot of memory otherwise.
Another possibility is to save all those steps you have drawn numerically in a list (like a vector graphic) in a way that you can redraw the full image up to a certain point - then you can just undo drawing by drawing just the first part of your list to a fresh image.
Edit: Would it work if you add this to the code and use it instead of undo()?
// add me to the code that has undo()
public void undoAll (){
final int length = currentStackLength();
for (int i = lenght - 1; i >= 0; i--) {
final DrawingPath undoCommand = currentStack.get( i );
currentStack.remove( i );
undoCommand.undo();
redoStack.add( undoCommand );
}
}