I'm trying to move a BitmapDrawable in a custom view. It works fine with a ShapeDrawable as follows:
public class MyView extends View {
private Drawable image;
public MyView() {
image = new ShapeDrawable(new RectShape());
image.setBounds(0, 0, 100, 100);
((ShapeDrawable) image).getPaint().setColor(Color.BLACK);
}
#Override
protected void onDraw(Canvas canvas) {
image.draw(canvas);
}
public void move(int x, int y) {
Rect bounds = image.getBounds();
bounds.left += x;
bounds.right += x;
bounds.top += y;
bounds.bottom += y;
invalidate();
}
}
However, if I use a BitmapDrawable, the drawable's bounds change, the onDraw method is called, but the image stays where it is on the screen.
The following constructor will reproduce the problem by creating a BitmapDrawable instead:
public MyView() {
image = getResources().getDrawable(R.drawable.image);
image.setBounds(0, 0, 100, 100);
}
How can I move a BitmapDrawable?
The documentation for Drawable.getBounds() says the following:
Note: for efficiency, the returned
object may be the same object stored
in the drawable (though this is not
guaranteed), so if a persistent copy
of the bounds is needed, call
copyBounds(rect) instead. You should
also not change the object returned by
this method as it may be the same
object stored in the drawable.
This is not cristal clear but it looks like we must not change value returned by getBounds(), it fires some nasty side effects.
By using copyBounds() and setBounds() it works like a charm.
public void move(int x, int y) {
Rect bounds = image.copyBounds();
bounds.left += x;
bounds.right += x;
bounds.top += y;
bounds.bottom += y;
image.setBounds(bounds);
invalidate();
}
Another way of moving a Drawable could be to move the Canvas on wich you are drawing:
#Override
protected void onDraw(Canvas canvas) {
canvas.translate(x, y);
image.draw(canvas);
}
Related
The red Margin represents an AbsoluteLayout, I have and arbitrary number of 'Board' objects placed on the screen. All I want is to draw a line on the screen using the coordinates of the Board object and the center of the screen. Each board object is responsible to draw this line.
Also I want the line to be behind the Board objects I'm guessing I have to change the z-index, or maybe draw the line on the AbsoluteLayout?
I have something like this:
public class Board {
ImageView line; //Imageview to draw line on
Point displayCenter; //Coordinates to the center of the screen
int x;
int y;
Activity activity;
Board(Point p, Point c, Activity activity) // Point c is the coordinates of the Board object
{
x = c.x
y = c.y
displayCenter.x = p.x;
displayCenter.y = p.y;
this.activity = activity;
updateLine();
}
public void updateLine(){
int w=activity.getWindowManager().getDefaultDisplay().getWidth();
int h=activity.getWindowManager().getDefaultDisplay().getHeight();
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
line.setImageBitmap(bitmap);
Paint paint = new Paint();
paint.setColor(0xFF979797);
paint.setStrokeWidth(10);
int startx = this.x;
int starty = this.y;
int endx = displayCenter.x;
int endy = displayCenter.y;
canvas.drawLine(startx, starty, endx, endy, paint);
}
}
first af all,
you should never ever use the absolute layout, it is deprecated for a good reason.
With that said you have two options. For both options you need to implement your own Layout.
For option no. 1 you can override the dispatchDraw(final Canvas canvas) see below.
public class CustomLayout extends AbsoluteLayout {
...
#Override
protected void dispatchDraw(final Canvas canvas) {
// put your code to draw behind children here.
super.dispatchDraw(canvas);
// put your code to draw on top of children here.
}
...
}
Option no. 2 If you like the drawing to occur in the onDraw me you need to set setWillNotDraw(false); since by default the onDraw method on ViewGroups won't be called.
public class CustomLayout extends AbsoluteLayout {
public CustomLayout(final Context context) {
super(context);
setWillNotDraw(false);
}
...
#Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
// put your code to draw behind children here.
}
}
guys
I try to implement an black arrow which is on head of arc. The arrow animates with the arc, and has circle trajectory. I already implement the red arc with animation based on different values.
Please see the attachment.
How can I implement the black arrow on top of red arc? since if I do the same way as red arc animation, the black arrow will print the trajectory which is not desired.
Thanks in advance!
Leon
all you need is Canvas.
Here is example.
This is the your draw class:
public class CircleView extends View
{
public CircleView(Context context) {
super(context);
path = new Path();
paint = new Paint();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float angle = 270;
float radius = canvas.getWidth()/3;
float x = canvas.getWidth()/2;
float y = canvas.getHeight()/2;
final RectF oval = new RectF();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(25);
oval.set(x - radius, y - radius, x + radius,y + radius);
// Draw circle
paint.setColor(Color.RED);
canvas.drawArc(oval, 135, angle, false, paint);
paint.setColor(Color.BLUE);
canvas.drawArc(oval, angle, 405-angle, false, paint);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.BLACK);
float l = 1.2f;
float a = angle*(float)Math.PI/180;
// Draw arrow
path.moveTo(x+ (float)Math.cos(a) *radius, y + (float)Math.sin(a) * radius);
path.lineTo(x+ (float)Math.cos(a+0.1) *radius*l, y + (float)Math.sin(a+0.1) * radius*l);
path.lineTo(x+ (float)Math.cos(a-0.1) *radius*l, y + (float)Math.sin(a-0.1) * radius*l);
path.lineTo(x+ (float)Math.cos(a) *radius, y + (float)Math.sin(a) * radius);
canvas.drawPath(path, paint);
}
private Path path;
private Paint paint;
}
This is activity for it:
public class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new CircleView(this));
}
}
For angle == 200 you will see image like this:
Working copy for IntelliJ Idea: https://github.com/weerf/android_circle
I have to draw circle on screen and get interaction to it by OnTouch method. Kindly help me out. Here is the code that I have tried. Here the problem is that It does not intract with user interaction but this code successfully draw the circle
public class DrawingView extends View implements OnTouchListener {
static int x, y, r = 255, g = 255, b = 255;
final static int radius = 30;
Paint paint; // using this ,we can draw on canvas
public DrawingView(Context context) {
super(context);
paint = new Paint();
paint.setAntiAlias(true); // for smooth rendering
paint.setARGB(255, r, g, b); // setting the paint color
// to make it focusable so that it will receive touch events properly
setFocusable(true);
// adding touch listener to this view
this.setOnTouchListener(this);
}
// overriding the View's onDraw(..) method
public void onDraw(Canvas canvas) {
paint.setARGB(255, r, g, b);
super.onDraw(canvas);
// drawing the circle
canvas.drawCircle(x, y, radius, paint);
randColor(); // calls this method to generate a color before drawing
invalidate(); // calls onDraw method
}
// this is the interface method of "OnTouchListener"
public boolean onTouch(View view, MotionEvent event) {
x = (int) event.getX() - (radius / 2); // some math logic to plot the
// circle in exact touch place
y = (int) event.getY() - (radius / 2);
// System.out.println("X,Y:"+"x"+","+y); //see this output in "LogCat"
randColor(); // calls this method to generate a color before drawing
invalidate(); // calls onDraw method
return true;
}
// this method sets a random color using Math.random()
// Note: RGB color values ranges from 0 to 255..
public void randColor() {
r = (int) (Math.random() * 255);
g = (int) (Math.random() * 255);
b = (int) (Math.random() * 255);
// Toast.makeText(c, "r,g,b="+r+","+g+","+b,Toast.LENGTH_SHORT).show();
}
}
But the problem is that, it does not get user interaction
Simply use this to draw circle
public class Circle extends View {
private final float x;
private final float y;
private final int r;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
public Circle(Context context, float x, float y, int r) {
super(context);
mPaint.setColor(0xFFFF0000);
this.x = x;
this.y = y;
this.r = r;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(x, y, r, mPaint);
}
For Interaction MainActivity class is here
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.circle);
FrameLayout main = (FrameLayout) findViewById(R.id.main_view);
main.addView(new Circle(this, 50, 50, 25));
main.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent e) {
float x = e.getX();
float y = e.getY();
FrameLayout flView = (FrameLayout) v;
flView.addView(new Circle(flView.getContext(), x, y, 5));
return true;
}
});
}
At least a couple problems:
You've not actually tested wether the touch x/y fall inside the radius of the circle. You need an if clause. invalidate() now gets called with every touch.
The order of events is wrong and some operations are called too many times. Just this inside ondDraw should work:
super.onDraw(canvas);
paint.setARGB(255, r, g, b); // (and you don't need this in the method above)
canvas.drawCircle(x, y, radius, paint);
remove the invalidate() in onDraw() method, and get random color in onTouch().
you want different color in every touch action, place in touch(), or based touch down want to change the color then check the action from the event.getAction()==MotionEvent.ACTION_DOWN.
remove the invalidate() in onDraw() method because when you are invalidating in onDraw() it will call onDraw Recursivly, and get random color in onTouch().
you want different color in every touch action, place the random color method in onTouch(), or based touch down want to change the color then check the action from the event.getAction()==MotionEvent.ACTION_DOWN.
I want to draw a circle (it is more but I have problems with the circle) on a canvas with the View Class.
I have started with this link: http://mindtherobot.com/blog/272/
The onMeasure() function is the same and I am scaling the same. In my emulator and on my Nexus 7 everything looks perfect but on my LG it isnĀ“t
It looks loke the color of the circle is blurred.
The code is:
protected void onDraw(Canvas canvas) {
float scale = (float) getWidth();
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(scale, scale);
drawCircle(canvas);
canvas.restore();
}
private void drawCircle(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.drawOval(rimRect, debug);
canvas.restore();
}
private void initDrawingTools() {
float O1x = -0.5f;
float O1y = -0.5f;
float O2x = 0.5f;
float O2y = 0.5f;
rimRect = new RectF(O1x, O1y, O2x, O2y);
debug = new Paint();
debug.setColor(Color.RED);
}
use AntiAlias property for Paint. it protect the image from blurring and gives smoothing edges
debug = new Paint();
debug.setAntiAlias(true);
debug.setColor(Color.RED);
I'm trying to animate drawing out a circle. In my custom view, I have
private final Paint mPaint = new Paint() {
{
setDither(true);
setStyle(Paint.Style.STROKE);
setStrokeCap(Paint.Cap.ROUND);
setStrokeJoin(Paint.Join.ROUND);
setColor(Color.BLUE);
setStrokeWidth(30.0f);
setAntiAlias(true);
}
};
...
protected void onDraw(Canvas canvas) {
super.onDraw();
if (mOval == null) {
mOval = new RectF(getLeft(), getTop(), getRight(), getBottom());
}
if (mPath == null) {
mPath = new Path();
mPath.moveTo(0, getHeight() / 2);
}
float sweepAngle = Math.min((float) mElapsedTime / 1000 * 60 * 1, 1) * 360;
if (sweepAngle == 0) {
mPath.reset();
} else if (mCurrentAngle != sweepAngle) {
mPath.arcTo(mOval, mCurrentAngle, sweepAngle);
}
mCurrentAngle = sweepAngle;
canvas.drawPath(mPath, mPaint);
}
At intervals, I'm updating mElapsedTime and calling invalidate(). However, nothing is drawn on the screen. I've tried several variations, but to no avail. Is there something I'm doing wrong? Is there an easier way to do this? Given a percentage of a circle, I want to be able to make that much of the circle be what is drawn on the screen.
There are two things here:
You have to call canvas.drawOval(...) before drawing the arc onto the oval. Otherwise it won't show up. This is why my method didn't work.
Canvas has a drawArc method that takes a start angle and a degrees to sweep out. See Canvas.drawArc(RectF, float, float, boolean, Paint). This is what I was looking for to draw circles.
EDIT: here's the relevant code from my View subclass:
private final Paint mArcPaint = new Paint() {
{
setDither(true);
setStyle(Paint.Style.STROKE);
setStrokeCap(Paint.Cap.ROUND);
setStrokeJoin(Paint.Join.ROUND);
setColor(Color.BLUE);
setStrokeWidth(40.0f);
setAntiAlias(true);
}
};
private final Paint mOvalPaint = new Paint() {
{
setStyle(Paint.Style.FILL);
setColor(Color.TRANSPARENT);
}
};
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF mOval = new RectF(left, top, right, bottom); //This is the area you want to draw on
float sweepAngle = 270; //Calculate how much of an angle you want to sweep out here
canvas.drawOval(mOval, mOvalPaint);
canvas.drawArc(mOval, 269, sweepAngle, false, mArcPaint); //270 is vertical. I found that starting the arc from just slightly less than vertical makes it look better when the circle is almost complete.
}