Here is my code for the activity main
package com.example.dell_7560.experiment6;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new TouchScreen(this,null));
}
}
Here is the touch screen code
package com.example.dell_7560.experiment6;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class TouchScreen extends View {
private Paint paint=new Paint();
private Path path=new Path();
public TouchScreen(Context context, AttributeSet attributeSet) {
super(context,attributeSet);
paint.setAntiAlias(true);
paint.setStrokeWidth(6f);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
}
protected void onDraw(Canvas canvas)
{
canvas.drawPath(path,paint);
}
public boolean onTouchEvent(MotionEvent event)
{
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
path.moveTo(eventX,eventY);
return true;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_MOVE:
path.lineTo(eventX,eventY);
break;
default:
return false;
}
invalidate();
return true;
}
}
Now the app works as follows:
Touch the screen.
Drag the finger (it creates the red light)
Untouch the screen, the red line creation stops.
Till here it is cool.
My doubt is, how can I clear the content in the ACTION_UP event?
i.e. on untouching the screen, the screen should be as good as new.
What you need to do is tell the view that it's invalid on ActionUp (you do this). Then it will automatically call onDraw again. The trick then is to not draw anything. All you need to do is either clear the path, or set a variable that will cause the draw routine to not draw. The background always clears anyway, so all you need it to not draw the path after the invalidate.
As #Mike M mentioned,
path.reset() works in ACTION_UP case
It clears the screen i.e. canvas when I lift up my finger/touch.
Related
I'm creating a text editing app that allows the user to edit text using different fonts, colors, sizes, etc. Right now I'm working on a feature that is supposed to curve the text. I already tried asking once HERE but the answer that was posted wasn't really what I was looking for (or maybe it was and I just don't understand exactly what they meant) so I figured I'd ask again with an ACTUAL reference to what I'm trying to accomplish. So as you can see, I'm trying to have the text curve based on what the valued of the slider (seekbar in my case) is, but I just don't know how to do it. I was trying to mimic what is done HERE specifically the Along a path part, but again I'm not sure how to make it work with a seekbar as shown in the image.
Thank you
EDIT
Here's the code that I've been working with. Like I stated earlier, this my be exactly what I'm looking for and I'm just stupid, but here it is anyways.
import android.app.Activity;
import android.graphics.*;
import android.os.Bundle;
import android.view.*;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class Painting extends Activity
{
public static int y = 0;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(new SampleView(this));
}
private static class SampleView extends View
{
private Paint mPaint;
private float mX;
private float[] mPos;
private Path mPath;
private Paint mPathPaint;
private static final int DY = 30;
private static final String TEXTONPATH = "Along a path";
private static void makePath(Path p)
{
p.moveTo(10, 0);
p.cubicTo(00, 00, 00, 00, 900, 00);
}
public SampleView(Context context)
{
super(context);
setFocusable(true);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextSize(90);
mPaint.setTypeface(Typeface.SERIF);
mPath = new Path();
makePath(mPath);
mPathPaint = new Paint();
mPathPaint.setAntiAlias(true);
mPathPaint.setColor(0x800000FF);
mPathPaint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawColor(Color.WHITE);
Paint p = mPaint;
float x = mX;
float y = 0;
float[] pos = mPos;
p.setColor(0x80FF000);
canvas.drawLine(x, y, x, y+DY*3, p);
p.setColor(Color.BLACK);
canvas.translate(0, DY*10);
canvas.drawPath(mPath, mPathPaint);
p.setTextAlign(Paint.Align.LEFT);
canvas.drawTextOnPath(TEXTONPATH, mPath, 0, 0, p);
}
}
}
You are not going to be able to do this with a TextView. Instead you will have to write you own class extending View.
You will have to override onDraw and draw the text onto the Canvas there.
To make it work with a SeekBar you can use SeekBar#setOnSeekBarChangeListener. There in onProgressChanged you can tell your View to change its appearance.
I'm writing a very simple Android drawing program, for which I wrote a class DoodleCanvas.java, which includes (at the very bottom) a function to clear the canvas by filling it with white.
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class DoodleCanvas extends View {
private Paint mPaint; //Paint object for setting attr
private Path mPath; //Path object for tracing Paint over
public Canvas canvas; //Canvas object for calling Paint
public DoodleCanvas(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(10);
mPath = new Path();
}
//Draw mPaint over mPath
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(mPath, mPaint);
super.onDraw(canvas);
}
//Create mPath from touch events
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
mPath.moveTo(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
mPath.lineTo(event.getX(), event.getY());
invalidate();
break;
}
return true;
}
//Class function for filling canvas with white, effectively cleaning it
public void clearcanvas(Canvas canvas) {
canvas.drawColor(Color.WHITE);
}
}
In my MainActivity.java, I have set up a clear button to call clearcanvas.
package brianslho.basiccanvas;
import android.graphics.Canvas;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
DoodleCanvas dcanvas; //Custom class DoodleCanvas (from "DoodleCanvas.java")
Button btn; //Button for clearing screen
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dcanvas = findViewById(R.id.canvas);
btn = findViewById(R.id.btn);
btn.setOnClickListener(clearcanvas);
}
//Clear screen on button click
protected Button.OnClickListener clearcanvas = new View.OnClickListener() {
#Override
public void onClick(View v) {
//ERROR: canvas is null
Canvas canvas = dcanvas.canvas;
dcanvas.clearcanvas(canvas);
}
};
}
The app crashes whenever I press the clear button. The error message is, in essence, java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Canvas.drawColor(int)' on a null object reference. I've declared the canvas inside onClick, which I'm very sure is not "good coding", but just as an attempt to fix the null problem. I've searched StackOverflow for similar problems (and boy there are a LOT) but couldn't find anything that'd solve this.
Thank you in advance for your help!
Your problem is you have declare variable with the same name: public Canvas canvas; vs public void clearcanvas(Canvas canvas) => program will understand that canvas in your function is the global instead of the parameter of function. => solution: change variable name
you have to call the constructor and you have declared variable with the same name public Canvas canvas; and public void clearcanvas(Canvas canvas).So change the variable.
dcanvas = new DoodleCanvas(Context context, AttributeSet attrs);
I have a custom view and I overrided the ondraw. When I run my program the method invalidate updates the ondraw on my emulator but it doesn't work on real phone at all and the view is fixed on phone.
Anybody knows why?
here is my code:
package com.example.canvas;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.view.View;
public class Myview extends View{
String second="0";
int r=100;
public Myview(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
Paint paint=new Paint();
paint.setColor(Color.GREEN);
paint.setStrokeWidth(12);
canvas.drawLine(getWidth()/2f, getHeight()/2f,getWidth()/2f+ ((r-18.0f)*(float)Math.cos(Math.toRadians((Float.valueOf(second)/60f*360f) -90.0f))) ,getHeight()/2f+((r-18.0f)*(float)Math.sin(Math.toRadians ((Float.valueOf(second)/60f*360f)-90.0f))), paint);
update();
super.onDraw(canvas);
}
private void update() {
Handler h=new Handler();
h.postDelayed(new Runnable() {
#Override
public void run() {
Calendar c=Calendar.getInstance();
SimpleDateFormat ss=new SimpleDateFormat("ss");
second=ss.format(c.getTime());
invalidate();
}
}, 1000);
}
}
Bingo!when I set timezone for calendar object to my country,invalidate worked and ondraw was updated on emulator and real phone.
Ok, I need to crop an ImageView in a particular shape, and I can't do this by adding over a png, because the background can be variable (ex. a pattern). So, I need that the area outside the shape is transparent.
The shape must be this:
I thought to use Path() to draw this shape and use it to mask the ImageView, but I have absolutely no idea how to draw a complex shape like this with Path().
Many thanks.
So I was bored and this looked like fun, so I've thrown together a simple Drawable you can use to do this. You could get fancier and add strokes and whatnot to it, but this works for the basic case you've suggested, and allows you to set the arrow to be pointing to any of the corners, and will also scale your image to fit the bounds of the Drawable. Here's the result:
You can use it by:
BubbleDrawable bubbleDrawable = new BubbleDrawable(
this, R.drawable.your_image, BubbleDrawable.Corner.TOP_RIGHT);
myImageView.setImageDrawable(bubbleDrawable);
And here's the code for BubbleDrawable:
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import static android.graphics.Matrix.ScaleToFit.FILL;
import static android.graphics.Paint.ANTI_ALIAS_FLAG;
import static android.graphics.Path.Direction.CW;
import static android.graphics.PixelFormat.TRANSPARENT;
import static android.graphics.Shader.TileMode.CLAMP;
import static test.com.testrotationanimation.BubbleDrawable.Corner.TOP_LEFT;
public final class BubbleDrawable extends Drawable {
private final Matrix mMatrix = new Matrix();
private final Paint mPaint = new Paint(ANTI_ALIAS_FLAG);
private final Path mPath = new Path();
private final RectF mSrcRect = new RectF();
private final RectF mDstRect = new RectF();
private final Shader mShader;
private Corner mArrowCorner = TOP_LEFT;
public BubbleDrawable(Bitmap bitmap, Corner arrowCorner) {
// Initialize a BitmapShader with the image you wish to draw
// (you can use other TileModes like REPEAT or MIRROR if you prefer)
mShader = new BitmapShader(bitmap, CLAMP, CLAMP);
mPaint.setShader(mShader);
// Save the bounds of the bitmap as the src rectangle -- will
// be used later to update the matrix when the bounds change
// so that the image fits within the bounds of this drawable
mSrcRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
// Set the corner in which the arrow will be drawn
mArrowCorner = arrowCorner;
}
public BubbleDrawable(Context ctx, int drawableResource, Corner arrowCorner) {
this(BitmapFactory.decodeResource(ctx.getResources(), drawableResource), arrowCorner);
}
public Corner getArrowCorner() {
return mArrowCorner;
}
public void setArrowCorner(Corner corner) {
mArrowCorner = corner;
updatePath();
invalidateSelf();
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
updateMatrix(bounds);
updatePath();
}
private void updateMatrix(Rect bounds) {
// Set the destination rectangle for the bitmap to be the
// new drawable bounds
mDstRect.set(bounds);
// Scale the bitmap's rectangle to the bounds of this drawable
mMatrix.setRectToRect(mSrcRect, mDstRect, FILL);
// Update the shader's matrix (to draw the bitmap at the right size)
mShader.setLocalMatrix(mMatrix);
}
private void updatePath() {
final Rect bounds = getBounds();
final float x = bounds.exactCenterX();
final float y = bounds.exactCenterY();
// Draw the initial circle (same for all corners)
mPath.reset();
mPath.addCircle(x, y, Math.min(x, y), CW);
// Add the rectangle which intersects with the center,
// based on the corner in which the arrow should draw
switch (mArrowCorner) {
case TOP_LEFT:
mPath.addRect(bounds.left, bounds.top, x, y, CW);
break;
case TOP_RIGHT:
mPath.addRect(x, bounds.top, bounds.right, y, CW);
break;
case BOTTOM_LEFT:
mPath.addRect(bounds.left, y, x, bounds.bottom, CW);
break;
case BOTTOM_RIGHT:
mPath.addRect(x, y, bounds.right, bounds.bottom, CW);
break;
}
}
#Override
public void draw(Canvas canvas) {
// Easy enough, just draw the path using the paint.
// It already has the BitmapShader applied which
// will do the work for you.
canvas.drawPath(mPath, mPaint);
}
#Override
public int getOpacity() {
// Indicate that this Drawable has fully-transparent pixel values
return TRANSPARENT;
}
#Override
public void setColorFilter(ColorFilter colorFilter) {
// Yay, you can even support color filters for your drawable
mPaint.setColorFilter(colorFilter);
}
#Override
public void setAlpha(int i) {
// You could do this by doing some canvas magic but I'm
// lazy and don't feel like it. Exercise for the reader. :)
throw new UnsupportedOperationException("Not implemented.");
}
public enum Corner {
TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT
}
}
i am making an app to make the hands of a clock.That is, i draw a simple line on the screen and want it rotate like the hands of a clock.how do i do this using a timer?(so that the position of the line refreshes).Only one end point of the line should rotate,while the other remains staionary.In the folllowing code,i only draw a line on the screen:
DrawView.java:
package LineRefresh.xyz.com;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;
public class DrawView extends View {
Paint paint = new Paint();
public DrawView(Context context) {
super(context);
}
#Override
public void onDraw(final Canvas canvas) {
paint.setColor(Color.BLACK);
canvas.drawLine(50, 200, 270, 200, paint);
}
}
LineRefresh.java:
package LineRefresh.xyz.com;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
public class LineRefresh extends Activity {
DrawView drawView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
drawView = new DrawView(this);
drawView.setBackgroundColor(Color.WHITE);
setContentView(drawView);
}
}
Try something like this:
private Handler mHandler = new Handler();
private Runnable MethodToDrawClockLine = new Runnable()
{
public void run()
{
//execute here your drawing routine.
// after drawing set the timer for another x period of time
mHandler.postDelayed(MethodToDrawClockLine, 1000); //it'll re-execute it after 1sec.
}
}
in you OnCreate or any method where you want to Start the Timer you can use:
mHandler.removeCallbacks(mUpdateTimeTask); //be sure to remove any handler before add a new one
mHandler.postDelayed(MethodToDrawClockLine, 1000); //it'll execute it after 1sec.
when you're done, remember to remove the handler.
mHandler.removeCallbacks(mUpdateTimeTask);
That shold be it.