Here's my OnDraw() method
void onDraw(Canvas canvas) {
mCanvas = canvas;
//invalidate();
int x = 0;
Iterator<Letter> it = mNextUpQueue.iterator();
while(it.hasNext()){
mCanvas.drawBitmap(it.next().getNext(), mNextUpCoordinates.get(x).x, mNextUpCoordinates.get(x).y, mPaint);
mCanvas.drawBitmap(mAvailableLetters.get(x).getNotPressed(), mAvailableLettersCoordinates.get(x).x, mAvailableLettersCoordinates.get(x).y, mPaint);
x++;
}
}
I have set canvas to a global variable mCanvas. But if I try to paint on mCanvas from outside the onDraw() method I get an error. Is it because I'm doing something wrong or the canvas must always be used from within the onDraw method?
You mustn't take the reference to the Canvas passed, as it is only valid during the onDraw(Canvas) method call.
I recommend reading http://developer.android.com/guide/topics/graphics/2d-graphics.html#draw-with-canvas thoroughly, the possible ways are explained there quite well:
To the Canvas provided to the onDraw(Canvas) method, during this method call. This is done in the UI thread, so nothing complicated should be attempted here
To a Canvas you created yourself. This could be useful for preparing a bitmap in another thread and then drawing the bitmap to the Canvas given to onDraw(Canvas) method
Using a SurfaceView, and obtaining a Canvas object from it with lockCanvas() method.
You can use invalidate(); to call onDraw() and draw canvas depending on your drawing logic.
Example
public class ThumbnailImage extends android.support.v7.widget.AppCompatImageView {
public static final int FALSE = 0, TRUE = 1, NOT_SET = 2;
private int drawingStatus;
public ThumbnailImage(Context context) {
super(context);
init();
}
public ThumbnailImage(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public ThumbnailImage(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
...
drawingStatus = NOT_SET;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (drawingStatus != NOT_SET) {
if (drawingStatus == TRUE) {
...
} else {
...
}
}
}
public void setDrawingStatus(int drawingStatus) {
this.drawingStatus = drawingStatus;
invalidate();
}
}
Related
I have 12 lines I've created using the following class
public class LineView extends View {
private Paint paint = new Paint();
private PointF pointA,pointB;
// private void init() {
// paint.setColor(Color.BLACK);
// }
public LineView(Context context) {
super(context);
// init();
}
public LineView(Context context, AttributeSet attrs) {
super(context, attrs);
// init();
}
public LineView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// init();
}
#SuppressLint("ResourceAsColor")
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
int color = R.color.GradientStart;
paint.setColor(color);
paint.setColor(Color.BLUE);
paint.setStrokeWidth(5);
//canvas.drawLine(x1, y1, x2, y2, paint);
canvas.drawLine(pointA.x, pointA.y, pointB.x, pointB.y, paint);
}
public void setPointA(PointF point){
pointA=point;
}
public void setPointB(PointF point){
pointB=point;
}
public void draw(){
invalidate();
requestLayout();
}
}
Instead of lines I what lines with arrows. The line with arrow will be drawn between buttons.
How can I add arrows to one end of my lines?
It would like like this when complete.
thanks
JN
I found two way for achieve your requirement.
1) To use nine patch image. I have tried to make nine patch image for you please use it
2) You can use vector image for it.
Tell me still you not getting solution.
I have created an activity that uses a canvas on which I can draw.
Within the activity, I need to retrieve data from a table that I have created elsewhere.
In other activities, I have used the database handler below and it works fine.
In this it does not, I think because I have extended imageview (required for the canvas) rather than extending Activity.
Thank you in advance for any help....
Here is my code:
public class examplecanvas extends ImageView {
float xPos, yPos;
private PointF point;
private Paint paint = new Paint();
public examplecanvas(Context context) {
super(context);
}
public examplecanvas(Context context, AttributeSet attrs) {
super(context, attrs);
}
public examplecanvas(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onTouchEvent(#NonNull MotionEvent event) {
float x = event.getX();
float y = event.getY();
}
return true;
}
#Override
protected void onDraw(#NonNull Canvas canvas) {
super.onDraw(canvas);
if (point != null) {
canvas.drawCircle(point.x, point.y, 100, paint);
canvas.save();
}
}
public void getmydata() {
String parenta = "parent"
myDBhandler1 dbHandler;
dbHandler = new myDBhandler1(examplecanvas.this, null, null, 1); <<<<HERE IS THE ERROR
}
}
...and this is the error I get:
Your database handler requires a context, so you could pass
new myDbHandler1(getContext(), ...);
"examplecanvas.this" does not have context like you want to provide to the DBHandler constructor. You should pass Context in your getmydata() function as a parameter (Then use YourActivity.this or getActivity() when you call this method, depends on Activity or Fragemnt). Or simply use the Context received in your extended ImageView constructors.
I have this custom view class which i found to make a rotate animation for the imageView i have in my project:
public class RotatedTitle extends ImageView
{
private float angleRotation;
public RotatedTitle(Context context)
{
super(context);
}
public RotatedTitle(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public RotatedTitle(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
public void setAngleRotation(float angleRotation)
{
this.angleRotation = angleRotation;
postInvalidate();
}
#Override
protected void onDraw(Canvas canvas)
{
Rect clipBounds = canvas.getClipBounds();
canvas.save();
canvas.rotate(angleRotation, clipBounds.exactCenterX(), clipBounds.exactCenterY());
super.onDraw(canvas);
canvas.restore();
postInvalidate();
}
}
Then am calling it like this in my main project:
RotatedTitle to = (RotatedTitle) findViewById(R.id.rotate);
to.setAngleRotation(10);
nothing is happening at all !! I do get the image to be drawn on the screen but no animation is happening. I tried to change invalidate() to postInvalidate() but nothing worked. Where is exactly to problem ? why I cant get any rotation animation!
Thank you
But you can use just RotateAnimation() class provided by SDK
ImageView image = (ImageView) findViewById(R.id.imageView);
Animation anim = new RotateAnimation(0.0f, 10.0f, pivotX, pivotY);
an.setDuration(5000);
an.setFillAfter(true);
image.setAnimation(anim);
If you're trying to rotate a view, use the RotateAnimation class. It was designed for this use. See the docs: http://developer.android.com/reference/android/view/animation/RotateAnimation.html
Hy, I'm trying to do an animation of a circle drawn on canvas. I can do that pretty easily with ObjectAnimator, however I'd like to start the animation when the view finishes loading or finishes drawing. If I start the animation in init(), the animation property will be "ahead" of the actual drawing, so I need to start it on a callback when the whole view is properly set up. I could do that onMeasure() or onSizeChanged() but those two get called too many times and if i have nested layouts it doesn't work properly. If I use startDelay() it works but I don't think that is an accurate procedure.
Here is a basic custom view class with animation property that changes the radius of a circle.
public class CustomView extends View {
private static final String TAG = CustomView.class.toString();
public CustomView(final Context context) {
super(context);
init(context);
}
public CustomView(final Context context, final AttributeSet attrs) {
super(context, attrs);
init(context);
}
public CustomView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(final Context context) {
// OUTER CIRCLE PAINT
mPaint = new Paint();
// Adds anti-aliasing to drawed elements
mPaint.setAntiAlias(true);
mPaint.setFilterBitmap(true);
mPaint.setStrokeWidth(1);
mPaint.setStrokeCap(Paint.Cap.SQUARE);
mPaint.setStyle(Paint.Style.FILL);
final int animationTime = getResources().getInteger(ANIMATION_TIME_ID);
progressAnimator = ObjectAnimator.ofFloat(this, "animProgress", 0f, 0f);
progressAnimator.setDuration(animationTime);
Log.d(TAG, "Init ended");
//startAnimationCircle(50f);
}
#Override
public void onDraw(final Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(this.getWidth()/2, this.getHeight()/2, animProgress, mPaint);
}
#Override
protected void onSizeChanged (int w, int h, int oldw, int oldh) {
//startAnimationCircle(50f);
}
/**
* onMeasure() is called automatically right after a call to measure()
*/
#Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//startAnimationCircle(50f);
}
private Paint mPaint;
private static final int ANIMATION_TIME_ID = android.R.integer.config_mediumAnimTime;
private float animProgress;
private ObjectAnimator progressAnimator;
public float getAnimProgress() {
return animProgress;
}
public void setAnimProgress(float animProgress) {
this.animProgress = animProgress;
this.invalidate();
}
public void startAnimationCircle(float size) {
progressAnimator.setFloatValues(animProgress, size);
//progressAnimator.setStartDelay(2000);
progressAnimator.start();
}
}
And the XML also.
<com.your-package.CustomView
android:layout_width="match_parent"
android:layout_height="match_parent" />
its my first time to ask a question here. I have this problem with my android app.
I would like to create different draw methods inside my custom view class. There are three buttons corresponding to 3 different shapes. When a button is pressed it will draw its shape. But the app crashes when I try to call the custom draw from the MainActivity for me to test it.
MainActivity
import com.example.shapes.view.ShapesView;
public class MainActivity extends Activity {
ShapesView shapesview;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
shapesview = (ShapesView) findViewById(R.id.ShapesViewID);
shapesview.DrawRectangle();
}
ShapesView
public class ShapesView extends View{
Canvas canvas;
public ShapesView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
this.canvas = canvas;
}
public void DrawRectangle() {
Paint mypaint = new Paint();
mypaint.setColor(Color.BLUE);
canvas.drawRect(30, 30, 200, 200, mypaint);
}
}
My XML layout file
<com.example.shapes.view.ShapesView
android:id="#+id/ShapesViewID"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content" />`
Please do help! Thank you very much!
If I understand your question you need a custom view (a button) which will draw itself differently based on some event. If that is the case then you need to respect the android's guidelines about the view drawing take a look here.
Now one possible solution for your case is to set some kind of flag about the state of your view and then use that flag when you are ready to draw. For example you can do this:
public class ShapesView extends View{
public int state = 0;
public ShapesView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
if (state == 1) {
Paint mypaint = new Paint();
mypaint.setColor(Color.BLUE);
canvas.drawRect(30, 30, 200, 200, mypaint);
}
}
and then whenever you need to draw your view from an activity you can use the following:
myview.state = 1;
myview.invalidate();
In your code what you are doing is that you call a views function during the onCreate of your activity which in turn tries to use a null canvas because the onDraw method of your view has not be called during that time. Furthermore as others have pointed out you must not use a canvas object outside the onDraw method.
Hope this helps...
I changed your code with following.
public class ShapesView extends View {
Paint mypaint;
public ShapesView(Context context, AttributeSet attrs) {
super(context, attrs);
mypaint = new Paint();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mypaint.setColor(Color.BLUE);
canvas.drawRect(30, 30, 200, 200, mypaint);
}
}
When you want to draw, call shapesview.invalidate();
Look at method's name its onDraw() and not getCanvas(). The documentation also doesn't makes any claims about the Canvas provided.
After onDraw() is done, that canvas may be disposed, its bitmap/buffer may be re-cycled, who knows.
So, it is not safe to use the Canvas outside of this method. Draw what you want, but only inside onDraw() method.
If you want to trigger the View to re-draw, at some other time, call invalidate().
Example:
View class to render any shape:
public class ShapeView extends View {
private Paint mPaint;
private ShapeRenderer mRenderer;
public ShapeView(Context context) {
super(context);
init();
}
public ShapeView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ShapeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init(){
mPaint = new Paint();
}
public void setPaintColor(int color){
mPaint.setColor(color);
}
public void setPaintStrokeWidth(float width){
mPaint.setStrokeWidth(width);
}
public void setRenderer(ShapeRenderer renderer) {
mRenderer = renderer;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mRenderer != null){
mRenderer.drawShape(canvas,mPaint);
}
}
public static interface ShapeRenderer{
public void drawShape(Canvas canvas, Paint paint);
}
}
A Class that draws a rectangle:
public class RectRenderer implements ShapeView.ShapeRenderer {
#Override
public void drawShape(Canvas canvas, Paint paint) {
canvas.drawRect(0,0,100,100,paint);
}
}
Draw a shape at runtime:
myShapeView.setPaintColor(Color.GREEN);
myShapeView.setPaintStroke(5f);
myShapeView.setRenderer(new RectRenderer());
myShapeView.invalidate();
I think you must redesign your component, so it can draw itself in onDraw method. If you need some class that must contain Canvas, you can try to do something like this.
class Drawer {
private Canvas canvas;
public Drawer(Canvas canvas) {
this.canvas = canvas;
}
public void DrawRectangle() {
Paint mypaint = new Paint();
mypaint.setColor(Color.BLUE);
canvas.drawRect(30, 30, 200, 200, mypaint);
}
}
On yours custom view you can do something like this.
public class ShapesView extends View{
public ShapesView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
Drawer drawer = new Drawer(canvas);
drawer.DrawRectangle();
}
public void DrawRectangle() {
Paint mypaint = new Paint();
mypaint.setColor(Color.BLUE);
canvas.drawRect(30, 30, 200, 200, mypaint);
}
}