I have a class Token which extends View. I override the onDraw method to draw two rects. After that i want to change the size of the Token object to match the size of the rects. For testing i added an onClickListener which should print out "Test"
layout = (RelativeLayout) findViewById(R.id.layout);
Paint p = new Paint();
Token a = new Token(0,0, Token.ONE, p, this);
layout.addView(a);
a.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
System.out.println("test");
}
});
The Token class:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
tSize = canvas.getWidth()/Activity.SIZE;
paint.setColor(Color.BLACK);
r.set(0, 0, tSize, tSize);
canvas.drawRect(r, paint);
r.set(tSize, 0, 2 * tSize, tSize);
canvas.drawRect(r, paint);
}
So far so good. The problem is that it is always printing out "Test", no matter where i press on the screen. I figured that is because the View, which holds this two rects is the the size of the whole screen.
So i tried LayoutParams:
setLayoutParams(new RelativeLayout.LayoutParams(tSize*2, tSize);
This results in the rects not showing up at all(i think they got to small). I tried some other numbers:
setLayoutParams(new RelativeLayout.LayoutParams(600, 600);
This is working. The rects show up and the Listener only works in a 600x600 field. The only Problem is the rects are smaller than i want them to be. My guess is that their size is relative to the size of the view they are in.
How would i achive that my View is exactly (tSize*2, tSize) with the rects filling it out?
you may override onMeasure method to change the GameTokenView's size.
Related
My question is about difference in setting the coordinates of the center a circle using the half of the view and directly. My device is 240 * 320 and when I use the getWidht()/2 and the getHeight() methods to draw a circle in center of the screen I am successful. But when I use the 120 value instead the getWidth/2 and the 160 value instead the getHight()/2 , I can not draw the circle in center of the screen. while I think the getWidth()/2 value is equal to the 120 value and the getHeight()/2 value is equal to the 160 value.
public class MainActivity extends Activity {
RelativeLayout relativeLayout;
C c;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
relativeLayout = (RelativeLayout) findViewById(R.id.re);
c = new C(getApplicationContext());
relativeLayout.addView(c);
}
}
class C extends View {
Paint paint;
C(Context context) {
super(context);
paint = new Paint();
}
#Override
protected void onDraw(Canvas canvas) {
paint.setAntiAlias(true);
paint.setColor(Color.RED);
canvas.drawCircle(120,160,20,paint);//this is not in the center of the screen.
canvas.drawCircle(getWidth/2, getHeight/2, 20 , paint);// But this is in center of the screen
}
}
Did you print the getWidth and getHeight? It maybe not 160 and 120, because it according to your views size, sometime the size it will reduce the status bar on some Android device...
I solved my problem. because the relativeLayout object has padding attribute. I should remove the padding attributes from the relativeLayout object. For more information please see here.
I am making a noughts and crosses game and i want to draw a line over the winning combination. The playing grid consists of a grid of buttons and i want the line to be over the top of them. At the moment the line will draw but it will have a black background hiding the grid of buttons even though it is set to transparent.
How would i make the transparency work then clear when i want to start a new game?
I hope that makes sense.
This is the draw class:
public class DrawView extends View {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
public DrawView(Context context) {
super(context);
paint.setColor(Color.RED);
paint.setStrokeWidth(10);
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawLine(0, 0, 1000, 1000, paint);
}
}
This is the part of the method in the game:
public void checkWin() {
if (squares[1] == 1 & squares[2] == 1 & squares[3] == 1) {
for (int i = 1; i <= 9; i++) {
buttons[i].setEnabled(false);
}
drawView = new DrawView(this);
drawView.setBackgroundColor(Color.TRANSPARENT);
setContentView(drawView);
Intent d = new Intent(this, Win.class);
startActivity(d);
If you want to draw a line with alpha, use :
paint.setAlpha(125);
paint.setColor(Color.RED);
paint.setStrokeWidth(10);
Just remember to set it back after you use it, if you use paint else where.
Note:
0 = completely transparent
255 = completely opaque
Edit: Okay, I think you might be trying to clear the canvas, so it starts off blank.
Draw clear onto it, with PorterDuff to clear it.
#Override
public void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
canvas.drawLine(0, 0, 1000, 1000, paint);
}
I dont think your setBackgroundColor works because you have overriden the draw method. But I could be wrong, the above code should fix the issue.
I bought the book Programming Android, and it's a little confusing and disappointing on how they have different code in their Github (https://github.com/bmeike/ProgrammingAndroid2Examples/tree/master/AndroidUIDemo) and their book. I'm stuck in an example that teaches how to draw a circle in a random point, with the color that is assigned to the button, like, when I click the RED button, it should draw a red circle in a view.
I did some extra code and somehow I managed to get it working. Here is my onDraw method:
#Override
protected void onDraw(Canvas canvas)
{
paint.setStyle(Style.STROKE);
paint.setColor(hasFocus() ? Color.BLUE : Color.GRAY);
canvas.drawRect(0, 0, getWidth() - 1, getHeight() - 1, paint);
if (this.points == null) return;
paint.setStyle(Style.FILL);
for (Point p : points.getAllPoints())
{
paint.setColor(p.getColor());
canvas.drawCircle(p.getX(), p.getY(),
p.getDiameter(), paint);
}
}
Sometimes it works, sometimes not, BUT when it works, it draws a thin, large oval shape.
p.getDiameter() is ALWAYS 6. Even if I put it to a fixed 6, the effect is the same.
Also, there is some strange thing happening: If I replace p.getY() and p.getX() by 50, it will never draw anything on the screen. 50 should not be out of the screen bounds.
In the image below you can see what it's being rendered and some code I use to create the views.
Here is some extra relevant code.
Setting the pointView size (the place where I draw points):
#Override
public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
pointView.setLayoutParams(new android.widget.LinearLayout.LayoutParams(root.getWidth(), root.getHeight()/2));
}
Add an OnCLickListener to the button, so when I click it, it should draw a circle.
button1.setOnClickListener(new OnClickListener() //I'll not put the Red button here, for the sake of brevity.
{
#Override
public void onClick(View arg0)
{
makeDot(pointModel, pointView, Color.GREEN);
}
});
And the makeDot method:
private final Random rnd = new Random();
void makeDot(Points points, PointView pointView, int color)
{
points.addPoint(
rnd.nextFloat()*pointView.getWidth(),
rnd.nextFloat()*pointView.getHeight(),
color, POINT_DIAMETER /*always 6*/);
}
(I think POINT_DIAMETER should be POINT_RADIUS but it's OK for now.)
So, how can I get it to draw a round circle in a random position in the screen?
Here are a few ideas:
In addPoint, try generating the coordinates like this:
points.addPoint(
rnd.nextInt() % pointView.getWidth(),
rnd.nextInt() % pointView.getHeight(),
color, POINT_DIAMETER /*always 6*/);
In your onClick method, try adding a call to invalidate:
button1.setOnClickListener(new OnClickListener() //I'll not put the Red button here, for the sake of brevity.
{
#Override
public void onClick(View arg0)
{
makeDot(pointModel, pointView, Color.GREEN);
pointView.invalidate();
}
});
When you set the Style.FILL, set the color as well:
paint.setStyle(Style.FILL);
paint.setColor(hasFocus() ? Color.BLUE : Color.GRAY);
Also, remove this line of code:
pointView.setScaleX(20);
That's all I could think of for now.
Hope this helps :)
I'm writing a custom view that displays signals. In order to shorten my onDraw() time I cache everything I've drawn so far in a Bitmap and just append to that in every onDraw() call. By doing this I can save huge amounts of time since I only need to draw a few fixels at a time instead of redoing the whole thing.
There is on thing bothering me though - it appears as drawing directly to the provided canvas provides a more "accurate" drawing than drawing on the bitmap first and then drawing the bitmap on the canvas. By looking at the lower part of the following picture you can see the difference:
I uploaded a demo project displaying the discrepancy at https://github.com/gardarh/android-uglybitmapdrawing/ but the relevant code is as follows:
#Override
public void onDraw(Canvas canvas) {
if(cachedBitmap == null) {
cachedBitmap = Bitmap.createBitmap(getWidth(), 200, Config.ARGB_8888);
cachedCanvas = new Canvas(cachedBitmap);
}
for(int i = 0; i < COORDS.length; i++) {
float[] curCoords = COORDS[i];
canvas.drawLine(curCoords[0], curCoords[1], curCoords[2], curCoords[3], linePaint);
cachedCanvas.drawLine(curCoords[0], curCoords[1], curCoords[2], curCoords[3], linePaint);
}
canvas.drawBitmap(cachedBitmap, 0, 120, null);
}
Why are the two traces not the same and more importantly, how can I make the lower trace look like the upper one?
The reason for the differences is that the canvas drawing is done by hardware acceleration (GPU), and the bitmap drawing is done by software (CPU). If you disable hardware acceleration, they become the exact same.
If you multiply the X coordinates by 10, you will see that the difference is in the way lines are joined. These are minor one pixel difference and I wouldn't bother with them. I am not sure which one is the more accurate, they seem like just slightly different implementations.
Android framework API provides 2D drawing APIs for simple animation that does not require major dynamic changes.
There are two ways of implementation using these API.
1. Drawing to a View
2. Drawing on a Canvas
1.Drawing a circle to View
Drawing to view is a better option when your UI does not require dynamic changes in the application.
This can be achieved simply by extending the View class and define an onDraw() callback method.
Use the Canvas given to you for all your drawing,
using various Canvas.draw...() methods (Ex: canvas.drawCircle(x / 2, y / 2, radius, paint);). onDraw() is a callback method invoked when the view is initially drawn.
Below is a simple example code to draw a circle:-
MainActivity.java
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyView(this));
}
public class MyView extends View {
public MyView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
int x = getWidth();
int y = getHeight();
int radius;
radius = 100;
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
canvas.drawPaint(paint);
// Use Color.parseColor to define HTML colors
paint.setColor(Color.parseColor("#FB9J2F"));
canvas.drawCircle(x / 2, y / 2, radius, paint);
}
} }
2. Drawing rectangle on a canvas
To draw dynamic 2D graphics where in your application needs to regularly re draw itself, drawing on a canvas is a better option. A Canvas works for you as an interface, to the actual surface upon which your graphics will be drawn.
If you need to create a new Canvas, then you must define the bitmap upon which drawing will actually be performed. The Bitmap is always required for a Canvas.
The below example explains to draw a rectangle:-
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/mylayout"> </LinearLayout>
MainActivity.java
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Paint paint = new Paint();
paint.setColor(Color.parseColor("#DD4N5C"));
Bitmap bitmap = Bitmap.createBitmap(512, 800, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawRect(150, 150, 250, 250, paint);
LinearLayout layout = (LinearLayout) findViewById(R.id.mylayout);
layout.setBackgroundDrawable(new BitmapDrawable(bitmap));
}
}
I disagree that you are saving much by going the bitmap route. You might consider a data structure that stores the drawn signal and converting this to a JSON object that can be serialized/deserialized.
As for your question, this is an educated guess but it has more to do with rescaling of the drawn signal, since you are not using getHeight() when you create the bitmap.
I'm extending LinearLayout:
public class MyLinearLayout extends LinearLayout {
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(0xFFFF0000);
canvas.drawLine(0, 0, getWidth(), getHeight(), paint);
}
}
If the layout has child elements, shouldn't the red line above be drawn on top of them? Considering a layout like:
<MyLinearLayout>
<ImageView />
</MyLinearLayout>
I'd expect to get the red line drawn above the child ImageView. But it gets drawn below it. I was assuming all child drawing would have been completed after the super.onDraw() line is finished.
Is there a way to get to the canvas and draw something on it after all child drawing has been completed?
Thanks
Layouts don't draw unless you call setWillNotDraw(false);. They do that for efficiency reasons.
EDIT:
onDraw() really is meant to just allow you to modify the Canvas before the drawing operation happens. It doesn't actually draw. What you want to do is override draw() like so:
#Override
protected void draw(Canvas canvas) {
super.draw(canvas);
Paint paint = new Paint();
paint.setColor(0xFFFF0000);
canvas.drawLine(0, 0, getWidth(), getHeight(), paint);
}
Calling the super will draw all the children in the view. Then it will draw all the lines. I do still believe you need setWillNotDraw(false) to be called though.
I'm rewriting my answer as I hadn't seen your line of code where you were drawing to the canvas.
The canvas like some other graphics environments is inverted. So calling getHeight() is actually drawing the line at the bottom. Instead call
canvas.drawLine( 0, 0, getWidth(), 0, paint);
This means draw the line at the top, across the width of the view.
Also, calling super first paints the children prior to any custom painting so your fine there.
If you have a LinearLayout and you need:
draw all of its children first, like buttons, image views
then, on top of those components, draw something directly on the canvas.
You can try this:
public class YourClass extends LinearLayout
{
#Override
protected void dispatchDraw(Canvas canvas)
{
super.dispatchDraw(canvas);
// do your custom drawings afterwards
canvas.drawCircle...
canvas.drawLine...