I have the following problem: I made a class that extends from SurfaceView, it also implements SurfaceHolder.Callback but when I try to draw a red square, it's always black. Below my code:
First I call the following method in the constructor:
private void initTransparentBackgroundColor(){
this.setBackgroundColor(Color.TRANSPARENT);
this.setZOrderOnTop(true);
getHolder().setFormat(PixelFormat.TRANSPARENT);
}
Then, I start drawing:
protected void onDraw(Canvas canvas){
Paint p = new Paint();
p.setColor(Color.RED);
p.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawRect(10,10,20,20,p);
}
What am I doing wrong?
Replace
getHolder().setFormat(PixelFormat.TRANSPARENT);
To this
getHolder().setFormat(PixelFormat.TRANSLUCENT);
Good Luck. :)
Related
I'm creating an Android game (basic game), and got stuck with displaying certain image on a canvas.
The game intention is to press the image and the image will change it's location.
I've made sure all the methods are written properly and image resource does exist.
The other questions which relates to "canvas isn't showing" here didn't assist me because my game doesn't involve any XML (yet).
I've followed this guide:
https://www.androidauthority.com/android-game-java-785331/
This is the my GameView class which suppose to create the image:
public class GameView extends SurfaceView implements SurfaceHolder.Callback {
public GameView(Context context) {
super(context);
getHolder().addCallback(this);
thread = new MainThread(getHolder(), this);
setFocusable(true);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Canvas canvas=new Canvas();
thread.setRunning(true);
thread.start();
characterSprite = new CharacterSprite(BitmapFactory.decodeResource(getResources(),R.drawable.avdgreen));
characterSprite.draw(canvas);
}
#Override
public void draw(Canvas canvas){
super.draw(canvas);
characterSprite.draw(canvas);
}
}
My CharacterSprite class:
public CharacterSprite(Bitmap png) {
image = png;
x=100;
y=100;
}
public void draw(Canvas canvas) {
canvas.drawBitmap(image, 100, 100, null);
}
The Image should appear on the screen, yet all I get is blank screen.
Thanks.
Just starting out with Android, canvas, the works. I have existing Java code that can draw shapes on a graphics object. I am trying to use that code in an Android app with Canvas. Basically, I'm trying to avoid refactoring all of my Java code to use Canvas explicitly. So I'm in the process of making my own "Graphics" object. All it should do is call the appropriate Canvas methods to draw the specified shape.
I've read multiple posts here about not being able to use the canvas object outside of the onDraw() method. From what I understand, you can't pass a canvas object to a different class and expect it to work correctly. But I also do not have a complete understanding of how all this works.
The app crashes in the Graphics class in the drawOval method. I've read up a log about all of this, but I haven't found a good answer as to why this specifically doesn't work.
I haven't been able to find a way to get crash logs in a java friendly way (aka a stacktrace). It just throws Fatal signal 11 (SIGSEGV), code 1, fault addr 0x130 in tid 1520.
Thanks! Let me know if more details are needed. Here's my code:
MainActivity
public class MainActivity extends AppCompatActivity {
MyCanvas canvas;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
canvas = new MyCanvas(this);
setContentView(canvas);
}
}
MyCanvas View
public class MyCanvas extends View {
Graphics graphics;
List<Shape> shapes;
public MyCanvas(Context context) {
super(context);
graphics = new Graphics();
shapes = new ArrayList<>();
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
graphics.setCanvas(canvas); //Sets the canvas object in the graphics class
for (Shape shape : shapes) {
try { //this is in a try/catch block for custom exception handling
//This just calls the specific shapes render method,
//in this case, a circle from the makeShape Method
//The graphics object then calls the specific shape to
//render on the canvas
shape.render(graphics, 0, 0);
} catch (ShapeException e) {
e.printStackTrace();
}
}
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
int x = (int) event.getX();
int y = (int) event.getY();
try { //this is in a try/catch block for custom exception handling
makeShape(x, y);
} catch (ShapeException e) {
e.printStackTrace();
}
invalidate();
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
break;
}
}
invalidate();
return true;
}
public void makeShape(int x, int y) throws ShapeException{
Point p = new Point(x, y);
Shape shape = new Circle(p, 50, 50);
shapes.add(shape);
}
}
Graphics
public class Graphics {
private Canvas canvas = null;
public Graphics() {
}
public void setCanvas(Canvas canvas) {
if (this.canvas == null) {
this.canvas = canvas;
}
}
public void drawOval(int x, int y, int width, int height) {
Paint paint1 = new Paint();
paint.setColor(Color.BLACK);
canvas.drawCircle(500, 500, 50, paint1); //App crashes here
}
}
You have a slight misunderstanding about being able to pass a Canvas to different classes. There is absolutely nothing wrong with that; passing a canvas to a class method is effectively the same as passing it to a regular function. Even storing the Canvas reference in a member variable isn't going to hurt anything.
HOWEVER, the above is only true with the understanding that the Canvas cannot be used outside the bounds of the draw()/onDraw() method. That is, any method that uses the Canvas must be called from within onDraw(), or a function called by onDraw(), etc.
This is because the Canvas is initialized (by the framework) immediately before onDraw(), in order to prepare for the current drawing operation. You might wish to think of it as initializing the Canvas with a Bitmap that will serve as the drawing output surface for this particular screen frame (which is not far from the truth). Once onDraw() has returned, the framework assumes your code will no longer be using it, and it can submit the output surface/Bitmap/etc. to the screen compositor for rendering, without fear of further alterations.
And, your approach is a good one, as a technique for adapting existing code to use a novel graphics object.
So, to address the crash: SIGSEGVs should never happen in normal Android development when you're not dealing (directly) with native/JNI routines. However, it occurs to me that you're not updating the Canvas object in the event it changes. This could be causing the crash (in native code, which is why you don't get a Java stack trace). You used an old Canvas after a newer one was given to you. Remove the condition if (this.canvas == null) and you should be fine.
I'm trying to create a translucent help overlay to be displayed over my activity's main screen when the user first opens the app. I would like to highlight a button contained in the main layout (and inflated using setContentView), by "cutting out" a section of the overlay which corresponds to the position of the button, and make the cutout transparent.
The overlay is a programmatically-created view (which extends RelativeLayout) which is added to my activity's main FrameLayout, like this:
private void addHelpOverlay(){
HelpOverlay help = new HelpOverlay(this);
help.setBackgroundColor(Color.parseColor("#BB222222"));
mainLayer.addView(help);
}
public class HelpOverlay extends RelativeLayout{
public HelpOverlay(Context context){
super(context);
}
#Override
public void dispatchDraw(Canvas canvas){
canvas.drawColor(Color.parseColor("#BB222222"));
Paint mPaint = new Paint();
mPaint.setColor(0xFFFFFF);
mPaint.setAlpha(0);
mPaint.setAntiAlias(true);
canvas.drawCircle(buttonX, buttonY, 100, mPaint);
super.dispatchDraw(canvas);
}
}
The above code doesn't actually show anything, just the translucent layout with no circle cutout. I assume this is because it's just drawing a transparent circle over top of the translucent layout. I'm really struggling to accomplish this, any suggestions would be appreciated!
Try adding PorterDuff to your paint object . which will make the specific area to transparent
Paint mPaint = new Paint();
mPaint.setColor(0xFFFFFF);
mPaint.setAlpha(0);
mPaint.setAntiAlias(true);
mPaint.setColor(Color.TRANSPARENT);
mPaint.setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.CLEAR));
canvas.drawCircle(buttonX, buttonY, 100, mPaint);
If you get a picth black in the circle area, that must be due to graphic rendering problem , you can enable it using below code right before you declare paint object .
if (android.os.Build.VERSION.SDK_INT >= 11) {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
I guess this should fix your issue
I am trying set up a kids app where the can draw on a canvas.
I have a layout which has an ImageView and looks something like this:
The Green background with the letter is the imageview, where the canvas should overlay and allow user to draw on. Also I want to allow the user to change the color stroke by pressing on the color globe.
How can I go on with it?
This answer here might help you with allowing the user the canvas: https://stackoverflow.com/a/7401699/2698582.
To allow the user to draw in different colors, you can use a color picker such as this one: https://code.google.com/p/android-color-picker/, or you can google for a different one.
If you define a new point class and change a bit of this example code, you'll be able to add this color change:
private class ColoredPoint {
public int x, y, color = Color.BLACK;
}
public class DrawView extends View implements OnTouchListener {
List<ColoredPoint> points = new ArrayList<ColoredPoint>();
Paint paint = new Paint();
int currentColor = Color.BLACK;
public DrawView(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
paint.setColor(currentColor);
}
#Override
public void onDraw(Canvas canvas) {
for (ColoredPoint point : points) {
paint.setColor(point.color);
canvas.drawCircle(point.x, point.y, 2, paint);
}
}
public boolean onTouch(View view, MotionEvent event) {
ColoredPoint point = new ColoredPoint();
point.color = currentColor;
point.x = event.getX();
point.y = event.getY();
points.add(point);
invalidate();
return true;
}
}
Finally, depending on your selected color picker, this code will vary. Basically, you'll add a touch listener to your color globe to show the color popup. Supposing that the popup has an "OK" button, you'll add a button listener to that button. When it is pressed, change the variable "currentColor" to the selected color.
As mentioned in that example post, you can also implement this using Lines. You might consider using a GestureDetector instead. This tutorial should help explain exactly how the GestureDetector works: http://developer.android.com/training/gestures/detector.html.
Take linearlayout and transfer imageview into linearlayout background color. Add your custom view into this linearlayout, and override ondraw method to that view class.
something like this:
<LinearLayout
background:#drawable/foo">
<com.packagename.customview
android:width="match_parent"
android:height="value">
>
</com.packagename.customview>
</LinearLayout>
create customview class and implement drawing part into it.
For the color picker, just use this link: https://code.google.com/p/android-color-picker/
I am trying to create an object that can be called whenever I want to draw something on the onDraw() function of my class. Here is my code which is not working:
//object class
public class DrawObject extends Canvas {
Paint paint = new Paint();
public void setColor(int color){
paint.setColor(color);
}
// I want to draw an arrow to instead of a line
public void drawArrow(float startPointX, float startPointY, float endPointX, float endPointY){
drawLine(startPointX, startPointY, endPointX, endPointY, paint);
// draw the rest of the arrow here
}
}
for the main class:
public class Screen extends ImageView{
Paint paint = new Paint();
public Screen(Context context){
super(context);
paint.setColor(Color.BLACK);
paint.setStyle(Style.STROKE);
}
public void onDraw(DrawObject drawObject){
//called DrawObject instead on Canvas
drawObject.drawArrow(10,10,100,100);
// I want to draw the arrow here but it is not working.
}
can someone please tell me what is the proper way to do it? Thanks.
I would actually do something along these lines:
Define an interface for drawable objects (to avoid confusion with the Android Drawable class, I've called it Paintable)
public interface Paintable {
public void paint(Canvas canvas) {}
}
Make any classes that you need to draw in a custom manner, such as the arrow you suggested, implement the Paintable interface, and override paint(Canvas canvas) and handle your drawing operations there.
public class Arrow implements Paintable {
//all sorts of cool shit here to define your arrow
#Override
public void paint(Canvas canvas) {
//draw your arrow on the canvas here
}
}
Maintain a list of Paintable objects in your custom ImageView, and simply call the paint() method for each object in the list. Yay for polymorphism!
public class Screen extends ImageView {
List<Paintable> paintableObjects;
public Screen(Context context) {
super(context);
paint.setColor(Color.BLACK);
paint.setStyle(Style.STROKE);
paintableObjects = new ArrayList<Paintable>();
}
public void addPaintable(Paintable p) {
paintableObjects.add(p);
}
public void onDraw(Canvas canvas) {
for(Paintable p : paintableObjects) {
p.paint(canvas);
}
}
}
Extending Canvas is the wrong approach since you get a canvas supplied by the system which you have to use. You can't force Android to use your subclass of Canvas. Instead you can simply pass the Canvas to your class whenever it needs it.
public class DrawObject {
Paint paint = new Paint();
public void setColor(int color) {
paint.setColor(color);
}
// I want to draw an arrow to instead of a line
public void drawArrow(Canvas canvas, float startPointX, float startPointY, float endPointX, float endPointY) {
canvas.drawLine(startPointX, startPointY, endPointX, endPointY, paint);
// draw the rest of the arrow here
}
}
And use like
public void onDraw(Canvas canvas){
drawObject.drawArrow(canvas, 10,10,100,100);
// I want to draw the arrow here but it is not working.
}
I really don't see the reason of extending the Canvas and defining your own object, unless you need to have some special control over it.
Normally you extend View and do all the drawing stuff in the onDraw() method through the Canvas object, ex:
public void onDraw(Canvas canvas){
canvas.drawLine(startPointX, startPointY, endPointX, endPointY, paint);
}
this bit is wrong
public void onDraw(DrawObject drawObject){ ... }
onDarw is a frame work method so sice you change the method signature this method will not be called.
There is no need to subclass canvas - its not the right approach.
so in you Screen class - override the standard onDraw method:
public void onDraw(Canvas c){
drawLine(startPointX, startPointY, endPointX, endPointY, paint);
}
to trigger the onDraw mthod programatically - call invalidate() on the Screen object.