I'm making an alphabet draw app where users (normally kids) will be able to fill color in the alphabet images in the app...
I've made the app which enabled me to draw anything on the canvas using the code given below...
Main Activity:
public class MainActivity extends Activity {
SimpleDrawingView view;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
view = new SimpleDrawingView (this);
setContentView(view);
view.setBackgroundResource(R.drawable.a1);
}
}
SimpleDrawingView class which actually draws on the view:
public class SimpleDrawingView extends View
{
private final int paintColor = Color.BLACK;
// defines paint and canvas
private Paint drawPaint;
// stores next circle
private Path path = new Path();
public SimpleDrawingView(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
setupPaint();
}
private void setupPaint() {
// Setup paint with color and stroke styles
drawPaint = new Paint();
drawPaint.setColor(paintColor);
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(15);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
}
#Override protected void onDraw(Canvas canvas) {
canvas.drawPath(path, drawPaint);
}
#Override public boolean onTouchEvent(MotionEvent event) {
float pointX = event.getX();
float pointY = event.getY();
// Checks for the event that occurs
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(pointX, pointY);
return true;
case MotionEvent.ACTION_MOVE:
path.lineTo(pointX, pointY);
break;
default: return false; }
// Force a view to draw again
postInvalidate();
return true;
}
}
It let me fill in the alphabet but it draws on the whole screen/view. I want to let the user fill/draw on the alphabet only and not on the entire screen.
Can someone tell me what to do or atleast some guidelines to start it ?
Related
I'm developing paint app the problem is when i choose color and paint and then pick different color the whole paint color changes to the new color can any one tell why this is happening and how to solve this ? and how to add eraser to this?
imageview DrawView here :
public class DrawView extends ImageView {
private ArrayList<Point> mTouches;
int paintColor;
public int setcolor(int a){
paintColor=a;
return paintColor;}
// Java constructor
public DrawView(Context context) {
super(context);
init();}
// XML constructor
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
init();}
private void init() {
mTouches = new ArrayList<Point>();}
#Override
public boolean onTouchEvent(MotionEvent event) {
// Capture a reference to each touch for drawing
float touchX = event.getX();
float touchY = event.getY();
mTouches.add(new Point(Math.round(touchX), Math.round(touchY)));
return super.onTouchEvent(event);}
#Override
protected void onDraw(Canvas c) {
// Let the image be drawn first
super.onDraw(c);
// Draw your custom points here
Paint paint = new Paint();
paint.setColor(paintColor);
paint.setAntiAlias(true);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
for (Point p : mTouches) {
c.drawCircle( p.x, p.y,15,paint);}} }
in my main:
im.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
DrawView mcustomImagview = (DrawView) v;
mcustomImagview.setcolor(color);
mcustomImagview.invalidate();
if (v.onTouchEvent(event)) {
// Do something with event.getX(), event.getY() }
return true;}})
Every time onDraw is called, the entire canvas is redraw. As you store only one paintColor in your custom class, only the last selected one is used.
You need to store the color together with the associated Point. Then when you loop throw your mTouches array, you change the color for the color of the current Point.
Btw the Paint object can be updated, so you don't need to create it every time as it's a bad practice to create new objects in onDraw.
EDIT
public class DrawView extends android.support.v7.widget.AppCompatImageView {
private ArrayList<ColouredPoint> mTouches;
// Current used colour
private int mCurrColour;
private Paint mPaint;
public void setColor(int colour) {
mCurrColour = colour;
}
public DrawView(Context context) {
super(context);
init();
}
// XML constructor
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mTouches = new ArrayList<>();
mPaint = new Paint();
mPaint.setColor(mCurrColour);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(5);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// Capture a reference to each touch for drawing
float touchX = event.getX();
float touchY = event.getY();
mTouches.add(new ColouredPoint(Math.round(touchX), Math.round(touchY), mCurrColour));
return super.onTouchEvent(event);
}
#Override
protected void onDraw(Canvas c) {
// Let the image be drawn first
super.onDraw(c);
// Draw your custom points here
for (ColouredPoint p : mTouches) {
mPaint.setColor(p.colour);
c.drawCircle(p.x, p.y, 15, mPaint);
}
}
/**
* Class to store the coordinate and the colour of the point.
*/
private class ColouredPoint {
int x;
int y;
int colour;
public ColouredPoint(int x, int y, int colour) {
this.x = x;
this.y = y;
this.colour = colour;
}
}
}
I have a simple app that draws a line as you move your finger around the screen.
I want to change it so instead of it drawing behind the finger, a circle follows directly below where you have touched. As you move your finger along the screen the circle should follow. It should not leave a path behind it.
The circle should appear where the finger is when the screen is touched and disappear when the finger is lifted.
MainActivity:
package com.example.dot2;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new SingleTouchEventListener(this, null));
}
}
SingleTouchEventListener:
package com.example.dot2;
import...
public class SingleTouchEventListener extends View {
private Paint paint = new Paint();
private Path path = new Path();
private Paint mPaint;
public SingleTouchEventListener(Context context, AttributeSet attrs) {
super(context, attrs);
paint.setAntiAlias(true);
paint.setStrokeWidth(6f);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
#Override
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_MOVE:
path.lineTo(eventX, eventY);
break;
case MotionEvent.ACTION_UP:
// nothing to do
break;
default:
return false;
}
// Schedules a repaint.
invalidate();
return true;
}
}
Thanks
I actually found code which did most of what I needed and added the rest...
Here's the code:
public class drawingView extends View implements OnTouchListener{
static int x,y,r=255,g=255,b=255;
final static int radius=30;
Paint paint;
public drawingView(Context context)
{
super(context);
paint=new Paint();
paint.setAntiAlias(true);
paint.setARGB(255, r, g, b);
setFocusable(true);
this.setOnTouchListener(this);
}
public void onDraw(Canvas canvas)
{
paint.setARGB(255, r, g, b);
//drawing the circle
canvas.drawCircle(x,y,radius,paint);
}
public boolean onTouch(View view,MotionEvent event)
{
x=(int)event.getX()-(radius/2); //logic to plot the circle in exact touch place
y=(int)event.getY()-(radius/2);
//System.out.println("X,Y:"+"x"+","+y);
randColor();
invalidate();
if (event.getAction() == MotionEvent.ACTION_UP) {
paint.setARGB(12, r, g, b);
}
return true;
}
public void randColor()
{
//r=(int)(Math.random()*255);
//g=(int)(Math.random()*255);
//b=(int)(Math.random()*255);
r=1;
g=2;
b=3;
//Toast.makeText(c, "r,g,b="+r+","+g+","+b,Toast.LENGTH_SHORT).show();
}
public void dissColor(){
r=255;
g=255;
b=255;
}
}
public class MyGameView extends View implements OnTouchListener{
static int x, y;
final static int radius = 25;
private int androidX;
private int androidY;
Paint paint;
Bitmap android;
boolean touch = false;
Handler handler = new Handler();
private Runnable r = new Runnable() {
#Override
public void run() {
invalidate();
}
};
private Runnable r2 = new Runnable() {
#Override
public void run() {
androidX = (int) Math.ceil(Math.random() * (MyGameView.this.getWidth()-android.getWidth()));
androidY = (int) Math.ceil(Math.random() * (MyGameView.this.getHeight()-android.getHeight()));
}
};
public MyGameView(Context context) {
super(context);
paint = new Paint();
paint.setAntiAlias(true); // smooth rendering
android = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
setFocusable(true);
setOnTouchListener(this);
}
#Override
protected void onDraw(Canvas canvas) {
if(touch == true){
paint.setARGB(255, 222, 78, 112);
canvas.drawBitmap(android, androidX, androidY, null);
canvas.drawCircle(x,y,radius,paint);
}
handler.postDelayed(r, 1000);
handler.postDelayed(r2, 3000);
super.onDraw(canvas);
}
#Override
public boolean onTouch(View view, MotionEvent event) {
x=(int)event.getX()-(radius/2);
y=(int)event.getY()-(radius/2);
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
touch = true;
break;
case MotionEvent.ACTION_UP:
touch = false;
break;
}
invalidate();
return true;
}
}
Here my code
What i want is when i hold my finger the bitmap start drawing every 3 second in random position x y.
Everything work but the problem image draw like crazy to every position and i can't figure how to do that i try calling invalidate() on ACTION_DOWN but now my circle draw every 3 second to the position i touch.
Any help is appreciate.
I'm not convinced that I totally understand what you want, but the problem with your code is the following: onDraw is called very often. In each of those calls, you run r2 with a delay of 3 seconds. So effectively, r2 is just run as often as onDraw, but the first time is delayed for 3 seconds.
What I think you want to do instead is run r2 in a separate thread and continuously update the position every 3 seconds, as in:
public class MyGameView extends View implements OnTouchListener{
static int x, y;
final static int radius = 25;
private int androidX;
private int androidY;
Paint paint;
Bitmap android;
boolean touch = false;
private Runnable r2 = new Runnable() {
#Override
public void run() {
androidX = (int) Math.ceil(Math.random() * (MyGameView.this.getWidth()-android.getWidth()));
androidY = (int) Math.ceil(Math.random() * (MyGameView.this.getHeight()-android.getHeight()));
}
};
public MyGameView(Context context) {
super(context);
paint = new Paint();
paint.setAntiAlias(true); // smooth rendering
android = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
setFocusable(true);
setOnTouchListener(this);
new Thread(r2).start(); // run r2 in a separate thread
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(touch == true){
paint.setARGB(255, 222, 78, 112);
canvas.drawBitmap(android, androidX, androidY, null);
canvas.drawCircle(x,y,radius,paint);
}
}
#Override
public boolean onTouch(View view, MotionEvent event) {
x=(int)event.getX()-(radius/2);
y=(int)event.getY()-(radius/2);
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
touch = true;
break;
case MotionEvent.ACTION_UP:
touch = false;
break;
}
return true;
}
}
I have a Rect object which I draw using drawRect(Rect, Paint). I have a OnTouchEvent that checks if the touch position is inside the Rect. If the touch is inside the Rect then something on my screen changes.
The issue I'm having is: Where I have to touch to get the on touch statements to execute is around 50 pixels higher than the Rect object.
Here is the relevant parts of my code:
public class MainActivity extends Activity {
DrawView drawView;
RelativeLayout fLY;
static Rect newGame = new Rect(LEFT, 165 + 35, 320, 200 + 35);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
fLY = new RelativeLayout(this);
drawView = new DrawView(this);
fLY.addView(drawView);
setContentView(fLY);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int touchX = (int)event.getX();
int touchY = (int)event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
if(newGame.contains(touchX, touchY))reset();
}
}
And my DrawView class:
public class DrawView extends View{
Paint paint = new Paint();
int COLORS[] = Constants.COLORS;
int left = 250;
int size = 35;
public DrawView(Context context) {
super(context);
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawRect(MainActivity.newGame, paint);
}
}
It's probably off by exactly the height of the Status Bar. You can get the height of the status bar through this answer: Height of statusbar?
Then add that to your getY and it should work.
I want to erase my bitmap but I don't want to erase background image. When I try to erase is white and it draw very hard in frames.
This is from CanvasView
erasePaint.setColor(Color.WHITE);
//erasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
erasePaint.setAntiAlias(true);
erasePaint.setStyle(Paint.Style.STROKE);
erasePaint.setStrokeJoin(Paint.Join.ROUND);
erasePaint.setStrokeWidth(12);
//....
protected void onDraw(Canvas canvas) {
paint.setPathEffect(null);
if(bitmap!=null){
for(MyEraser e:eraserList){
canvas.drawPath(e.p,erasePaint);
invalidate();
}
final OnTouchListener eraseListener = new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// erasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
//FirstActivity.ll.setVisibility(LinearLayout.GONE);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
myEraser = new MyEraser();
lastTouchX = event.getX();
lastTouchY = event.getY();
myEraser.mouseDown(event.getX(), event.getY());
return true;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
resetDirtyRect(event.getX(),event.getY());
int historySize = event.getHistorySize();
for(int i=0;i<historySize;i++){
float historicalX = event.getHistoricalX(i);
float historicalY = event.getHistoricalY(i);
expandDirtyRect(historicalX, historicalY);
myEraser.mouseUp(historicalX, historicalY);
}
myEraser.mouseUp(event.getX(), event.getY());
eraserList.add(myEraser);
break;
default:
Log.d("mock it up", "Unknown touch event " + event.toString());
return false;
}
invalidate(
(int) (dirtyRect.left - HALF_STROKE_WIDTH),
(int) (dirtyRect.top - HALF_STROKE_WIDTH),
(int) (dirtyRect.right + HALF_STROKE_WIDTH),
(int) (dirtyRect.bottom + HALF_STROKE_WIDTH));
lastTouchX = event.getX();
lastTouchY = event.getY();
return true;
}
};
}
}
This is MyEraser
public class MyEraser {
Paint paint = new Paint();
Path p = new Path();
public MyEraser(){
paint.setColor(Color.WHITE);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(5);
}
public void mouseDown(float x, float y) {
//path.addCircle(x,y,5,Path.Direction.CW);
p.moveTo( x, y );
// path.lineTo(x, y);
}
public void mouseMove(Path path, float x, float y) {
// path.addCircle(x,y,5,Path.Direction.CW);
}
public void mouseUp(float x, float y){
//path.addCircle(x,y,5,Path.Direction.CW);
p.lineTo(x, y);
}
public void draw(Canvas c,Path path){
//paint.setColor(Color.WHITE);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(7);
c.drawPath(p, paint);
}
}
EDIT: Started over for better explanation :)
I am not familiar with Android, in fact I have never used it. Use this code as an example of how to organize your code, rather than a functional code.
class MyView extends View {
private Eraser myEraser;
private Bitmap myBackgroundImage;
private Canvas myForegroundCanvas;
public MyView(Context context, Attributes, attrs) {
myEraser = new Eraser()
myBackgroundImage = BitmapFactory.decodeResource(getResources(), R.drawable.your_background_name);
Bitmap image = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); // the width and height of the view
myForegroundCanvas = new Canvas(image);
}
public boolean onTouchEvent(MotionEvent event) {
// update your eraser path
return true;
}
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(myBackgroundImage, 0, 0, null);
//for (Eraser item : eraserList) {
// if you have multiple eraser, add them to a list
myEraser.draw(myForegroundCanvas);
//}
canvas.drawCanvas(myForegroundCanvas, 0, 0, null);
}
}
The main idea is to keep the separation between your background and foreground images. That way, you could easily change the background and it would be updated. You could also have a reset on the foreground to erase everything, etc.
I hope this helps you.